diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 601479b..983094e 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -86,6 +86,8 @@ set(SLIC3R_GUI_SOURCES GUI/Widgets/WebView.hpp GUI/Widgets/ErrorMsgStaticText.cpp GUI/Widgets/ErrorMsgStaticText.hpp + GUI/Widgets/MultiNozzleSync.hpp + GUI/Widgets/MultiNozzleSync.cpp GUI/AboutDialog.cpp GUI/AboutDialog.hpp GUI/NetworkTestDialog.cpp @@ -176,6 +178,8 @@ set(SLIC3R_GUI_SOURCES GUI/GLSelectionRectangle.hpp GUI/Gizmos/GizmoObjectManipulation.cpp GUI/Gizmos/GizmoObjectManipulation.hpp + GUI/Gizmos/GLGizmoAlignment.cpp + GUI/Gizmos/GLGizmoAlignment.hpp GUI/GLModel.hpp GUI/GLModel.cpp GUI/GLTexture.hpp @@ -352,6 +356,8 @@ set(SLIC3R_GUI_SOURCES GUI/TextLines.hpp GUI/PlateSettingsDialog.cpp GUI/PlateSettingsDialog.hpp + GUI/PlateMoveDialog.cpp + GUI/PlateMoveDialog.hpp GUI/ImGuiWrapper.hpp GUI/ImGuiWrapper.cpp GUI/ImageMessageDialog.hpp @@ -419,6 +425,8 @@ set(SLIC3R_GUI_SOURCES GUI/Jobs/BindJob.cpp GUI/Jobs/NotificationProgressIndicator.hpp GUI/Jobs/NotificationProgressIndicator.cpp + GUI/Jobs/BooleanOperationJob.hpp + GUI/Jobs/BooleanOperationJob.cpp GUI/PhysicalPrinterDialog.hpp GUI/PhysicalPrinterDialog.cpp GUI/ProgressStatusBar.hpp @@ -517,6 +525,8 @@ set(SLIC3R_GUI_SOURCES GUI/Calibration.cpp GUI/PrintOptionsDialog.hpp GUI/PrintOptionsDialog.cpp + GUI/PurgeModeDialog.hpp + GUI/PurgeModeDialog.cpp GUI/SafetyOptionsDialog.hpp GUI/SafetyOptionsDialog.cpp GUI/CreatePresetsDialog.hpp diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index cca8956..a6f3ab7 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -850,7 +850,7 @@ void GLVolume::set_range(double min_z, double max_z) //QDS: add outline related logic //static unsigned char stencil_data[1284][2944]; -void GLVolume::render(const GUI::Camera &camera, const std::vector> &colors, Model &model, bool with_outline, const std::array &body_color) const +void GLVolume::render(const GUI::Camera &camera, const std::vector> &colors, Model &model, bool with_outline, const std::array &body_color, bool disable_mmu_colors) const { if (!is_active) return; @@ -867,6 +867,10 @@ void GLVolume::render(const GUI::Camera &camera, const std::vector= model_objects.size()) break; @@ -1246,7 +1250,8 @@ void GLWipeTowerVolume::render(const GUI::Camera &camera, const std::vector> &extruder_colors, Model &model, bool with_outline, - const std::array &body_color) const + const std::array &body_color, + bool disable_mmu_colors) const { if (!is_active) return; @@ -1730,7 +1735,9 @@ void GLVolumeCollection::render(GUI::ERenderPipelineStage render_pip if (GUI::ERenderPipelineStage::Silhouette != render_pipeline_stage) { shader->set_uniform("is_text_shape", volume.first->is_text_shape); - shader->set_uniform("uniform_color", volume.first->render_color); + // Use volume color override if enabled + const std::array& effective_color = get_volume_render_color(volume.second.first, volume.first->render_color); + shader->set_uniform("uniform_color", effective_color); shader->set_uniform("z_range", m_z_range); shader->set_uniform("clipping_plane", m_clipping_plane); shader->set_uniform("use_color_clip_plane", m_use_color_clip_plane); @@ -1798,12 +1805,18 @@ void GLVolumeCollection::render(GUI::ERenderPipelineStage render_pip //QDS: add outline related logic auto red_color = std::array({ 1.0f, 0.0f, 0.0f, 1.0f });//slice_error - volume.first->render(camera, colors,model,with_outline && volume.first->selected, volume.first->slice_error ? red_color : body_color); + // Only disable MMU colors if this specific volume has a color override + bool this_volume_has_override = m_use_volume_color_override && + m_volume_color_overrides.find(volume.second.first) != m_volume_color_overrides.end(); + volume.first->render(camera, colors,model,with_outline && volume.first->selected, volume.first->slice_error ? red_color : body_color, this_volume_has_override); } else { if (volume.first->selected) { shader->set_uniform("u_model_matrix", volume.first->world_matrix()); - volume.first->render(camera, colors, model, false, body_color); + // Only disable MMU colors if this specific volume has a color override + bool this_volume_has_override = m_use_volume_color_override && + m_volume_color_overrides.find(volume.second.first) != m_volume_color_overrides.end(); + volume.first->render(camera, colors, model, false, body_color, this_volume_has_override); } } @@ -1973,7 +1986,7 @@ bool GLVolumeCollection::check_outside_state(const BuildVolume &build_volume, Mo case BuildVolume::Type::Rectangle: //FIXME this test does not evaluate collision of a build volume bounding box with non-convex objects. state = plate_build_volume.volume_state_bbox(bb); - if ((state == BuildVolume::ObjectState::Inside) && (extruder_count > 1)) + if ((state == BuildVolume::ObjectState::Inside) && (extruder_count > 1) && !GUI::wxGetApp().plater()->force_ban_check_volume_bbox_state_with_extruder_area()) { state = plate_build_volume.check_volume_bbox_state_with_extruder_areas(bb, inside_extruders); } diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 86adbf2..bb1fef1 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -591,7 +591,8 @@ public: const std::vector>& colors, Model & model, bool with_outline = false, - const std::array &body_color = {1.0f, 1.0f, 1.0f, 1.0f} ) const; + const std::array &body_color = {1.0f, 1.0f, 1.0f, 1.0f}, + bool disable_mmu_colors = false ) const; //QDS: add simple render function for thumbnail void simple_render(const std::shared_ptr& shader, ModelObjectPtrs& model_objects, std::vector>& extruder_colors,bool ban_light =false) const; @@ -626,7 +627,8 @@ public: const std::vector> & colors, Model & model, bool with_outline = false, - const std::array & body_color = {1.0f, 1.0f, 1.0f, 1.0f}) const override; + const std::array & body_color = {1.0f, 1.0f, 1.0f, 1.0f}, + bool disable_mmu_colors = false) const override; std::vector iva_per_colors; bool IsTransparent(); @@ -679,6 +681,10 @@ private: bool m_use_color_clip_plane{false}; std::array m_color_clip_plane_colors{ColorRGBA::RED(), ColorRGBA::BLUE()}; + // Volume-based color override for gizmos (e.g., mesh boolean) + bool m_use_volume_color_override{false}; + std::unordered_map> m_volume_color_overrides; + struct Slope { // toggle for slope rendering @@ -800,6 +806,29 @@ public: } void set_color_clip_plane_colors(const std::array &colors) { m_color_clip_plane_colors = colors; } + // Volume color override methods (similar to color_clip_plane methods) + void set_use_volume_color_override(bool use) { m_use_volume_color_override = use; } + void set_volume_color_override(unsigned int volume_idx, const std::array& color) { + m_volume_color_overrides[volume_idx] = color; + } + void set_volumes_color_override(const std::vector& volume_indices, const std::array& color) { + for (unsigned int idx : volume_indices) { + m_volume_color_overrides[idx] = color; + } + } + void clear_volume_color_override(unsigned int volume_idx) { + m_volume_color_overrides.erase(volume_idx); + } + void clear_all_volume_color_overrides() { + m_volume_color_overrides.clear(); + m_use_volume_color_override = false; + } + const std::array& get_volume_render_color(unsigned int volume_idx, const std::array& default_color) const { + if (!m_use_volume_color_override) return default_color; + auto it = m_volume_color_overrides.find(volume_idx); + return (it != m_volume_color_overrides.end()) ? it->second : default_color; + } + bool is_slope_GlobalActive() const { return m_slope.isGlobalActive; } bool is_slope_active() const { return m_slope.active; } void set_slope_active(bool active) { m_slope.active = active; } diff --git a/src/slic3r/GUI/AMSMaterialsSetting.cpp b/src/slic3r/GUI/AMSMaterialsSetting.cpp index 4be60af..ae4f09e 100644 --- a/src/slic3r/GUI/AMSMaterialsSetting.cpp +++ b/src/slic3r/GUI/AMSMaterialsSetting.cpp @@ -278,7 +278,7 @@ void AMSMaterialsSetting::create_panel_normal(wxWindow* parent) m_panel_SN->Fit(); wxBoxSizer* m_tip_sizer = new wxBoxSizer(wxHORIZONTAL); - m_tip_readonly = new Label(parent, ""); + m_tip_readonly = new Label(parent, _L("")); m_tip_readonly->SetForegroundColour(*wxBLACK); m_tip_readonly->SetBackgroundColour(*wxWHITE); m_tip_readonly->SetMinSize(wxSize(FromDIP(380), -1)); @@ -322,6 +322,22 @@ void AMSMaterialsSetting::create_panel_kn(wxWindow* parent) cali_title_sizer->Add(m_ratio_text, 0, wxALIGN_CENTER_VERTICAL); //cali_title_sizer->Add(m_wiki_ctrl, 0, wxALIGN_CENTER_VERTICAL); + wxBoxSizer *m_sizer_nozzle_type = new wxBoxSizer(wxHORIZONTAL); + // Nozzle Type + m_title_nozzle_type = new wxStaticText(parent, wxID_ANY, _L("Nozzle Type"), wxDefaultPosition, wxSize(AMS_MATERIALS_SETTING_LABEL_WIDTH, -1), 0); + m_title_nozzle_type->SetMinSize(wxSize(FromDIP(80), -1)); + m_title_nozzle_type->SetMaxSize(wxSize(FromDIP(80), -1)); + m_title_nozzle_type->SetFont(::Label::Body_13); + m_title_nozzle_type->SetForegroundColour(AMS_MATERIALS_SETTING_GREY800); + m_title_nozzle_type->Wrap(-1); + m_sizer_nozzle_type->Add(m_title_nozzle_type, 0, wxALIGN_CENTER, 0); + m_sizer_nozzle_type->Add(0, 0, 0, wxEXPAND, 0); + + m_comboBox_nozzle_type = new ::ComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, AMS_MATERIALS_SETTING_COMBOX_WIDTH, 0, nullptr, wxCB_READONLY); + m_comboBox_nozzle_type->Bind(wxEVT_COMMAND_COMBOBOX_SELECTED, &AMSMaterialsSetting::on_select_nozzle_pos_id, this); + m_sizer_nozzle_type->Add(m_comboBox_nozzle_type, 1, wxALIGN_CENTER, 0); + + wxBoxSizer *m_sizer_cali_resutl = new wxBoxSizer(wxHORIZONTAL); // pa profile m_title_pa_profile = new wxStaticText(parent, wxID_ANY, _L("PA Profile"), wxDefaultPosition, wxSize(AMS_MATERIALS_SETTING_LABEL_WIDTH, -1), 0); @@ -372,6 +388,8 @@ void AMSMaterialsSetting::create_panel_kn(wxWindow* parent) sizer->Add(0, 0, 0, wxTOP, FromDIP(10)); sizer->Add(cali_title_sizer, 0, wxLEFT | wxRIGHT | wxEXPAND, FromDIP(20)); sizer->Add(0, 0, 0, wxTOP, FromDIP(12)); + sizer->Add(m_sizer_nozzle_type, 0, wxLEFT | wxRIGHT, FromDIP(20)); + sizer->Add(0, 0, 0, wxTOP, FromDIP(10)); sizer->Add(m_sizer_cali_resutl, 0, wxLEFT | wxRIGHT, FromDIP(20)); sizer->Add(0, 0, 0, wxTOP, FromDIP(10)); sizer->Add(kn_val_sizer, 0, wxLEFT | wxRIGHT | wxEXPAND, FromDIP(20)); @@ -437,12 +455,14 @@ void AMSMaterialsSetting::update_filament_editing(bool is_printing) { if (is_printing) { m_comboBox_filament->Enable(obj->is_support_filament_setting_inprinting); + m_comboBox_nozzle_type->Enable(obj->is_support_filament_setting_inprinting); m_comboBox_cali_result->Enable(obj->is_support_filament_setting_inprinting); m_button_confirm->Show(obj->is_support_filament_setting_inprinting); m_button_reset->Show(obj->is_support_filament_setting_inprinting); } else { m_comboBox_filament->Enable(true); + m_comboBox_nozzle_type->Enable(true); m_comboBox_cali_result->Enable(true); m_button_reset->Show(true); m_button_confirm->Show(true); @@ -560,8 +580,7 @@ void AMSMaterialsSetting::on_select_ok(wxCommandEvent &event) PresetBundle* preset_bundle = wxGetApp().preset_bundle; if (preset_bundle) { for (auto it = preset_bundle->filaments.begin(); it != preset_bundle->filaments.end(); it++) { - - auto filament_item = map_filament_items[m_comboBox_filament->GetValue().ToStdString()]; + auto filament_item = map_filament_items[into_u8(m_comboBox_filament->GetValue())]; std::string filament_id = filament_item.filament_id; if (it->filament_id.compare(filament_id) == 0) { @@ -579,7 +598,9 @@ void AMSMaterialsSetting::on_select_ok(wxCommandEvent &event) if (vendor && (vendor->values.size() > 0)) { std::string vendor_name = vendor->values[0]; - DevFilaBlacklist::check_filaments_in_blacklist(obj->printer_type, vendor_name, filamnt_type, it->filament_id, ams_id, slot_id, it->name, in_blacklist, action, info); + std::string nozzle_flow = obj->GetFilaSystem() ? obj->GetFilaSystem()->GetNozzleFlowStringByAmsId(std::to_string(ams_id)) : std::string(); + + DevFilaBlacklist::check_filaments_in_blacklist(obj->printer_type, vendor_name, filamnt_type, it->filament_id, ams_id, slot_id, it->alias, nozzle_flow, in_blacklist, action, info); } if (in_blacklist) { @@ -751,6 +772,7 @@ void AMSMaterialsSetting::set_empty_color(wxColour color) { m_clr_picker->is_empty(true); m_clr_picker->set_color(color); + m_clr_picker->set_colors({ color }); m_clr_name->SetLabelText(wxEmptyString); } @@ -776,8 +798,10 @@ void AMSMaterialsSetting::set_ctype(int ctype) void AMSMaterialsSetting::on_picker_color(wxCommandEvent& event) { - unsigned int color_num = event.GetInt(); - set_color(wxColour(color_num>>24&0xFF, color_num>>16&0xFF, color_num>>8&0xFF, color_num&0xFF)); + unsigned int color_num = event.GetInt(); + const wxColour& color = wxColour(color_num >> 24 & 0xFF, color_num >> 16 & 0xFF, color_num >> 8 & 0xFF, color_num & 0xFF); + set_color(color); + set_colors({ color }); } void AMSMaterialsSetting::on_clr_picker(wxMouseEvent &event) @@ -813,6 +837,7 @@ void AMSMaterialsSetting::update_widgets() { if (obj && obj->get_printer_series() == PrinterSeries::SERIES_X1 && obj->cali_version <= -1) { // Low version firmware does not display k value + m_panel_normal->Show(); m_panel_kn->Hide(); } else if(is_virtual_tray()) // virtual tray @@ -857,7 +882,7 @@ static void _collect_filament_info(const wxString& shown_name, unordered_map& query_filament_types) { query_filament_vendors[shown_name] = filament.config.get_filament_vendor(); - query_filament_vendors[shown_name] = filament.config.get_filament_type(); + query_filament_types[shown_name] = filament.config.get_filament_type(); } void AMSMaterialsSetting::Popup(wxString filament, wxString sn, wxString temp_min, wxString temp_max, wxString k, wxString n) @@ -883,19 +908,26 @@ void AMSMaterialsSetting::Popup(wxString filament, wxString sn, wxString temp_mi std::set filament_id_set; PresetBundle * preset_bundle = wxGetApp().preset_bundle; std::ostringstream stream; - stream << std::fixed << std::setprecision(1) << obj->GetExtderSystem()->GetNozzleDiameter(0); + int extruder_id = obj->get_extruder_id_by_ams_id(std::to_string(ams_id)); + if (!obj->GetExtderSystem()->GetExtderById(extruder_id)) + { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " get extruder id failed"; + extruder_id = 0; + } + stream << std::fixed << std::setprecision(1) << obj->GetExtderSystem()->GetNozzleDiameter(extruder_id); std::string nozzle_diameter_str = stream.str(); std::set printer_names = preset_bundle->get_printer_names_by_printer_type_and_nozzle(DevPrinterConfigUtil::get_printer_display_name(obj->printer_type), nozzle_diameter_str); + auto & filaments = preset_bundle->filaments; if (preset_bundle) { - BOOST_LOG_TRIVIAL(trace) << "system_preset_bundle filament number=" << preset_bundle->filaments.size(); - for (auto filament_it = preset_bundle->filaments.begin(); filament_it != preset_bundle->filaments.end(); filament_it++) { + BOOST_LOG_TRIVIAL(trace) << "system_preset_bundle filament number=" << filaments.size(); + for (auto filament_it = filaments.begin(); filament_it != filaments.end(); filament_it++) { //filter by system preset Preset& preset = *filament_it; /*The situation where the user preset is not displayed is as follows: 1. Not a root preset 2. Not system preset and the printer firmware does not support user preset */ - if (preset_bundle->filaments.get_preset_base(*filament_it) != &preset || (!filament_it->is_system && !obj->is_support_user_preset)) { + if (filaments.get_preset_base(*filament_it) != &preset || (!filament_it->is_system && !obj->is_support_user_preset)) { continue; } @@ -908,35 +940,20 @@ void AMSMaterialsSetting::Popup(wxString filament, wxString sn, wxString temp_mi } else { filament_id_set.insert(filament_it->filament_id); // name matched - if (filament_it->is_system) { - filament_items.push_back(filament_it->alias); - _collect_filament_info(filament_it->alias, preset, query_filament_vendors, query_filament_types); + auto fialment_alias = filaments.get_preset_alias(*filament_it, true); + if (!fialment_alias.empty()) { + filament_items.push_back(from_u8(fialment_alias)); + _collect_filament_info(fialment_alias, preset, query_filament_vendors, query_filament_types); FilamentInfos filament_infos; - filament_infos.filament_id = filament_it->filament_id; - filament_infos.setting_id = filament_it->setting_id; - map_filament_items[filament_it->alias] = filament_infos; - } else { - char target = '@'; - size_t pos = filament_it->name.find(target); - if (pos != std::string::npos) { - std::string user_preset_alias = filament_it->name.substr(0, pos - 1); - wxString wx_user_preset_alias = wxString(user_preset_alias.c_str(), wxConvUTF8); - user_preset_alias = wx_user_preset_alias.ToStdString(); - - filament_items.push_back(user_preset_alias); - _collect_filament_info(user_preset_alias, preset, query_filament_vendors, query_filament_types); - - FilamentInfos filament_infos; - filament_infos.filament_id = filament_it->filament_id; - filament_infos.setting_id = filament_it->setting_id; - map_filament_items[user_preset_alias] = filament_infos; - } + filament_infos.filament_id = filament_it->filament_id; + filament_infos.setting_id = filament_it->setting_id; + map_filament_items[fialment_alias] = filament_infos; } if (filament_it->filament_id == ams_filament_id) { - hint_filament_name = from_u8(filament_it->alias); - qidi_filament_name = from_u8(filament_it->alias); + hint_filament_name = from_u8(fialment_alias); + qidi_filament_name = from_u8(fialment_alias); // update if nozzle_temperature_range is found @@ -1102,7 +1119,7 @@ void AMSMaterialsSetting::post_select_event(int index) { void AMSMaterialsSetting::on_select_cali_result(wxCommandEvent &evt) { m_pa_cali_select_id = evt.GetSelection(); - if (m_pa_cali_select_id >= 0) { + if (m_pa_cali_select_id >= 0 && m_pa_profile_items.size() > m_pa_cali_select_id) { m_input_k_val->GetTextCtrl()->SetValue(float_to_string_with_precision(m_pa_profile_items[m_pa_cali_select_id].k_value)); m_input_n_val->GetTextCtrl()->SetValue(float_to_string_with_precision(m_pa_profile_items[m_pa_cali_select_id].n_coef)); } @@ -1112,6 +1129,216 @@ void AMSMaterialsSetting::on_select_cali_result(wxCommandEvent &evt) } } +void AMSMaterialsSetting::on_select_nozzle_pos_id(wxCommandEvent &evt) +{ + int selected_id = evt.GetSelection(); + + if(selected_id == -1){ + m_comboBox_cali_result->Disable(); + } else{ + m_comboBox_cali_result->Enable(); + update_pa_profile_items(); + } +} + +void AMSMaterialsSetting::update_pa_profile_items() +{ + if (!obj || !obj->GetNozzleSystem()) return; + + int extruder_id = obj->get_extruder_id_by_ams_id(std::to_string(ams_id)); + NozzleFlowType nozzle_flow_type = obj->GetExtderSystem()->GetNozzleFlowType(extruder_id); + float nozzle_diameter = obj->GetExtderSystem()->GetNozzleDiameter(extruder_id); + + auto rack = obj->GetNozzleSystem()->GetNozzleRack(); + int sel = m_comboBox_nozzle_type->GetSelection(); + if(rack->IsSupported() && extruder_id == MAIN_EXTRUDER_ID && sel != wxNOT_FOUND) { + auto sel_pair = (std::pair*)m_comboBox_nozzle_type->GetClientData(sel); + + switch(sel_pair->first) { + case NozzleDiameterType::NOZZLE_DIAMETER_0_2: nozzle_diameter = 0.2f; break; + case NozzleDiameterType::NOZZLE_DIAMETER_0_4: nozzle_diameter = 0.4f; break; + case NozzleDiameterType::NOZZLE_DIAMETER_0_6: nozzle_diameter = 0.6f; break; + case NozzleDiameterType::NOZZLE_DIAMETER_0_8: nozzle_diameter = 0.8f; break; + default: nozzle_diameter = nozzle_diameter; break; + } + nozzle_flow_type = sel_pair->second; + } + + NozzleVolumeType nozzle_volume_type = NozzleVolumeType::nvtStandard; + if (nozzle_flow_type != NozzleFlowType::NONE_FLOWTYPE) { + nozzle_volume_type = NozzleVolumeType(nozzle_flow_type - 1); + } + + wxArrayString items; + m_pa_profile_items.clear(); + m_comboBox_cali_result->SetValue(wxEmptyString); + // add default item + PACalibResult default_item; + default_item.cali_idx = -1; + default_item.filament_id = ams_filament_id; + if (obj->GetConfig()->SupportCalibrationPA_FlowAuto()) { + default_item.k_value = -1; + default_item.n_coef = -1; + } else { + get_default_k_n_value(ams_filament_id, default_item.k_value, default_item.n_coef); + } + m_pa_profile_items.emplace_back(default_item); + items.push_back(_L("Default")); + + m_input_k_val->GetTextCtrl()->SetValue(wxEmptyString); + std::vector cali_history = obj->pa_calib_tab; + std::sort(cali_history.begin(), cali_history.end(), [](const PACalibResult &left, const PACalibResult &right) { return left.nozzle_pos_id < right.nozzle_pos_id; }); + for (auto cali_item : cali_history) { + if (cali_item.filament_id == ams_filament_id) { + if (obj->is_multi_extruders()) { + if (cali_item.extruder_id != extruder_id) + continue; + + if (cali_item.nozzle_volume_type != nozzle_volume_type || !is_approx(cali_item.nozzle_diameter, nozzle_diameter)) + continue; + } + + if(rack->IsSupported() && extruder_id == MAIN_EXTRUDER_ID) + { + if(cali_item.nozzle_pos_id == 0) { + items.push_back(wxString::Format("R | %s", from_u8(cali_item.name))); + } else if(cali_item.nozzle_pos_id >= 0x10){ + items.push_back(wxString::Format("%d | %s", (cali_item.nozzle_pos_id & 0x0f) + 1, from_u8(cali_item.name))); + } else { + items.push_back(wxString::Format("N/A | %s", from_u8(cali_item.name))); + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << "Nozzle position id is -1 or invalid."; + } + } else { + items.push_back(wxString::Format("%s", from_u8(cali_item.name))); + } + + m_pa_profile_items.push_back(cali_item); + } + } + m_comboBox_cali_result->Set(items); + m_comboBox_cali_result->SetSelection(0); +} + +void AMSMaterialsSetting::update_nozzle_combo(MachineObject* obj){ + if(!obj || !obj->GetNozzleSystem()) return; + + auto rack = obj->GetNozzleSystem()->GetNozzleRack(); + int extruder_id = obj->GetFilaSystem()->GetExtruderIdByAmsId(std::to_string(ams_id)); + + if(rack->IsSupported() && extruder_id == MAIN_EXTRUDER_ID){ + int r_nozzle_id = obj->GetExtderSystem()->GetExtderById(MAIN_EXTRUDER_ID)->GetNozzleId(); + auto r_nozzle = obj->GetNozzleSystem()->GetExtNozzle(r_nozzle_id); + auto nozzle_map = rack->GetRackNozzles(); + + std::set> nozzle_type_set; + if(r_nozzle.IsNormal()){ // Add the nozzle of the toolhead + nozzle_type_set.insert(std::make_pair(r_nozzle.GetNozzleDiameterType(), r_nozzle.GetNozzleFlowType())); + } + for (auto &nozzle : nozzle_map) { // Add the nozzle of the rack + if (nozzle.second.IsNormal()) { + nozzle_type_set.insert(std::make_pair(nozzle.second.GetNozzleDiameterType(), nozzle.second.GetNozzleFlowType())); + } + } + + std::vector> nozzle_type_vec(nozzle_type_set.begin(), nozzle_type_set.end()); + std::sort(nozzle_type_vec.begin(), nozzle_type_vec.end(), [](const std::pair & left, const std::pair& right) -> bool { + if(left.first == right.first) { + return left.second < right.second; + } else { + return left.first < right.first; + } + }); + + /* make nozzle type combobox item */ + m_comboBox_nozzle_type->Clear(); + for(auto pair : nozzle_type_vec){ + wxString item; + switch(pair.first) { + case NozzleDiameterType::NOZZLE_DIAMETER_0_2: item = "0.2 mm"; break; + case NozzleDiameterType::NOZZLE_DIAMETER_0_4: item = "0.4 mm"; break; + case NozzleDiameterType::NOZZLE_DIAMETER_0_6: item = "0.6 mm"; break; + case NozzleDiameterType::NOZZLE_DIAMETER_0_8: item = "0.8 mm"; break; + default: item = _L("Unknown"); break; + } + item += " "; + switch(pair.second) { + case NozzleFlowType::S_FLOW: item += _L("Standard Flow"); break; + case NozzleFlowType::H_FLOW: item += _L("High Flow"); break; + default: item += _L("Unknown"); break; + } + m_comboBox_nozzle_type->Append(item, wxNullBitmap, new std::pair(pair)); + } + m_title_nozzle_type->Show(); + m_comboBox_nozzle_type->Show(); + + /* set nozzle pos tooltip */ + auto font = m_title_pa_profile->GetFont(); + font.SetUnderlined(true); + m_title_pa_profile->SetFont(font); + m_title_pa_profile->SetToolTip(_L("Note: The hotend number on the right extruder is tied to the holder. When the hotend is moved to a new holder, its number will update automatically.")); + + } else{ + m_title_nozzle_type->Hide(); + m_comboBox_nozzle_type->Hide(); + + auto font = m_title_pa_profile->GetFont(); + font.SetUnderlined(false); + m_title_pa_profile->SetFont(font); + m_title_pa_profile->SetToolTip(wxEmptyString); + } + + Layout(); + Fit(); +} + +int AMSMaterialsSetting::get_nozzle_combo_id_code() const{ + auto sel = m_comboBox_nozzle_type->GetSelection(); + if (sel != wxNOT_FOUND) return *(reinterpret_cast(m_comboBox_nozzle_type->GetClientData(sel))); + + return -1; +} + +int AMSMaterialsSetting::get_nozzle_sel_by_sn(MachineObject* obj, const std::string& sn){ + if(!obj) return -1; + + auto nozzle = obj->get_nozzle_by_sn(sn); + + for(unsigned int i = 0; iGetCount(); i++){ + auto sel_pair = (std::pair*)m_comboBox_nozzle_type->GetClientData(i); + + if(sel_pair->first == nozzle.GetNozzleDiameterType() && sel_pair->second == nozzle.GetNozzleFlowType()) + return i; + } + + return -1; +} + +int AMSMaterialsSetting::get_cali_index_by_ams_slot(MachineObject *obj, int ams_id, int slot_id) +{ + if (!obj) return -1; + + // Get the flag whether to open the filament setting dialog from the device page + int *from_printer = static_cast(m_comboBox_filament->GetClientData()); + if (!from_printer || (*from_printer != 1)) return -1; + + if (obj->cali_version >= 0) { + if (ams_id == VIRTUAL_TRAY_MAIN_ID || ams_id == VIRTUAL_TRAY_DEPUTY_ID) { + for (auto slot : obj->vt_slot) { + if (slot.id == std::to_string(ams_id)) return slot.cali_idx; + } + } else { + DevAmsTray *selected_tray = obj->GetFilaSystem()->GetAmsTray(std::to_string(ams_id), std::to_string(slot_id)); + if (!selected_tray) { + return -1; + } else { + return selected_tray->cali_idx; + } + } + } + + return -1; +} + void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt) { // Get the flag whether to open the filament setting dialog from the device page @@ -1128,7 +1355,7 @@ void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt) nozzle_diameter_str); for (auto it = preset_bundle->filaments.begin(); it != preset_bundle->filaments.end(); it++) { if (!m_comboBox_filament->GetValue().IsEmpty()) { - auto filament_item = map_filament_items[m_comboBox_filament->GetValue().ToStdString()]; + auto filament_item = map_filament_items[into_u8(m_comboBox_filament->GetValue())]; std::string filament_id = filament_item.filament_id; if (it->filament_id.compare(filament_id) == 0) { bool has_compatible_printer = false; @@ -1188,6 +1415,8 @@ void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt) //reset cali int cali_select_idx = -1; + update_nozzle_combo(obj); + if ( !this->obj || m_filament_selection < 0) { m_input_k_val->Enable(false); m_input_n_val->Enable(false); @@ -1196,6 +1425,8 @@ void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt) m_button_confirm->SetBorderColor(wxColour(0x90, 0x90, 0x90)); m_comboBox_cali_result->Clear(); m_comboBox_cali_result->SetValue(wxEmptyString); + m_comboBox_nozzle_type->Clear(); + m_comboBox_nozzle_type->SetValue(wxEmptyString); m_input_k_val->GetTextCtrl()->SetValue(wxEmptyString); m_input_n_val->GetTextCtrl()->SetValue(wxEmptyString); m_comboBox_filament->SetClientData(new int(0)); @@ -1214,14 +1445,14 @@ void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt) if (preset_bundle) { for (auto it = preset_bundle->filaments.begin(); it != preset_bundle->filaments.end(); it++) { - auto itor = map_filament_items.find(m_comboBox_filament->GetValue().ToStdString()); + auto itor = map_filament_items.find(into_u8(m_comboBox_filament->GetValue())); if ( itor != map_filament_items.end()) { ams_filament_id = itor->second.filament_id; ams_setting_id = itor->second.setting_id; break; } - if (it->alias.compare(m_comboBox_filament->GetValue().ToStdString()) == 0) { + if (it->alias.compare(into_u8(m_comboBox_filament->GetValue())) == 0) { ams_filament_id = it->filament_id; ams_setting_id = it->setting_id; break; @@ -1229,10 +1460,6 @@ void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt) } } - wxArrayString items; - m_pa_profile_items.clear(); - m_comboBox_cali_result->SetValue(wxEmptyString); - auto get_cali_index = [this](const std::string& str) -> int{ for (int i = 0; i < int(m_pa_profile_items.size()); ++i) { if (m_pa_profile_items[i].name == str) @@ -1248,79 +1475,42 @@ void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt) dlg.ShowModal(); } - NozzleFlowType nozzle_flow_type = obj->GetExtderSystem()->GetNozzleFlowType(extruder_id); - NozzleVolumeType nozzle_volume_type = NozzleVolumeType::nvtStandard; - if (nozzle_flow_type != NozzleFlowType::NONE_FLOWTYPE) - { - nozzle_volume_type = NozzleVolumeType(nozzle_flow_type - 1); + std::vector cali_history = obj->pa_calib_tab; + auto iter = std::find_if(cali_history.begin(), cali_history.end(), [cur_cali_idx=get_cali_index_by_ams_slot(obj, ams_id, slot_id)](const PACalibResult& item){ + return item.cali_idx == cur_cali_idx; + }); + if (iter != cali_history.end() && !iter->nozzle_sn.empty() && iter->nozzle_sn != "N/A") { + int sel = get_nozzle_sel_by_sn(obj, iter->nozzle_sn); + m_comboBox_nozzle_type->SetSelection(sel); + } else { + m_comboBox_nozzle_type->SetSelection(-1); + m_comboBox_nozzle_type->SetValue(wxEmptyString); } if (obj->cali_version >= 0) { - // add default item - PACalibResult default_item; - default_item.cali_idx = -1; - default_item.filament_id = ams_filament_id; - if (obj->GetConfig()->SupportCalibrationPA_FlowAuto()) { - default_item.k_value = -1; - default_item.n_coef = -1; - } - else { - get_default_k_n_value(ams_filament_id, default_item.k_value, default_item.n_coef); - } - m_pa_profile_items.emplace_back(default_item); - items.push_back(_L("Default")); + update_pa_profile_items(); - m_input_k_val->GetTextCtrl()->SetValue(wxEmptyString); - std::vector cali_history = this->obj->pa_calib_tab; - for (auto cali_item : cali_history) { - if (cali_item.filament_id == ams_filament_id) { - if (obj->is_multi_extruders() && (cali_item.extruder_id != extruder_id || cali_item.nozzle_volume_type != nozzle_volume_type)) { - continue; - } - items.push_back(from_u8(cali_item.name)); - m_pa_profile_items.push_back(cali_item); - } - } + int cali_idx = get_cali_index_by_ams_slot(obj, ams_id, slot_id); + /* get sel_idx of cali idex in pa_profile_items */ + cali_select_idx = CalibUtils::get_selected_calib_idx(m_pa_profile_items, cali_idx); - m_comboBox_cali_result->Set(items); - if (ams_id == VIRTUAL_TRAY_MAIN_ID || ams_id == VIRTUAL_TRAY_DEPUTY_ID) { - if (from_printer && (*from_printer == 1)) { - for (auto slot : obj->vt_slot) { - if (slot.id == std::to_string(ams_id)) - cali_select_idx = CalibUtils::get_selected_calib_idx(m_pa_profile_items, slot.cali_idx); - } - - if (cali_select_idx >= 0) - m_comboBox_cali_result->SetSelection(cali_select_idx); - else - m_comboBox_cali_result->SetSelection(0); - } - else { - int index = get_cali_index(m_comboBox_filament->GetLabel().ToStdString()); - m_comboBox_cali_result->SetSelection(index); - } - } - else { - if (from_printer && (*from_printer == 1)) { - DevAmsTray* selected_tray = this->obj->GetFilaSystem()->GetAmsTray(std::to_string(ams_id), std::to_string(slot_id)); - if (!selected_tray) - { - return; - } - - cali_select_idx = CalibUtils::get_selected_calib_idx(m_pa_profile_items, selected_tray->cali_idx); - if (cali_select_idx < 0) - { - BOOST_LOG_TRIVIAL(info) << "extrusion_cali_status_error: cannot find pa profile, ams_id = " << ams_id - << ", slot_id = " << slot_id << ", cali_idx = " << selected_tray->cali_idx; - cali_select_idx = 0; - } + if(from_printer && (*from_printer == 1)) { + if (cali_select_idx >= 0) { m_comboBox_cali_result->SetSelection(cali_select_idx); + } else { + m_comboBox_cali_result->SetSelection(0); + BOOST_LOG_TRIVIAL(info) << "extrusion_cali_status_error: cannot find pa profile" + << ", ams_id = " << ams_id + << ", slot_id = " << slot_id + << ", cali_idx = " << cali_idx; } - else { - int index = get_cali_index(m_comboBox_filament->GetLabel().ToStdString()); - m_comboBox_cali_result->SetSelection(index); - } + } else { +#ifdef __APPLE__ + cali_select_idx = get_cali_index(m_comboBox_filament->GetValue().ToStdString()); +#else + cali_select_idx = get_cali_index(m_comboBox_filament->GetLabel().ToStdString()); +#endif + m_comboBox_cali_result->SetSelection(cali_select_idx); } if (cali_select_idx >= 0) { @@ -1333,14 +1523,8 @@ void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt) } } else { - if (!ams_filament_id.empty()) { - //m_input_k_val->GetTextCtrl()->SetValue("0.00"); - m_input_k_val->Enable(true); - } - else { - //m_input_k_val->GetTextCtrl()->SetValue("0.00"); - m_input_k_val->Disable(); - } + //m_input_k_val->GetTextCtrl()->SetValue("0.00"); + m_input_k_val->Enable(!ams_filament_id.empty()); } m_comboBox_filament->SetClientData(new int(0)); diff --git a/src/slic3r/GUI/AMSMaterialsSetting.hpp b/src/slic3r/GUI/AMSMaterialsSetting.hpp index 1d92213..4ec5b39 100644 --- a/src/slic3r/GUI/AMSMaterialsSetting.hpp +++ b/src/slic3r/GUI/AMSMaterialsSetting.hpp @@ -15,6 +15,7 @@ #include "Widgets/ComboBox.hpp" #include "Widgets/TextInput.hpp" #include "../slic3r/Utils/CalibUtils.hpp" +#include "DeviceCore/DevNozzleRack.h" #define AMS_MATERIALS_SETTING_DEF_COLOUR wxColour(255, 255, 255) #define AMS_MATERIALS_SETTING_GREY900 wxColour(38, 46, 48) @@ -137,8 +138,10 @@ protected: void create_panel_normal(wxWindow* parent); void create_panel_kn(wxWindow* parent); void on_dpi_changed(const wxRect &suggested_rect) override; + void on_select_nozzle_id(wxCommandEvent &evt); void on_select_filament(wxCommandEvent& evt); void on_select_cali_result(wxCommandEvent &evt); + void on_select_nozzle_pos_id(wxCommandEvent &evt); void on_select_ok(wxCommandEvent &event); void on_select_reset(wxCommandEvent &event); void on_select_close(wxCommandEvent &event); @@ -146,7 +149,12 @@ protected: bool is_virtual_tray(); void update_widgets(); + void update_pa_profile_items(); void update_filament_editing(bool is_printing); + void update_nozzle_combo(MachineObject* obj); + int get_nozzle_combo_id_code() const; + int get_nozzle_sel_by_sn(MachineObject* obj, const std::string& sn); + int get_cali_index_by_ams_slot(MachineObject* obj, int ams_id, int slot_id); protected: StateColor m_btn_bg_blue; @@ -157,6 +165,7 @@ protected: wxStaticText * warning_text; //wxPanel * m_panel_body; wxStaticText * m_title_filament; + wxStaticText * m_title_nozzle_type; wxStaticText * m_title_pa_profile; wxStaticText * m_title_colour; wxStaticText * m_title_temperature; @@ -187,6 +196,7 @@ protected: #else ComboBox *m_comboBox_filament; #endif + ComboBox * m_comboBox_nozzle_type; ComboBox * m_comboBox_cali_result; TextInput* m_readonly_filament; diff --git a/src/slic3r/GUI/AMSSetting.cpp b/src/slic3r/GUI/AMSSetting.cpp index 1eb7575..7d2f83f 100644 --- a/src/slic3r/GUI/AMSSetting.cpp +++ b/src/slic3r/GUI/AMSSetting.cpp @@ -4,6 +4,8 @@ #include "slic3r/GUI/DeviceCore/DevExtruderSystem.h" #include "slic3r/GUI/DeviceCore/DevFilaSystem.h" +#include "slic3r/GUI/DeviceCore/DevUpgrade.h" + #include "slic3r/GUI/DeviceCore/DevManager.h" #include "slic3r/GUI/MsgDialog.hpp" @@ -644,7 +646,7 @@ void AMSSettingTypePanel::Update(const MachineObject* obj) } if (ptr->IsSwitching()) { - int display_percent = obj->get_upgrade_percent(); + int display_percent = obj->GetUpgrade().lock()->GetUpgradeProgressInt(); if (display_percent == 100 || display_percent == 0) { display_percent = 1;// special case, sometimes it's switching but percent is 0 or 100 } @@ -663,11 +665,7 @@ void AMSSettingTypePanel::Update(const MachineObject* obj) m_type_combobox->Clear(); for (auto ams_firmware : m_ams_firmwares) { - if (m_ams_firmware_current_idx == ams_firmware.first) { - m_type_combobox->Append(_L(ams_firmware.second.m_name)); - } else { - m_type_combobox->Append(_L(ams_firmware.second.m_name)); - } + m_type_combobox->Append(_L(ams_firmware.second.m_name)); } m_type_combobox->SetSelection(m_ams_firmware_current_idx); } diff --git a/src/slic3r/GUI/AMSSetting.hpp b/src/slic3r/GUI/AMSSetting.hpp index c317ff1..8e8a258 100644 --- a/src/slic3r/GUI/AMSSetting.hpp +++ b/src/slic3r/GUI/AMSSetting.hpp @@ -120,7 +120,7 @@ private: std::weak_ptr m_ams_firmware_switch; int m_ams_firmware_current_idx{ -1 }; - std::unordered_map m_ams_firmwares; + std::map m_ams_firmwares; // widgets AMSSetting* m_setting_dlg; diff --git a/src/slic3r/GUI/AmsMappingPopup.cpp b/src/slic3r/GUI/AmsMappingPopup.cpp index a9684ec..4c2de0a 100644 --- a/src/slic3r/GUI/AmsMappingPopup.cpp +++ b/src/slic3r/GUI/AmsMappingPopup.cpp @@ -3,6 +3,7 @@ #include "libslic3r/Utils.hpp" #include "libslic3r/Thread.hpp" +#include "slic3r/Utils/WxFontUtils.hpp" #include "GUI.hpp" #include "GUI_App.hpp" #include "GUI_Preview.hpp" @@ -25,6 +26,8 @@ #include "DeviceCore/DevFilaSystem.h" +#include "DeviceTab/wgtDeviceNozzleSelect.h" + namespace Slic3r { namespace GUI { #define MATERIAL_ITEM_SIZE wxSize(FromDIP(65), FromDIP(50)) #define MATERIAL_REC_WHEEL_SIZE wxSize(FromDIP(17), FromDIP(16)) @@ -54,7 +57,7 @@ static void _add_containers(const AmsMapingPopup * win, } } - MaterialItem::MaterialItem(wxWindow *parent, wxColour mcolour, wxString mname) + MaterialItem::MaterialItem(wxWindow *parent, wxColour mcolour, wxString mname, std::string filament_id) : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize) { m_arraw_bitmap_gray = ScalableBitmap(this, "drop_down2", FromDIP(8)); @@ -64,18 +67,19 @@ static void _add_containers(const AmsMapingPopup * win, //m_ams_wheel_mitem = ScalableBitmap(this, "ams_wheel", FromDIP(25)); m_ams_wheel_mitem = ScalableBitmap(this, "ams_wheel_narrow", 25); m_ams_not_match = ScalableBitmap(this, "filament_not_mactch", 25); + m_rack_nozzle_bitmap = ScalableBitmap(this, "dev_rack_nozzle_print_job", 22); m_material_coloul = mcolour; m_material_name = mname; m_ams_coloul = wxColour(0xEE,0xEE,0xEE); + m_filament_id = filament_id; + #ifdef __WINDOWS__ SetDoubleBuffered(true); #endif //__WINDOWS__ - SetSize(MATERIAL_ITEM_SIZE); - SetMinSize(MATERIAL_ITEM_SIZE); - SetMaxSize(MATERIAL_ITEM_SIZE); + messure_size(); SetBackgroundColour(*wxWHITE); Bind(wxEVT_PAINT, &MaterialItem::paintEvent, this); @@ -110,23 +114,35 @@ void MaterialItem::set_ams_info(wxColour col, wxString txt, int ctype, std::vect if (m_ams_ctype != ctype) { m_ams_ctype = ctype; need_refresh = true; } if (m_ams_coloul != col) { m_ams_coloul = col; need_refresh = true;} if (m_ams_name != txt) { m_ams_name = txt; need_refresh = true; } - if (need_refresh) { Refresh();} + if (need_refresh) { + messure_size(); + Refresh(); + } BOOST_LOG_TRIVIAL(info) << "set_ams_info " << m_ams_name; } +void MaterialItem::set_nozzle_info(const wxString& mapped_nozzle_str) +{ + if (m_mapped_nozzle_str != mapped_nozzle_str) { + m_mapped_nozzle_str = mapped_nozzle_str; + messure_size(); + Refresh(); + } +} + void MaterialItem::reset_ams_info() { m_ams_name = "-"; m_ams_coloul = wxColour(0xCE, 0xCE, 0xCE); m_ams_cols.clear(); m_ams_ctype = 0; + m_mapped_nozzle_str.Clear(); + messure_size(); Refresh(); } void MaterialItem::disable() { if (IsEnabled()) { - //this->Disable(); - //Refresh(); m_enable = false; } } @@ -134,8 +150,6 @@ void MaterialItem::disable() void MaterialItem::enable() { if (!IsEnabled()) { - /*this->Enable(); - Refresh();*/ m_enable = true; } } @@ -170,23 +184,17 @@ void MaterialItem::paintEvent(wxPaintEvent &evt) { wxPaintDC dc(this); render(dc); - - //PrepareDC(buffdc); - //PrepareDC(dc); - } void MaterialItem::render(wxDC &dc) { - - wxString mapping_txt = wxEmptyString; if (m_ams_name.empty()) { - mapping_txt = "-"; + m_mapping_text = "-"; } else { - mapping_txt = m_ams_name; + m_mapping_text = m_ams_name; } - if (mapping_txt == "-") { m_match = false;} + if (m_mapping_text == "-") { m_match = false;} else {m_match = true;} #ifdef __WXMSW__ @@ -206,47 +214,6 @@ void MaterialItem::render(wxDC &dc) #else doRender(dc); #endif - - auto mcolor = m_material_coloul; - auto acolor = m_ams_coloul; - change_the_opacity(acolor); - if (!IsEnabled()) { - mcolor = wxColour(0x90, 0x90, 0x90); - acolor = wxColour(0x90, 0x90, 0x90); - } - - // materials name - dc.SetFont(::Label::Body_13); - - auto material_name_colour = mcolor.GetLuminance() < 0.6 ? *wxWHITE : wxColour(0x26, 0x2E, 0x30); - if (mcolor.Alpha() == 0) {material_name_colour = wxColour(0x26, 0x2E, 0x30);} - dc.SetTextForeground(material_name_colour); - - if (dc.GetTextExtent(m_material_name).x > GetSize().x - 10) { - dc.SetFont(::Label::Body_10); - } - - auto material_txt_size = dc.GetTextExtent(m_material_name); - dc.DrawText(m_material_name, wxPoint((GetSize().x - material_txt_size.x) / 2, ((float)GetSize().y * 2 / 5 - material_txt_size.y) / 2)); - - dc.SetTextForeground(StateColor::darkModeColorFor(wxColour(0x26, 0x2E, 0x30))); - dc.SetFont(::Label::Head_12); - - auto mapping_txt_size = wxSize(0, 0); - if (mapping_txt.size() >= 4) { - mapping_txt.insert(mapping_txt.size() / 2, "\n"); - mapping_txt_size = dc.GetTextExtent(mapping_txt); - m_text_pos_y = ((float) GetSize().y * 3 / 5 - mapping_txt_size.y) / 2 + (float) GetSize().y * 2 / 5 - mapping_txt_size.y / 2; - m_text_pos_x = mapping_txt_size.x / 4; - } else { - mapping_txt_size = dc.GetTextExtent(mapping_txt); - m_text_pos_y = ((float) GetSize().y * 3 / 5 - mapping_txt_size.y) / 2 + (float) GetSize().y * 2 / 5; - m_text_pos_x = 0; - } - - if (m_match) { - dc.DrawText(mapping_txt, wxPoint(GetSize().x / 2 + (GetSize().x / 2 - mapping_txt_size.x) / 2 - FromDIP(8) - FromDIP(LEFT_OFFSET) + m_text_pos_x, m_text_pos_y)); - } } @@ -263,11 +230,13 @@ void MaterialItem::doRender(wxDC& dc) auto acolor = m_ams_coloul; change_the_opacity(acolor); - if (mcolor.Alpha() == 0) { + if (mcolor.Alpha() == 0) + { dc.DrawBitmap(m_transparent_mitem.bmp(), FromDIP(1), FromDIP(1)); } - if (!IsEnabled()) { + if (!IsEnabled()) + { mcolor = wxColour(0x90, 0x90, 0x90); acolor = wxColour(0x90, 0x90, 0x90); } @@ -275,108 +244,174 @@ void MaterialItem::doRender(wxDC& dc) //top dc.SetPen(*wxTRANSPARENT_PEN); dc.SetBrush(wxBrush(mcolor)); - dc.DrawRoundedRectangle(0, 0, MATERIAL_ITEM_SIZE.x, FromDIP(20), 5); + dc.DrawRoundedRectangle(0, 0, size.x, FromDIP(20), 5); dc.SetPen(*wxTRANSPARENT_PEN); dc.SetBrush(wxBrush(mcolor)); - dc.DrawRectangle(0, FromDIP(10), MATERIAL_ITEM_SIZE.x, FromDIP(10)); + dc.DrawRectangle(0, FromDIP(10), size.x, FromDIP(10)); + + // materials name + auto up = 0; + auto material_name_colour = mcolor.GetLuminance() < 0.6 ? *wxWHITE : wxColour(0x26, 0x2E, 0x30); + if (mcolor.Alpha() == 0) { material_name_colour = wxColour(0x26, 0x2E, 0x30); } + dc.SetTextForeground(material_name_colour); + + dc.SetFont(::Label::Body_12); + if (dc.GetTextExtent(m_material_name).x > GetSize().x - 10) + { + dc.SetFont(::Label::Body_10); + } + + auto material_txt_size = dc.GetTextExtent(m_material_name); + dc.DrawText(m_material_name, wxPoint((GetSize().x - material_txt_size.x) / 2, (FromDIP(20) - material_txt_size.y) / 2)); dc.SetPen(wxColour(0xAC, 0xAC, 0xAC)); - dc.DrawLine(FromDIP(1), FromDIP(20), FromDIP(MATERIAL_ITEM_SIZE.x), FromDIP(20)); + dc.DrawLine(FromDIP(1), FromDIP(20), FromDIP(size.x), FromDIP(20)); + up += FromDIP(20); + up += FromDIP(2); // spacing //bottom rectangle in wheel bitmap, size is MATERIAL_REC_WHEEL_SIZE(22) + auto paint_recty = up + (m_ams_wheel_mitem.GetBmpSize().y - MATERIAL_REC_WHEEL_SIZE.y) / 2; auto left = (size.x / 2 - MATERIAL_REC_WHEEL_SIZE.x) / 2 + FromDIP(3); - auto up = (size.y * 0.4 + (size.y * 0.6 - MATERIAL_REC_WHEEL_SIZE.y) / 2); auto right = left + MATERIAL_REC_WHEEL_SIZE.x - FromDIP(3); dc.SetPen(*wxTRANSPARENT_PEN); //bottom - if (m_ams_cols.size() > 1) { + if (m_ams_cols.size() > 1) + { int gwidth = std::round(MATERIAL_REC_WHEEL_SIZE.x / (m_ams_cols.size() - 1)); //gradient - if (m_ams_ctype == 0) { - for (int i = 0; i < m_ams_cols.size() - 1; i++) { - auto rect = wxRect(left, up, right - left, MATERIAL_REC_WHEEL_SIZE.y); + if (m_ams_ctype == 0) + { + for (int i = 0; i < m_ams_cols.size() - 1; i++) + { + auto rect = wxRect(left, paint_recty, right - left, MATERIAL_REC_WHEEL_SIZE.y); dc.GradientFillLinear(rect, m_ams_cols[i], m_ams_cols[i + 1], wxEAST); left += gwidth; } } - else { + else + { int cols_size = m_ams_cols.size(); - for (int i = 0; i < cols_size; i++) { + for (int i = 0; i < cols_size; i++) + { dc.SetBrush(wxBrush(m_ams_cols[i])); float x = left + ((float)MATERIAL_REC_WHEEL_SIZE.x) * i / cols_size; - if (i != cols_size - 1) { - dc.DrawRoundedRectangle(x - FromDIP(LEFT_OFFSET), up, ((float) MATERIAL_REC_WHEEL_SIZE.x) / cols_size + FromDIP(3), MATERIAL_REC_WHEEL_SIZE.y, 3); + if (i != cols_size - 1) + { + dc.DrawRoundedRectangle(x - FromDIP(LEFT_OFFSET), paint_recty, ((float)MATERIAL_REC_WHEEL_SIZE.x) / cols_size + FromDIP(3), MATERIAL_REC_WHEEL_SIZE.y, 3); } - else { - dc.DrawRoundedRectangle(x - FromDIP(LEFT_OFFSET), up, ((float) MATERIAL_REC_WHEEL_SIZE.x) / cols_size - FromDIP(1), MATERIAL_REC_WHEEL_SIZE.y, 3); + else + { + dc.DrawRoundedRectangle(x - FromDIP(LEFT_OFFSET), paint_recty, ((float)MATERIAL_REC_WHEEL_SIZE.x) / cols_size - FromDIP(1), MATERIAL_REC_WHEEL_SIZE.y, 3); } } } } - else { - if (m_match) { + else + { + if (m_match) + { dc.SetPen(*wxTRANSPARENT_PEN); dc.SetBrush(wxBrush(wxColour(acolor))); - dc.DrawRectangle((size.x / 2 - MATERIAL_REC_WHEEL_SIZE.x) / 2 + FromDIP(3) - FromDIP(LEFT_OFFSET), up, MATERIAL_REC_WHEEL_SIZE.x - FromDIP(1), - MATERIAL_REC_WHEEL_SIZE.y); + dc.DrawRectangle((size.x / 2 - MATERIAL_REC_WHEEL_SIZE.x) / 2 + FromDIP(3) - FromDIP(LEFT_OFFSET), paint_recty, MATERIAL_REC_WHEEL_SIZE.x - FromDIP(1), + MATERIAL_REC_WHEEL_SIZE.y); } } - - ////border -//#if __APPLE__ -// if (m_match) { -// dc.SetPen(wxColour(0xAC, 0xAC, 0xAC)); -// } else { -// dc.SetPen(wxPen(wxColour(234, 31, 48), 2)); -// } -// dc.SetBrush(*wxTRANSPARENT_BRUSH); -// dc.DrawRoundedRectangle(FromDIP(1), FromDIP(1), MATERIAL_ITEM_SIZE.x - FromDIP(1), MATERIAL_ITEM_SIZE.y - FromDIP(1), 5); -// -// if (m_selected) { -// dc.SetPen(wxColour(0x44, 0x79, 0xFB)); -// dc.SetBrush(*wxTRANSPARENT_BRUSH); -// dc.DrawRoundedRectangle(FromDIP(1), FromDIP(1), MATERIAL_ITEM_SIZE.x - FromDIP(1), MATERIAL_ITEM_SIZE.y - FromDIP(1), 5); -// } -//#else - - if (m_match) { + if (m_match) + { dc.SetPen(wxPen(wxGetApp().dark_mode() ? wxColour(107, 107, 107) : wxColour(0xAC, 0xAC, 0xAC), FromDIP(1))); - } else { + } + else + { dc.SetPen(wxPen(wxColour(234, 31, 48), FromDIP(1))); } dc.SetBrush(*wxTRANSPARENT_BRUSH); - dc.DrawRoundedRectangle(FromDIP(0), FromDIP(0), MATERIAL_ITEM_SIZE.x - FromDIP(0), MATERIAL_ITEM_SIZE.y - FromDIP(0), 5); + dc.DrawRoundedRectangle(FromDIP(0), FromDIP(0), size.x - FromDIP(0), size.y, 5); - if (m_selected) { + if (m_selected) + { dc.SetPen(wxPen(wxColour(0x44, 0x79, 0xFB), FromDIP(2))); dc.SetBrush(*wxTRANSPARENT_BRUSH); - dc.DrawRoundedRectangle(FromDIP(1), FromDIP(1), MATERIAL_ITEM_SIZE.x - FromDIP(1), MATERIAL_ITEM_SIZE.y - FromDIP(1), 5); + dc.DrawRoundedRectangle(FromDIP(1), FromDIP(1), size.x - FromDIP(1), size.y - FromDIP(1), 5); } -//#endif - if (m_text_pos_y > 0 && m_match) { - // arrow (remove arrow) - if ((acolor.Red() > 160 && acolor.Green() > 160 && acolor.Blue() > 160) && (acolor.Red() < 180 && acolor.Green() < 180 && acolor.Blue() < 180)) { - dc.DrawBitmap(m_arraw_bitmap_white.bmp(), size.x - m_arraw_bitmap_white.GetBmpSize().x - FromDIP(4)- FromDIP(LEFT_OFFSET), m_text_pos_y + FromDIP(3)); - } else { - dc.DrawBitmap(m_arraw_bitmap_gray.bmp(), size.x - m_arraw_bitmap_gray.GetBmpSize().x - FromDIP(4)- FromDIP(LEFT_OFFSET), m_text_pos_y + FromDIP(3)); + //#endif + + auto wheel_left = (GetSize().x / 2 - m_ams_wheel_mitem.GetBmpSize().x) / 2 + FromDIP(2) - FromDIP(LEFT_OFFSET); + auto wheel_top = up; + + if (!m_match) + { + wheel_left = (size.x - m_ams_not_match.GetBmpWidth()) / 2 - FromDIP(LEFT_OFFSET); + dc.DrawBitmap(m_ams_not_match.bmp(), wheel_left, wheel_top); + } + else + { + if (acolor.Alpha() == 0) + { + dc.DrawBitmap(m_filament_wheel_transparent.bmp(), wheel_left, wheel_top); + } + else + { + dc.DrawBitmap(m_ams_wheel_mitem.bmp(), wheel_left, wheel_top); } } - auto wheel_left = (GetSize().x / 2 - m_ams_wheel_mitem.GetBmpSize().x) / 2 + FromDIP(2); - auto wheel_top = ((float)GetSize().y * 0.6 - m_ams_wheel_mitem.GetBmpSize().y) / 2 + (float)GetSize().y * 0.4; + // text and arrow + dc.SetTextForeground(StateColor::darkModeColorFor(wxColour(0x26, 0x2E, 0x30))); - if (!m_match) { - wheel_left += m_ams_wheel_mitem.GetBmpSize().x; - dc.DrawBitmap(m_ams_not_match.bmp(), (size.x - m_ams_not_match.GetBmpWidth()) / 2 - FromDIP(LEFT_OFFSET), wheel_top); - } else { - if (acolor.Alpha() == 0) { - dc.DrawBitmap(m_filament_wheel_transparent.bmp(), wheel_left - FromDIP(LEFT_OFFSET), wheel_top); - } else { - dc.DrawBitmap(m_ams_wheel_mitem.bmp(), wheel_left - FromDIP(LEFT_OFFSET), wheel_top); + int text_pos_x = 0; + int text_pos_y = 0; + auto mapping_txt_size = wxSize(0, 0); + if (m_mapping_text.size() >= 4) + { + m_mapping_text.insert(m_mapping_text.size() / 2, "\n"); + WxFontUtils::get_suitable_font_size(FromDIP(13), dc); + mapping_txt_size = dc.GetTextExtent(m_mapping_text); + text_pos_y = up + (m_ams_wheel_mitem.GetBmpSize().y - mapping_txt_size.y) / 2; + text_pos_x = mapping_txt_size.x / 4; + } + else + { + dc.SetFont(::Label::Head_12); + mapping_txt_size = dc.GetTextExtent(m_mapping_text); + text_pos_y = up + (m_ams_wheel_mitem.GetBmpSize().y - mapping_txt_size.y) / 2; + text_pos_x = 0; + } + + int arrow_left = size.x - m_arraw_bitmap_white.GetBmpSize().x - FromDIP(4) - FromDIP(LEFT_OFFSET); + if (m_match) + { + dc.DrawText(m_mapping_text, wxPoint(GetSize().x / 2 + (GetSize().x / 2 - mapping_txt_size.x) / 2 - FromDIP(8) - FromDIP(LEFT_OFFSET) + text_pos_x, text_pos_y)); + + int arrow_y = text_pos_y + (mapping_txt_size.y - m_arraw_bitmap_white.GetBmpHeight()) / 2; + if ((acolor.Red() > 160 && acolor.Green() > 160 && acolor.Blue() > 160) && (acolor.Red() < 180 && acolor.Green() < 180 && acolor.Blue() < 180)) + { + dc.DrawBitmap(m_arraw_bitmap_white.bmp(), arrow_left, arrow_y); } + else + { + dc.DrawBitmap(m_arraw_bitmap_gray.bmp(), arrow_left, arrow_y); + } + } + + up += m_ams_wheel_mitem.GetBmpSize().y; + + + if (!m_mapped_nozzle_str.IsEmpty()) { + wxPen dashed_pen(WXCOLOUR_GREY400, 1, wxPENSTYLE_SHORT_DASH); + dc.SetPen(dashed_pen); + up += FromDIP(4); // spacing + dc.DrawLine(FromDIP(1), up, FromDIP(size.x), up); + + int bitmap_y = up + (size.y - up - m_rack_nozzle_bitmap.GetBmpHeight()) / 2; + int bitmap_l = wheel_left + (m_ams_wheel_mitem.GetBmpWidth() - m_rack_nozzle_bitmap.GetBmpWidth()) / 2 + FromDIP(2); + dc.DrawBitmap(m_rack_nozzle_bitmap.bmp(), bitmap_l, bitmap_y); + + int text_y = up + (size.y - up - dc.GetTextExtent(m_mapped_nozzle_str).y) / 2; + dc.SetFont(::Label::Head_12); + dc.DrawText(m_mapped_nozzle_str, bitmap_l + m_rack_nozzle_bitmap.GetBmpWidth() + FromDIP(12), text_y); } } @@ -384,7 +419,20 @@ void MaterialItem::reset_valid_info() { set_ams_info(m_back_ams_coloul, m_back_ams_name, m_back_ams_ctype, m_back_ams_cols); } - MaterialSyncItem::MaterialSyncItem(wxWindow *parent, wxColour mcolour, wxString mname) : MaterialItem(parent, mcolour, mname) +void MaterialItem::messure_size() +{ + if (m_mapped_nozzle_str.IsEmpty()) { + SetSize(wxSize(FromDIP(65), FromDIP(50))); + SetMinSize(wxSize(FromDIP(65), FromDIP(50))); + SetMaxSize(wxSize(FromDIP(65), FromDIP(50))); + } else { + SetSize(wxSize(FromDIP(65), FromDIP(84))); + SetMinSize(wxSize(FromDIP(65), FromDIP(84))); + SetMaxSize(wxSize(FromDIP(65), FromDIP(84))); + } +} + +MaterialSyncItem::MaterialSyncItem(wxWindow *parent, wxColour mcolour, wxString mname, std::string filament_id) : MaterialItem(parent, mcolour, mname, filament_id) { } @@ -501,14 +549,14 @@ void MaterialSyncItem::doRender(wxDC &dc) // top dc.SetPen(*wxTRANSPARENT_PEN); dc.SetBrush(wxBrush(mcolor)); - dc.DrawRoundedRectangle(0, 0, MATERIAL_ITEM_SIZE.x, FromDIP(20), 5); + dc.DrawRoundedRectangle(0, 0, size.x, FromDIP(20), 5); dc.SetPen(*wxTRANSPARENT_PEN); dc.SetBrush(wxBrush(mcolor)); - dc.DrawRectangle(0, FromDIP(10), MATERIAL_ITEM_SIZE.x, FromDIP(10)); + dc.DrawRectangle(0, FromDIP(10), size.x, FromDIP(10)); dc.SetPen(wxColour(0xAC, 0xAC, 0xAC)); - dc.DrawLine(FromDIP(1), FromDIP(20), FromDIP(MATERIAL_ITEM_SIZE.x), FromDIP(20)); + dc.DrawLine(FromDIP(1), FromDIP(20), FromDIP(size.x), FromDIP(20)); // bottom rectangle in wheel bitmap, size is MATERIAL_REC_WHEEL_SIZE(22) auto left = (size.x / 2 - MATERIAL_REC_WHEEL_SIZE.x) / 2 + FromDIP(3); auto up = (size.y * 0.4 + (size.y * 0.6 - MATERIAL_REC_WHEEL_SIZE.y) / 2); @@ -555,32 +603,32 @@ void MaterialSyncItem::doRender(wxDC &dc) else { dc.SetPen(*wxTRANSPARENT_PEN); dc.SetBrush(wxBrush(mcolor)); - dc.DrawRoundedRectangle(0, FromDIP(21), MATERIAL_ITEM_SIZE.x, MATERIAL_ITEM_SIZE.y - FromDIP(21), 5); + dc.DrawRoundedRectangle(0, FromDIP(21), size.x, size.y - FromDIP(21), 5); - dc.DrawRectangle(0, FromDIP(21), MATERIAL_ITEM_SIZE.x, FromDIP(10)); + dc.DrawRectangle(0, FromDIP(21), size.x, FromDIP(10)); } ////border #if __APPLE__ dc.SetPen(wxColour(0xAC, 0xAC, 0xAC)); dc.SetBrush(*wxTRANSPARENT_BRUSH); - dc.DrawRoundedRectangle(1, 1, MATERIAL_ITEM_SIZE.x - 1, MATERIAL_ITEM_SIZE.y - 1, 5); + dc.DrawRoundedRectangle(1, 1, size.x - 1, size.y - 1, 5); if (m_selected) { dc.SetPen(wxColour(0x44, 0x79, 0xFB)); dc.SetBrush(*wxTRANSPARENT_BRUSH); - dc.DrawRoundedRectangle(1, 1, MATERIAL_ITEM_SIZE.x - 1, MATERIAL_ITEM_SIZE.y - 1, 5); + dc.DrawRoundedRectangle(1, 1, size.x - 1, size.y - 1, 5); } #else dc.SetPen(wxPen(wxGetApp().dark_mode() ? wxColour(107, 107, 107) : wxColour(0xAC, 0xAC, 0xAC), FromDIP(1))); dc.SetBrush(*wxTRANSPARENT_BRUSH); - dc.DrawRoundedRectangle(0, 0, MATERIAL_ITEM_SIZE.x, MATERIAL_ITEM_SIZE.y, 5); + dc.DrawRoundedRectangle(0, 0, size.x, size.y, 5); if (m_selected) { dc.SetPen(wxPen(wxColour(0x44, 0x79, 0xFB), FromDIP(2))); dc.SetBrush(*wxTRANSPARENT_BRUSH); - dc.DrawRoundedRectangle(FromDIP(1), FromDIP(1), MATERIAL_ITEM_SIZE.x - FromDIP(1), MATERIAL_ITEM_SIZE.y - FromDIP(1), 5); + dc.DrawRoundedRectangle(FromDIP(1), FromDIP(1), size.x - FromDIP(1), size.y - FromDIP(1), 5); } #endif @@ -704,7 +752,9 @@ AmsMapingPopup::AmsMapingPopup(wxWindow *parent, bool use_in_sync_dialog) : m_right_tips->SetBackgroundColour(StateColor::darkModeColorFor("0xFFFFFF")); m_right_tips->SetFont(::Label::Body_13); m_right_tips->SetLabel(m_right_tip_text); + m_sizer_ams_right_horizonal->Add(m_right_tips, 0, wxEXPAND , 0); + m_sizer_ams_right_horizonal->AddStretchSpacer(); m_reset_btn = new ScalableButton(m_right_first_text_panel, wxID_ANY, wxGetApp().dark_mode() ? "erase_dark" : "erase", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, true, 14); @@ -718,16 +768,34 @@ AmsMapingPopup::AmsMapingPopup(wxWindow *parent, bool use_in_sync_dialog) : m_sizer_ams_right_horizonal->Add(m_reset_btn, 0, wxALIGN_TOP | wxEXPAND ); m_reset_btn->Hide(); m_right_first_text_panel->SetSizer(m_sizer_ams_right_horizonal); - const int same_height = 15; + const int same_height = 20; m_left_first_text_panel->SetMaxSize(wxSize(-1, FromDIP(same_height))); m_right_first_text_panel->SetMaxSize(wxSize(-1, FromDIP(same_height))); + // content_sizer + wxSizer *content_ams_sizer = new wxBoxSizer(wxVERTICAL); + m_right_split_ams_sizer = create_split_sizer(m_right_marea_panel, _L("Right AMS")); + content_ams_sizer->Add(m_right_split_ams_sizer, 0, wxEXPAND, 0); + content_ams_sizer->Add(m_sizer_ams_basket_right, 0, wxEXPAND | wxTOP, FromDIP(8)); + content_ams_sizer->Add(create_split_sizer(m_right_marea_panel, _L("External")), 0, wxEXPAND | wxTOP, FromDIP(8)); + content_ams_sizer->Add(m_right_extra_slot, 0, wxEXPAND | wxTOP, FromDIP(8)); + + m_rack_nozzle_select = new wgtDeviceNozzleRackSelect(m_right_marea_panel); + m_rack_nozzle_select->Bind(EVT_NOZZLE_RACK_ITEM_CLICKED, &AmsMapingPopup::OnNozzleMappingSelected, this); + m_rack_nozzle_select->Show(false); + + wxSizer *content_sizer = new wxBoxSizer(wxHORIZONTAL); + content_sizer->Add(content_ams_sizer, 1, wxEXPAND | wxLEFT); + content_sizer->Add(m_rack_nozzle_select, 1, wxEXPAND | wxLEFT, FromDIP(25)); + + m_flush_warning_panel = new DevIconLabel(m_right_marea_panel, "dev_warning", ""); + m_flush_warning_panel->SetAllBackgroundColor(StateColor::darkModeColorFor("#FFFFE0")); + m_flush_warning_panel->GetLabelItem()->SetForegroundColour(StateColor::darkModeColorFor("#F09A17")); + m_flush_warning_panel->Show(false); + m_sizer_ams_right->Add(m_right_first_text_panel, 0, wxEXPAND | wxBOTTOM | wxTOP, FromDIP(8)); - m_right_split_ams_sizer = create_split_sizer(m_right_marea_panel, _L("Right BOX")); - m_sizer_ams_right->Add(m_right_split_ams_sizer, 0, wxEXPAND, 0); - m_sizer_ams_right->Add(m_sizer_ams_basket_right, 0, wxEXPAND|wxTOP, FromDIP(8)); - m_sizer_ams_right->Add(create_split_sizer(m_right_marea_panel, _L("External")), 0, wxEXPAND|wxTOP, FromDIP(8)); - m_sizer_ams_right->Add(m_right_extra_slot, 0, wxEXPAND|wxTOP, FromDIP(8)); + m_sizer_ams_right->Add(m_flush_warning_panel, 0, wxBOTTOM | wxALIGN_LEFT, FromDIP(8)); + m_sizer_ams_right->Add(content_sizer, 0, wxEXPAND, 0); m_left_marea_panel->SetSizer(m_sizer_ams_left); @@ -736,7 +804,7 @@ AmsMapingPopup::AmsMapingPopup(wxWindow *parent, bool use_in_sync_dialog) : //m_sizer_ams->Add(m_left_marea_panel, 0, wxEXPAND, FromDIP(0)); m_sizer_ams->Add(m_left_marea_panel, 0, wxRIGHT, FromDIP(10)); m_sizer_ams->Add(0, 0, 0, wxEXPAND, FromDIP(15)); - m_sizer_ams->Add(m_right_marea_panel, 1, wxEXPAND, FromDIP(0)); + m_sizer_ams->Add(m_right_marea_panel, 0, wxEXPAND, FromDIP(0)); m_sizer_main->Add(title_panel, 0, wxEXPAND | wxALL, FromDIP(2)); m_sizer_main->Add(m_sizer_ams, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(14)); @@ -748,8 +816,20 @@ AmsMapingPopup::AmsMapingPopup(wxWindow *parent, bool use_in_sync_dialog) : Bind(wxEVT_SHOW, [this](wxShowEvent& e) { if (e.IsShown() && m_parent_item) { - wxPoint pos = m_parent_item->ClientToScreen(wxPoint(0, 0)); - pos.y += m_parent_item->GetRect().height; + + // Position below the parent item by default + auto pos = m_parent_item->ClientToScreen(wxPoint(0, 0)); + auto parent_size = m_parent_item->GetRect(); + pos.y += parent_size.height; + + // If there's not enough space above, align to the top of the screen + auto screenSize = wxGetDisplaySize(); + auto popupSize = GetBestSize(); + if (screenSize.y - pos.y < popupSize.y) { + pos.y -= parent_size.height; + pos.y -= popupSize.y; + } + this->Move(pos); } }); @@ -781,6 +861,9 @@ void AmsMapingPopup::msw_rescale() for (auto item : m_mapping_item_list) { item->msw_rescale(); } for (auto container : m_amsmapping_container_list) { container->msw_rescale(); } + m_flush_warning_panel->Rescale(); + m_rack_nozzle_select->Rescale(); + Fit(); Refresh(); }; @@ -828,7 +911,14 @@ void AmsMapingPopup::set_tag_texture(std::string texture) bool AmsMapingPopup::is_match_material(std::string material) const { - return m_tag_material == material ? true : false; + //y75 + auto toLower = [](std::string s) -> std::string{ + std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); }); + return s; + }; + std::string m_tag_material_to_lowser = toLower(m_tag_material); + std::string material_to_lower = toLower(material); + return m_tag_material_to_lowser == material_to_lower ? true : false; } @@ -1056,6 +1146,10 @@ void AmsMapingPopup::update(MachineObject* obj, const std::vector& /*title*/ update_title(obj); + /*rack*/ + update_rack_select(obj); + update_flush_waste(obj); + if (wxGetApp().dark_mode() && m_reset_btn->GetName() != "erase_dark") { m_reset_btn->SetName("erase_dark"); m_reset_btn->SetBitmap(ScalableBitmap(m_right_first_text_panel, "erase_dark", 14).bmp()); @@ -1448,6 +1542,61 @@ void AmsMapingPopup::paintEvent(wxPaintEvent &evt) dc.DrawRoundedRectangle(0, 0, GetSize().x, GetSize().y, 0); } +void AmsMapingPopup::update_rack_select(MachineObject *obj) +{ + //y75 + if (obj->get_agent() == nullptr) + return; + + m_rack = obj ? obj->GetNozzleRack() : nullptr; + + bool show_rack_select_area = false; + if (!m_mapping_from_multi_machines && !m_use_in_sync_dialog && + obj && obj->GetNozzleRack()->IsSupported() && !obj->get_nozzle_mapping_result().GetNozzleMapping().empty()) { + int mapped_nozzle_pos_id = obj->get_nozzle_mapping_result().GetMappedNozzlePosIdByFilaId(obj, m_current_filament_id); + m_rack_nozzle_select->UpdateRackSelect(obj->GetNozzleRack(), mapped_nozzle_pos_id); + + show_rack_select_area = true; + } + + if (show_rack_select_area != m_rack_nozzle_select->IsShown()) { + m_right_tip_text = show_rack_select_area ? _L("Select Filament && Hotends") : _L("Select Filament"); + m_right_tips->SetLabel(m_right_tip_text); + m_rack_nozzle_select->Show(show_rack_select_area); + Layout(); + Fit(); + } +} + +void AmsMapingPopup::OnNozzleMappingSelected(wxCommandEvent& evt) +{ + if (auto ptr = m_rack.lock()) { + MachineObject* obj = ptr->GetNozzleSystem()->GetOwner(); + obj->set_manual_nozzle_mapping(m_current_filament_id, m_rack_nozzle_select->GetSelectedNozzlePosID()); + update_flush_waste(obj); + } + + evt.Skip(); + Dismiss(); +} + +void AmsMapingPopup::update_flush_waste(MachineObject* obj) +{ + if (!obj) { + m_flush_warning_panel->Hide(); + return; + }; + + float flush_waste_base = obj->get_nozzle_mapping_result().GetFlushWeightBase(); + float flush_waste_current = obj->get_nozzle_mapping_result().GetFlushWeightCurrent(); + if ((flush_waste_base != -1) && (flush_waste_current != -1) && flush_waste_current > flush_waste_base){ + m_flush_warning_panel->SetLabel(wxString::Format(_L("Printing with the current nozzle may produce an extra %0.2f g of waste."), flush_waste_current - flush_waste_base)); + m_flush_warning_panel->Show(); + } else { + m_flush_warning_panel->Hide(); + } +} + MappingItem::MappingItem(wxWindow *parent) : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize) { @@ -1566,7 +1715,7 @@ void MappingItem::render(wxDC &dc) top += 0.5 * mapping_item_checked.GetBmpHeight(); // materials name - dc.SetFont(::Label::Head_13); + dc.SetFont(::Label::Head_12); auto txt_colour = m_coloul.GetLuminance() < 0.6 ? *wxWHITE : wxColour(0x26, 0x2E, 0x30); @@ -1575,11 +1724,12 @@ void MappingItem::render(wxDC &dc) if (m_coloul.Alpha() == 0) txt_colour = wxColour(0x26, 0x2E, 0x30); dc.SetTextForeground(txt_colour); - + //show a1 auto txt_size = dc.GetTextExtent(m_tray_index); top += FromDIP(2); dc.DrawText(m_tray_index, wxPoint((GetSize().x - txt_size.x) / 2, top)); + //show materials name top += txt_size.y + FromDIP(2); m_name.size() > 4 ? dc.SetFont(::Label::Body_9) : dc.SetFont(::Label::Body_12); if(m_name.size() > 5){ @@ -2800,4 +2950,57 @@ void AmsHumidityLevelList::doRender(wxDC& dc) } } -}} // namespace Slic3r::GUI +DevIconLabel::DevIconLabel(wxWindow* parent, const wxString& icon, const wxString& label) + : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize) +{ + CreateGui(); + + SetIcon(icon); + SetLabel(label); +} + +void DevIconLabel::CreateGui() +{ + wxSizer* sizer_main = new wxBoxSizer(wxHORIZONTAL); + m_icon = new wxStaticBitmap(this, wxID_ANY, wxNullBitmap); + m_icon->SetBackgroundColour(*wxWHITE); + + m_label = new Label(this); + m_label->SetFont(::Label::Body_13); + m_label->SetBackgroundColour(*wxWHITE); + + sizer_main->Add(m_icon, 0, wxALIGN_CENTER_VERTICAL, 0); + sizer_main->Add(m_label, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(8)); + SetSizer(sizer_main); + Layout(); + Fit(); +} + +void DevIconLabel::SetLabel(const wxString& label) +{ + m_label->SetLabel(label); +} + +void DevIconLabel::SetIcon(const wxString& icon) +{ + if (m_icon_str != icon) { + m_icon_str = icon; + m_icon->SetBitmap(create_scaled_bitmap(icon.ToStdString(), this, 20)); + Refresh(); + } +} + +void DevIconLabel::SetAllBackgroundColor(const wxColour& color) +{ + SetBackgroundColour(color); + m_icon->SetBackgroundColour(color); + m_label->SetBackgroundColour(color); +} + +void Slic3r::GUI::DevIconLabel::Rescale() +{ + m_icon->SetBitmap(create_scaled_bitmap(m_icon_str.ToStdString(), this, 20)); + Refresh(); +} + +}} // namespace Slic3r::GUI \ No newline at end of file diff --git a/src/slic3r/GUI/AmsMappingPopup.hpp b/src/slic3r/GUI/AmsMappingPopup.hpp index 5970458..a3eb497 100644 --- a/src/slic3r/GUI/AmsMappingPopup.hpp +++ b/src/slic3r/GUI/AmsMappingPopup.hpp @@ -42,7 +42,18 @@ #define MAPPING_ITEM_INVALID_REMAIN -1 -namespace Slic3r { namespace GUI { +// Previous definitions +namespace Slic3r +{ +class DevNozzleRack; +namespace GUI +{ +class wgtDeviceNozzleRackSelect; +}; +}; + +namespace Slic3r { +namespace GUI { #define AMS_TOTAL_COUNT 4 @@ -77,23 +88,48 @@ struct TrayData class MaterialItem: public wxPanel { protected: - int m_text_pos_x = 0; int m_text_pos_y = -1; bool m_dropdown_allow_painted = true; + wxString m_mapping_text; public: - MaterialItem(wxWindow *parent, wxColour mcolour, wxString mname); + MaterialItem(wxWindow *parent, wxColour mcolour, wxString mname, std::string filament_id); ~MaterialItem(); - wxPanel* m_main_panel; + void allow_paint_dropdown(bool flag); + + void set_ams_info(wxColour col, wxString txt, + int ctype = 0, std::vector cols = std::vector(), + bool record_back_info = false); + void reset_ams_info(); + virtual void reset_valid_info(); + + void set_nozzle_info(const wxString& mapped_nozzle_str); + + void disable(); + void enable(); + void on_normal(); + void on_selected(); + void on_warning(); + + void msw_rescale(); + +protected: + void messure_size(); + +public: + std::string m_filament_id; + wxColour m_material_coloul; wxString m_material_name; + wxString m_mapped_nozzle_str; //info wxColour m_ams_coloul; wxString m_ams_name; int m_ams_ctype = 0; std::vector m_ams_cols = std::vector(); + //reset wxColour m_back_ams_coloul; wxString m_back_ams_name; @@ -106,34 +142,23 @@ public: ScalableBitmap m_filament_wheel_transparent; ScalableBitmap m_ams_wheel_mitem; ScalableBitmap m_ams_not_match; + ScalableBitmap m_rack_nozzle_bitmap; bool m_selected {false}; bool m_warning{false}; bool m_match {true}; bool m_enable {true}; - void msw_rescale(); - void allow_paint_dropdown(bool flag); - void set_ams_info(wxColour col, wxString txt, int ctype=0, std::vector cols= std::vector(),bool record_back_info = false); - void reset_ams_info(); - - void disable(); - void enable(); - void on_normal(); - void on_selected(); - void on_warning(); - void paintEvent(wxPaintEvent &evt); virtual void render(wxDC &dc); void match(bool mat); virtual void doRender(wxDC &dc); - virtual void reset_valid_info(); }; class MaterialSyncItem : public MaterialItem { public: - MaterialSyncItem(wxWindow *parent, wxColour mcolour, wxString mname); + MaterialSyncItem(wxWindow *parent, wxColour mcolour, wxString mname, std::string filament_id); ~MaterialSyncItem(); int get_real_offset(); void render(wxDC &dc) override; @@ -206,6 +231,29 @@ protected: void doRender(wxDC& dc); }; +class DevIconLabel : public wxPanel +{ +public: + DevIconLabel(wxWindow* parent, const wxString& icon, const wxString& label); + +public: + void SetAllBackgroundColor(const wxColour& color); + + Label* GetLabelItem() const { return m_label; } + void SetLabel(const wxString& label); + void SetIcon(const wxString& icon); + + void Rescale(); + +private: + void CreateGui(); + +private: + Label* m_label{ nullptr }; + wxString m_icon_str; + wxStaticBitmap* m_icon{ nullptr }; +}; + class AmsMapingPopup : public PopupWindow { bool m_use_in_sync_dialog = false; @@ -213,6 +261,9 @@ class AmsMapingPopup : public PopupWindow bool m_ext_mapping_filatype_check = true; wxStaticText* m_title_text{ nullptr }; + wgtDeviceNozzleRackSelect *m_rack_nozzle_select{nullptr}; + DevIconLabel* m_flush_warning_panel; + public: AmsMapingPopup(wxWindow *parent,bool use_in_sync_dialog = false); ~AmsMapingPopup() {}; @@ -258,6 +309,7 @@ public: wxBoxSizer* m_sizer_split_ams_right; bool m_mapping_from_multi_machines {false}; + bool get_use_in_sync_dialog() { return m_use_in_sync_dialog; } void set_sizer_title(wxBoxSizer *sizer, wxString text); wxBoxSizer* create_split_sizer(wxWindow* parent, wxString text); void set_send_win(wxWindow* win) {send_win = win;}; @@ -265,6 +317,7 @@ public: void set_tag_texture(std::string texture); void update(MachineObject* obj, const std::vector& ams_mapping_result); void update_title(MachineObject* obj); + void update_rack_select(MachineObject* obj); void update_items_check_state(const std::vector& ams_mapping_result); void update_ams_data_multi_machines(); void add_ams_mapping(std::vector tray_data, bool remain_detect_flag, wxWindow *container, wxBoxSizer *sizer); @@ -294,6 +347,13 @@ public: void EnableExtMappingFilaTypeCheck(bool to_check = true) { m_ext_mapping_filatype_check = to_check;} ; private: + // events + void OnNozzleMappingSelected(wxCommandEvent& evt); + void update_flush_waste(MachineObject* obj); + +private: + std::weak_ptr m_rack; + ResetCallback m_reset_callback{nullptr}; std::string m_material_index; bool m_only_show_ext_spool{false}; diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 03fd2ae..586b344 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -260,9 +260,13 @@ void BackgroundSlicingProcess::process_fff() BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" %1%: gcode_result reseted, will start print::process")%__LINE__; m_print->process(); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" %1%: after print::process, send slicing complete event to gui...")%__LINE__; - if (m_current_plate->get_real_filament_map_mode(preset_bundle.project_config) < FilamentMapMode::fmmManual) { - std::vector f_maps = m_fff_print->get_filament_maps(); - m_current_plate->set_filament_maps(f_maps); + if (m_current_plate->get_real_filament_map_mode(preset_bundle.project_config) < FilamentMapMode::fmmManual) { + m_current_plate->set_filament_maps(m_fff_print->get_filament_maps()); + m_current_plate->set_filament_volume_maps(m_fff_print->get_filament_volume_maps()); + } + if(m_current_plate->get_real_filament_map_mode(preset_bundle.project_config) != FilamentMapMode::fmmNozzleManual){ + std::vector f_nozzle_maps = m_fff_print->get_filament_nozzle_maps(); + m_current_plate->set_filament_nozzle_maps(f_nozzle_maps); } wxCommandEvent evt(m_event_slicing_completed_id); diff --git a/src/slic3r/GUI/CaliHistoryDialog.cpp b/src/slic3r/GUI/CaliHistoryDialog.cpp index 7cc115f..0ce1375 100644 --- a/src/slic3r/GUI/CaliHistoryDialog.cpp +++ b/src/slic3r/GUI/CaliHistoryDialog.cpp @@ -10,19 +10,22 @@ #include "DeviceCore/DevExtruderSystem.h" #include "DeviceCore/DevManager.h" +#include "DeviceCore/DevNozzleRack.h" namespace Slic3r { namespace GUI { -#define HISTORY_WINDOW_SIZE wxSize(FromDIP(700), FromDIP(600)) +#define HISTORY_WINDOW_SIZE wxSize(FromDIP(740), FromDIP(600)) #define EDIT_HISTORY_DIALOG_INPUT_SIZE wxSize(FromDIP(160), FromDIP(24)) #define NEW_HISTORY_DIALOG_INPUT_SIZE wxSize(FromDIP(250), FromDIP(24)) #define HISTORY_WINDOW_ITEMS_COUNT 6 +#define MAX_HISTORY_ITEMS_NUM 100 enum CaliColumnType : int { Cali_Name = 0, Cali_Filament, + Cali_Nozzle_ID, Cali_Nozzle, Cali_K_Value, Cali_Delete, @@ -32,28 +35,26 @@ enum CaliColumnType : int { bool support_nozzle_volume(const MachineObject* obj) { - if (!obj) + if (!obj) return false; + + if(DevPrinterConfigUtil::support_disable_cali_flow_type(obj->printer_type)) return false; - Preset * machine_preset = get_printer_preset(obj); - if (machine_preset) { - int extruder_nums = machine_preset->config.option("nozzle_diameter")->values.size(); - auto nozzle_volume_opt = machine_preset->config.option("nozzle_volume"); - if (nozzle_volume_opt) { - int printer_variant_size = nozzle_volume_opt->values.size(); - return (printer_variant_size / extruder_nums) > 1; - } - } + + if (obj->is_nozzle_flow_type_supported()) + return true; + return false; } -int get_colume_idx(CaliColumnType type, MachineObject* obj) -{ - if (!support_nozzle_volume(obj) - && (type > CaliColumnType::Cali_Nozzle)) { - return type - 1; +wxString nozzle_id_code_to_string(int code){ + if(code == 0){ + return "R"; + } else if(code >= 0x10){ + return wxString::Format("%d", code - 0x10 +1); + } else { + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << "Nozzle position id is -1 or invalid."; + return "N/A"; } - - return type; } static wxString get_preset_name_by_filament_id(std::string filament_id) @@ -327,6 +328,8 @@ void HistoryWindow::enbale_action_buttons(bool enable) { } void HistoryWindow::sync_history_data() { + int column_idx = 0; + Freeze(); m_history_data_panel->DestroyChildren(); m_history_data_panel->Enable(); @@ -339,22 +342,33 @@ void HistoryWindow::sync_history_data() { auto title_name = new Label(m_history_data_panel, _L("Name")); title_name->SetFont(Label::Head_14); - gbSizer->Add(title_name, {0, get_colume_idx(CaliColumnType::Cali_Name, curr_obj) }, {1, 1}, wxBOTTOM, FromDIP(15)); + gbSizer->Add(title_name, {0, column_idx++ }, {1, 1}, wxBOTTOM, FromDIP(15)); BOOST_LOG_TRIVIAL(info) << "=====================" << title_name->GetLabelText().ToStdString(); auto title_preset_name = new Label(m_history_data_panel, _L("Filament")); title_preset_name->SetFont(Label::Head_14); - gbSizer->Add(title_preset_name, { 0, get_colume_idx(CaliColumnType::Cali_Filament, curr_obj) }, { 1, 1 }, wxBOTTOM, FromDIP(15)); + gbSizer->Add(title_preset_name, { 0, column_idx++ }, { 1, 1 }, wxBOTTOM, FromDIP(15)); + + auto rack = curr_obj->GetNozzleSystem()->GetNozzleRack(); + if(rack->IsSupported() && get_extruder_id() == MAIN_EXTRUDER_ID) { + auto nozzle_name = new Label(m_history_data_panel, _L("Nozzle ID")); + nozzle_name->SetFont(Label::Head_14); + auto font = nozzle_name->GetFont(); + font.SetUnderlined(true); + nozzle_name->SetFont(font); + nozzle_name->SetToolTip(_L("Note: The hotend number on the right extruder is tied to the holder. When the hotend is moved to a new holder, its number will update automatically.")); + gbSizer->Add(nozzle_name, {0, column_idx++}, {1, 1}, wxBOTTOM, FromDIP(15)); + } if (support_nozzle_volume(curr_obj)) { auto nozzle_name = new Label(m_history_data_panel, _L("Nozzle Flow")); nozzle_name->SetFont(Label::Head_14); - gbSizer->Add(nozzle_name, {0, get_colume_idx(CaliColumnType::Cali_Nozzle, curr_obj)}, {1, 1}, wxBOTTOM, FromDIP(15)); + gbSizer->Add(nozzle_name, {0, column_idx++}, {1, 1}, wxBOTTOM, FromDIP(15)); } auto title_k = new Label(m_history_data_panel, _L("Factor K")); title_k->SetFont(Label::Head_14); - gbSizer->Add(title_k, { 0, get_colume_idx(CaliColumnType::Cali_K_Value,curr_obj) }, { 1, 1 }, wxBOTTOM, FromDIP(15)); + gbSizer->Add(title_k, { 0, column_idx++ }, { 1, 1 }, wxBOTTOM, FromDIP(15)); // Hide //auto title_n = new Label(m_history_data_panel, wxID_ANY, _L("N")); @@ -363,7 +377,7 @@ void HistoryWindow::sync_history_data() { auto title_action = new Label(m_history_data_panel, _L("Action")); title_action->SetFont(Label::Head_14); - gbSizer->Add(title_action, {0, get_colume_idx(CaliColumnType::Cali_Delete, curr_obj)}, {1, 1}); + gbSizer->Add(title_action, {0, column_idx++}, {1, 1}); auto to_lower_case = [](const std::string &str) { std::string lowerStr = str; @@ -378,6 +392,12 @@ void HistoryWindow::sync_history_data() { std::string right_str = to_lower_case(right.name); return left_str < right_str ? true : left_str > right_str ? false : (left_str < right_str); }); + + // grid item num in row + int column_count = column_idx + 1; + // reset colume_idx + column_idx = 0; + int i = 1; for (auto& result : m_calib_results_history) { auto name_value = new Label(m_history_data_panel, from_u8(result.name)); @@ -394,13 +414,14 @@ void HistoryWindow::sync_history_data() { delete_button->SetBackgroundColour(*wxWHITE); delete_button->SetMinSize(wxSize(-1, FromDIP(24))); delete_button->SetCornerRadius(FromDIP(12)); - delete_button->Bind(wxEVT_BUTTON, [this, gbSizer, i, &result](auto& e) { + delete_button->Bind(wxEVT_BUTTON, [this, gbSizer, i, &result, column_count](auto& e) { if (m_ui_op_lock) { return; } else { m_ui_op_lock = true; } - for (int j = 0; j < HISTORY_WINDOW_ITEMS_COUNT; j++) { + + for (int j = 0; j < column_count; j++) { auto item = gbSizer->FindItemAtPosition({ i, j }); if (item && item->GetWindow()) item->GetWindow()->Hide(); @@ -413,6 +434,8 @@ void HistoryWindow::sync_history_data() { cali_info.cali_idx = result.cali_idx; cali_info.nozzle_diameter = result.nozzle_diameter; cali_info.filament_id = result.filament_id; + cali_info.nozzle_pos_id = result.nozzle_pos_id; + cali_info.nozzle_sn = result.nozzle_sn; CalibUtils::delete_PA_calib_result(cali_info); }); @@ -447,17 +470,27 @@ void HistoryWindow::sync_history_data() { } }); - gbSizer->Add(name_value, {i, get_colume_idx(CaliColumnType::Cali_Name, curr_obj)}, {1, 1}, wxBOTTOM, FromDIP(15)); - gbSizer->Add(preset_name_value, {i, get_colume_idx(CaliColumnType::Cali_Filament, curr_obj)}, {1, 1}, wxBOTTOM, FromDIP(15)); + gbSizer->Add(name_value, {i, column_idx++}, {1, 1}, wxBOTTOM, FromDIP(15)); + gbSizer->Add(preset_name_value, {i, column_idx++}, {1, 1}, wxBOTTOM, FromDIP(15)); + + auto rack = curr_obj->GetNozzleSystem()->GetNozzleRack(); + if(rack->IsSupported() && get_extruder_id() == MAIN_EXTRUDER_ID){ + // Nozzle ID + wxString nozzle_id = nozzle_id_code_to_string(result.nozzle_pos_id); + auto nozzle_id_label = new Label(m_history_data_panel, nozzle_id); + gbSizer->Add(nozzle_id_label, {i, column_idx++}, {1, 1}, wxBOTTOM, FromDIP(15)); + } + if (support_nozzle_volume(curr_obj)) { wxString nozzle_name = get_nozzle_volume_type_name(result.nozzle_volume_type); auto nozzle_name_label = new Label(m_history_data_panel, nozzle_name); - gbSizer->Add(nozzle_name_label, {i, get_colume_idx(CaliColumnType::Cali_Nozzle, curr_obj)}, {1, 1}, wxBOTTOM, FromDIP(15)); + gbSizer->Add(nozzle_name_label, {i, column_idx++}, {1, 1}, wxBOTTOM, FromDIP(15)); } - gbSizer->Add(k_value, {i, get_colume_idx(CaliColumnType::Cali_K_Value, curr_obj)}, {1, 1}, wxBOTTOM, FromDIP(15)); + gbSizer->Add(k_value, {i, column_idx++}, {1, 1}, wxBOTTOM, FromDIP(15)); //gbSizer->Add(n_value, { i, 3 }, { 1, 1 }, wxBOTTOM, FromDIP(15)); - gbSizer->Add(delete_button, {i, get_colume_idx(CaliColumnType::Cali_Delete, curr_obj)}, {1, 1}, wxBOTTOM, FromDIP(15)); - gbSizer->Add(edit_button, {i, get_colume_idx(CaliColumnType::Cali_Edit, curr_obj)}, {1, 1}, wxBOTTOM, FromDIP(15)); + gbSizer->Add(delete_button, {i, column_idx++}, {1, 1}, wxBOTTOM, FromDIP(15)); + gbSizer->Add(edit_button, {i, column_idx++}, {1, 1}, wxBOTTOM, FromDIP(15)); + column_idx = 0; i++; m_ui_op_lock = false; } @@ -559,8 +592,18 @@ EditCalibrationHistoryDialog::EditCalibrationHistoryDialog(wxWindow flex_sizer->Add(extruder_name_value); } + // Nozzle ID + auto rack = obj->GetNozzleSystem()->GetNozzleRack(); + if(rack->IsSupported() && result.extruder_id == MAIN_EXTRUDER_ID) { + Label* nozzle_id_title = new Label(top_panel, _L("Nozzle ID")); + wxString nozzle_id = nozzle_id_code_to_string(result.nozzle_pos_id); + Label* nozzle_id_value = new Label(top_panel, nozzle_id); + flex_sizer->Add(nozzle_id_title); + flex_sizer->Add(nozzle_id_value); + } + if (support_nozzle_volume(curr_obj)) { - Label *nozzle_name_title = new Label(top_panel, _L("Nozzle")); + Label *nozzle_name_title = new Label(top_panel, _L("Nozzle Flow")); wxString nozzle_name; const ConfigOptionDef *nozzle_volume_type_def = print_config_def.get("nozzle_volume_type"); if (nozzle_volume_type_def && nozzle_volume_type_def->enum_keys_map) { @@ -792,6 +835,21 @@ NewCalibrationHistoryDialog::NewCalibrationHistoryDialog(wxWindow *parent, const flex_sizer->Add(name_title); flex_sizer->Add(m_name_value); + if (curr_obj->is_multi_extruders()) + { + Label *extruder_name_title = new Label(top_panel, _L("Extruder")); + m_comboBox_extruder = new ::ComboBox(top_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, NEW_HISTORY_DIALOG_INPUT_SIZE, 0, nullptr, wxCB_READONLY); + wxArrayString extruder_items; + extruder_items.push_back(_L("Left")); + extruder_items.push_back(_L("Right")); + m_comboBox_extruder->Set(extruder_items); + m_comboBox_extruder->SetSelection(-1); + m_comboBox_extruder->Bind(wxEVT_COMMAND_COMBOBOX_SELECTED, &NewCalibrationHistoryDialog::on_select_extruder, this); + + flex_sizer->Add(extruder_name_title); + flex_sizer->Add(m_comboBox_extruder); + } + Label * preset_name_title = new Label(top_panel, _L("Filament")); m_comboBox_filament = new ::ComboBox(top_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, NEW_HISTORY_DIALOG_INPUT_SIZE, 0, nullptr, wxCB_READONLY); @@ -803,28 +861,41 @@ NewCalibrationHistoryDialog::NewCalibrationHistoryDialog(wxWindow *parent, const flex_sizer->Add(preset_name_title); flex_sizer->Add(m_comboBox_filament); - if (curr_obj->is_multi_extruders()) - { - Label *extruder_name_title = new Label(top_panel, _L("Extruder")); - m_comboBox_extruder = new ::ComboBox(top_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, NEW_HISTORY_DIALOG_INPUT_SIZE, 0, nullptr, wxCB_READONLY); - wxArrayString extruder_items; - extruder_items.push_back(_L("Left")); - extruder_items.push_back(_L("Right")); - m_comboBox_extruder->Set(extruder_items); - m_comboBox_extruder->SetSelection(-1); - flex_sizer->Add(extruder_name_title); - flex_sizer->Add(m_comboBox_extruder); + // Nozzle ID + auto rack = curr_obj->GetNozzleSystem()->GetNozzleRack(); + if(rack->IsSupported()){ + Label *nozzle_id_title = new Label(top_panel, _L("Nozzle ID")); + m_comboBox_nozzle_id = new ::ComboBox(top_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, NEW_HISTORY_DIALOG_INPUT_SIZE, 0, nullptr, wxCB_READONLY); + m_comboBox_nozzle_id->Bind(wxEVT_COMMAND_COMBOBOX_SELECTED, &NewCalibrationHistoryDialog::on_select_nozzle_pos, this); + + int r_nozzle_id = obj->GetExtderSystem()->GetExtderById(MAIN_EXTRUDER_ID)->GetNozzleId(); + auto r_nozzle = obj->GetNozzleSystem()->GetExtNozzle(r_nozzle_id); + + if(r_nozzle.IsNormal()){ + m_comboBox_nozzle_id->Append("R", wxNullBitmap, new int{0}); + } + for (auto nozzle_item : rack->GetRackNozzles()) { + if(nozzle_item.second.IsNormal()){ + m_comboBox_nozzle_id->Append(wxString::Format("%d", nozzle_item.first + 1), wxNullBitmap, new int{0x10 | nozzle_item.first}); + } + } + + m_comboBox_nozzle_id->SetSelection(-1); + m_comboBox_nozzle_id->Disable(); + flex_sizer->Add(nozzle_id_title); + flex_sizer->Add(m_comboBox_nozzle_id); } if (support_nozzle_volume(curr_obj)) { - Label *nozzle_name_title = new Label(top_panel, _L("Nozzle")); + Label *nozzle_name_title = new Label(top_panel, _L("Nozzle Flow")); m_comboBox_nozzle_type = new ::ComboBox(top_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, NEW_HISTORY_DIALOG_INPUT_SIZE, 0, nullptr, wxCB_READONLY); - wxArrayString nozzle_items; const ConfigOptionDef *nozzle_volume_type_def = print_config_def.get("nozzle_volume_type"); if (nozzle_volume_type_def && nozzle_volume_type_def->enum_keys_map) { - for (auto item : nozzle_volume_type_def->enum_labels) { nozzle_items.push_back(_L(item)); } + for (auto item : nozzle_volume_type_def->enum_labels) { + if (item == "Hybrid") continue; + m_comboBox_nozzle_type->Append(_L(item), wxNullBitmap, new int{to_nozzle_flow_type(item)}); + } } - m_comboBox_nozzle_type->Set(nozzle_items); m_comboBox_nozzle_type->SetSelection(-1); flex_sizer->Add(nozzle_name_title); flex_sizer->Add(m_comboBox_nozzle_type); @@ -832,10 +903,15 @@ NewCalibrationHistoryDialog::NewCalibrationHistoryDialog(wxWindow *parent, const Label *nozzle_diameter_title = new Label(top_panel, _L("Nozzle Diameter")); m_comboBox_nozzle_diameter = new ::ComboBox(top_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, NEW_HISTORY_DIALOG_INPUT_SIZE, 0, nullptr, wxCB_READONLY); - static std::array nozzle_diameter_list = {0.2f, 0.4f, 0.6f, 0.8f}; - for (int i = 0; i < nozzle_diameter_list.size(); i++) { - m_comboBox_nozzle_diameter->AppendString(wxString::Format("%1.1f mm", nozzle_diameter_list[i])); - if (abs(obj->GetExtderSystem()->GetNozzleDiameter(0) - nozzle_diameter_list[i]) < 1e-3) { + + static std::array nozzle_diameter_list = {NozzleDiameterType::NOZZLE_DIAMETER_0_2, NozzleDiameterType::NOZZLE_DIAMETER_0_4, + NozzleDiameterType::NOZZLE_DIAMETER_0_6, NozzleDiameterType::NOZZLE_DIAMETER_0_8}; + for (int i = 0; i < nozzle_diameter_list.size(); i++) + { + auto diameter = nozzle_diameter_list[i]; + m_comboBox_nozzle_diameter->Append(wxString::Format("%1.1f mm", to_nozzle_diameter_float(diameter)), wxNullBitmap, new int{diameter}); + + if(obj->GetExtderSystem()->GetNozzleDiameterType(0) == diameter){ m_comboBox_nozzle_diameter->SetSelection(i); } } @@ -888,6 +964,15 @@ NewCalibrationHistoryDialog::NewCalibrationHistoryDialog(wxWindow *parent, const wxGetApp().UpdateDlgDarkUI(this); } +int NewCalibrationHistoryDialog::get_nozzle_combo_id_code() const{ + if(!m_comboBox_nozzle_id) return -1; + + auto sel = m_comboBox_nozzle_id->GetSelection(); + if (sel != wxNOT_FOUND) return *(reinterpret_cast(m_comboBox_nozzle_id->GetClientData(sel))); + + return -1; +} + int NewCalibrationHistoryDialog::get_extruder_id(int extruder_index) { if ((extruder_index != -1) && curr_obj->is_multi_extruders()) { @@ -896,6 +981,57 @@ int NewCalibrationHistoryDialog::get_extruder_id(int extruder_index) return 0; } +void NewCalibrationHistoryDialog::on_select_extruder(wxCommandEvent &event) +{ + if (!m_comboBox_nozzle_id || !m_comboBox_nozzle_type) return; + + /* select right extruder */ + if (m_comboBox_extruder->GetSelection() == 1) + { + m_comboBox_nozzle_id->Enable(); + + m_comboBox_nozzle_type->Disable(); + m_comboBox_nozzle_diameter->Disable(); + } + else + { + m_comboBox_nozzle_id->Disable(); + m_comboBox_nozzle_id->SetValue(wxEmptyString); + + m_comboBox_nozzle_type->Enable(); + m_comboBox_nozzle_diameter->Enable(); + } +} + +void NewCalibrationHistoryDialog::on_select_nozzle_pos(wxCommandEvent &event) +{ + if (!curr_obj) return; + + auto sel = m_comboBox_nozzle_id->GetSelection(); + if (sel == wxNOT_FOUND) return; + + int pos = *(reinterpret_cast(m_comboBox_nozzle_id->GetClientData(sel))); + + auto nozzle = curr_obj->get_nozzle_by_id_code(pos); + if (nozzle.IsNormal()) { + auto nozzle_flow = nozzle.GetNozzleFlowType(); + m_comboBox_nozzle_type->SetSelection(-1); + for(unsigned int i=0; i < m_comboBox_nozzle_type->GetCount(); i++) { + if(nozzle_flow == NozzleFlowType(*(int*)m_comboBox_nozzle_type->GetClientData(i))) { + m_comboBox_nozzle_type->SetSelection(i); + } + } + + auto nozzle_diameter = nozzle.GetNozzleDiameterType(); + m_comboBox_nozzle_diameter->SetSelection(-1); + for(unsigned int i=0; i < m_comboBox_nozzle_diameter->GetCount(); i++) { + if(nozzle_diameter == NozzleDiameterType(*(int*)m_comboBox_nozzle_diameter->GetClientData(i))) { + m_comboBox_nozzle_diameter->SetSelection(i); + } + } + } +} + void NewCalibrationHistoryDialog::on_ok(wxCommandEvent &event) { wxString name = m_name_value->GetTextCtrl()->GetValue(); @@ -911,9 +1047,14 @@ void NewCalibrationHistoryDialog::on_ok(wxCommandEvent &event) wxString k_str = wxString::Format("%.3f", k); m_k_value->GetTextCtrl()->SetValue(k_str); - double nozzle_value = 0.0; - wxString nozzle_value_str = m_comboBox_nozzle_diameter->GetValue(); - nozzle_value_str.ToDouble(&nozzle_value); + auto sel = m_comboBox_nozzle_diameter->GetSelection(); + double nozzle_value = 0.0f; + if (sel != wxNOT_FOUND) { + nozzle_value = to_nozzle_diameter_float(NozzleDiameterType(*(int *) m_comboBox_nozzle_diameter->GetClientData(sel))); + } else { + nozzle_value = to_nozzle_diameter_float(NozzleDiameterType::NONE_DIAMETER_TYPE); + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << "invalid nozzle diameter type sel"; + } std::string filament_name = m_comboBox_filament->GetValue().ToStdString(); if (filament_name.empty()) { @@ -939,7 +1080,20 @@ void NewCalibrationHistoryDialog::on_ok(wxCommandEvent &event) msg_dlg.ShowModal(); return; } - m_new_result.nozzle_volume_type = NozzleVolumeType(m_comboBox_nozzle_type->GetSelection()); + auto sel = m_comboBox_nozzle_type->GetSelection(); + if (sel != wxNOT_FOUND) { + m_new_result.nozzle_volume_type = to_nozzle_volume_type(NozzleFlowType(*(int*)m_comboBox_nozzle_type->GetClientData(sel))); + } else { + m_new_result.nozzle_volume_type = NozzleVolumeType::nvtStandard; + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << "invalid nozzle flow type sel"; + } + } + + int nozzle_pos_id = get_nozzle_combo_id_code(); + std::string nozzle_sn; + if (nozzle_pos_id != -1) { + DevNozzle nozzle = curr_obj->get_nozzle_by_id_code(nozzle_pos_id); + nozzle_sn = nozzle.GetSerialNumber().ToStdString(); } auto filament_item = map_filament_items[m_comboBox_filament->GetValue().ToStdString()]; @@ -954,6 +1108,8 @@ void NewCalibrationHistoryDialog::on_ok(wxCommandEvent &event) m_new_result.nozzle_diameter = nozzle_value; m_new_result.filament_id = filament_id; m_new_result.setting_id = setting_id; + m_new_result.nozzle_pos_id = nozzle_pos_id; + m_new_result.nozzle_sn = nozzle_sn; // Check for duplicate names from history { diff --git a/src/slic3r/GUI/CaliHistoryDialog.hpp b/src/slic3r/GUI/CaliHistoryDialog.hpp index 8b3dcde..7a308e5 100644 --- a/src/slic3r/GUI/CaliHistoryDialog.hpp +++ b/src/slic3r/GUI/CaliHistoryDialog.hpp @@ -75,10 +75,12 @@ public: protected: virtual void on_ok(wxCommandEvent &event); virtual void on_cancel(wxCommandEvent &event); - + void on_select_extruder(wxCommandEvent &event); + void on_select_nozzle_pos(wxCommandEvent &event); wxArrayString get_all_filaments(const MachineObject *obj); int get_extruder_id(int extruder_index); // extruder_index 0 : left, 1 : right + int get_nozzle_combo_id_code() const; protected: PACalibResult m_new_result; @@ -92,6 +94,7 @@ protected: ComboBox *m_comboBox_filament; ComboBox *m_comboBox_extruder; ComboBox *m_comboBox_nozzle_type; + ComboBox *m_comboBox_nozzle_id{nullptr}; struct FilamentInfos { diff --git a/src/slic3r/GUI/Calibration.cpp b/src/slic3r/GUI/Calibration.cpp index d8ac680..5f2f7db 100644 --- a/src/slic3r/GUI/Calibration.cpp +++ b/src/slic3r/GUI/Calibration.cpp @@ -303,11 +303,15 @@ void CalibrationDialog::update_cali(MachineObject *obj) else { m_calibration_btn->Enable(); } + m_calibration_flow->DeleteAllItems(); + last_stage_list_info = obj->stage_list_info; + m_calibration_btn->SetLabel(_L("Start Calibration")); if (!m_checkbox_list["vibration"]->GetValue() && !m_checkbox_list["bed_leveling"]->GetValue() && !m_checkbox_list["xcam_cali"]->GetValue() && !m_checkbox_list["motor_noise"]->GetValue() && - !m_checkbox_list["nozzle_cali"]->GetValue() && !m_checkbox_list["bed_cali"]->GetValue()) + !m_checkbox_list["nozzle_cali"]->GetValue() && !m_checkbox_list["bed_cali"]->GetValue() && + !m_checkbox_list["clump_pos_cali"]->GetValue()) { m_calibration_btn->Disable(); m_calibration_btn->SetLabel(_L("No step selected")); @@ -316,6 +320,8 @@ void CalibrationDialog::update_cali(MachineObject *obj) m_calibration_btn->Enable(); } } + + } bool CalibrationDialog::is_stage_list_info_changed(MachineObject *obj) diff --git a/src/slic3r/GUI/CalibrationWizard.cpp b/src/slic3r/GUI/CalibrationWizard.cpp index e8cfadd..ffd4a40 100644 --- a/src/slic3r/GUI/CalibrationWizard.cpp +++ b/src/slic3r/GUI/CalibrationWizard.cpp @@ -8,6 +8,10 @@ #include "CaliHistoryDialog.hpp" #include "CalibUtils.hpp" #include "QDTUtil.hpp" + +#include "DeviceCore/DevInfo.h" +#include "DeviceCore/DevManager.h" + //w29 #include "MainFrame.hpp" diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 9069f16..cb790d6 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -262,16 +262,26 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con } //QDS: limite the max layer_herght - if (config->opt_float("layer_height") > 0.6 + EPSILON) - { + if (config->opt_float("layer_height") > 0.6 + EPSILON) { const wxString msg_text = _(L("Too large layer height.\nReset to 0.2")); - MessageDialog dialog(nullptr, msg_text, "", wxICON_WARNING | wxOK); - DynamicPrintConfig new_conf = *config; - is_msg_dlg_already_exist = true; - dialog.ShowModal(); - new_conf.set_key_value("layer_height", new ConfigOptionFloat(0.2)); - apply(config, &new_conf); - is_msg_dlg_already_exist = false; + if (wxGetApp().app_config->get("developer_mode") == "true") { + MessageDialog dialog(nullptr, msg_text, "", wxICON_WARNING | wxYES_NO); + is_msg_dlg_already_exist = true; + if (dialog.ShowModal() == wxID_YES) { + DynamicPrintConfig new_conf = *config; + new_conf.set_key_value("layer_height", new ConfigOptionFloat(0.2)); + apply(config, &new_conf); + } + is_msg_dlg_already_exist = false; + } else { + MessageDialog dialog(nullptr, msg_text, "", wxICON_WARNING | wxOK); + DynamicPrintConfig new_conf = *config; + is_msg_dlg_already_exist = true; + dialog.ShowModal(); + new_conf.set_key_value("layer_height", new ConfigOptionFloat(0.2)); + apply(config, &new_conf); + is_msg_dlg_already_exist = false; + } } //limit scarf start height @@ -390,6 +400,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con } double sparse_infill_density = config->option("sparse_infill_density")->value; + int fill_multiline = config->option("fill_multiline")->value; auto timelapse_type = config->opt_enum("timelapse_type"); DynamicPrintConfig *global_config = &wxGetApp().preset_bundle->prints.get_edited_preset().config; @@ -472,6 +483,15 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con is_msg_dlg_already_exist = false; } + if (is_global_config && (config->opt_int("wall_filament") || config->opt_int("sparse_infill_filament") + || config->opt_int("solid_infill_filament") )) { + DynamicPrintConfig new_conf = *config; + new_conf.set_key_value("wall_filament", new ConfigOptionInt(0)); + new_conf.set_key_value("sparse_infill_filament", new ConfigOptionInt(0)); + new_conf.set_key_value("solid_infill_filament", new ConfigOptionInt(0)); + apply(config, &new_conf); + } + //QDS //if (config->opt_enum("wall_generator") == PerimeterGeneratorType::Arachne && // config->opt_bool("enable_overhang_speed")) @@ -715,6 +735,20 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in // sparse_infill_filament uses the same logic as in Print::extruders() for (auto el : { "sparse_infill_pattern", "sparse_infill_anchor_max", "infill_combination", "minimum_sparse_infill_area", "sparse_infill_filament", "infill_shift_step", "infill_rotate_step", "symmetric_infill_y_axis"}) toggle_line(el, have_infill); + + // Determine if the selected infill pattern supports multiline infill. + InfillPattern pattern = config->opt_enum("sparse_infill_pattern"); + + bool support_multiline_infill = pattern == ipCubic || pattern == ipGrid || pattern == ipRectilinear || pattern == ipStars || pattern == ipAlignedRectilinear || + pattern == ipGyroid || pattern == ipHoneycomb || pattern == ipLightning || pattern == ip3DHoneycomb || + pattern == ipAdaptiveCubic || pattern == ipSupportCubic; + + toggle_line("fill_multiline", have_infill && support_multiline_infill); + if (support_multiline_infill == false) { + DynamicPrintConfig new_conf = *config; + new_conf.set_key_value("fill_multiline", new ConfigOptionInt(1)); + apply(config, &new_conf); + } // Only allow configuration of open anchors if the anchoring is enabled. bool has_infill_anchors = have_infill && config->option("sparse_infill_anchor_max")->value > 0; toggle_line("sparse_infill_anchor", has_infill_anchors); @@ -724,7 +758,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in bool is_locked_zig = config->option>("sparse_infill_pattern")->value == InfillPattern::ipLockedZag; toggle_line("infill_shift_step", is_cross_zag || is_locked_zig); - for (auto el : {"skeleton_infill_density", "skin_infill_density", "infill_lock_depth", "skin_infill_depth", "skin_infill_line_width", "skeleton_infill_line_width", "locked_skin_infill_pattern", "locked_skeleton_infill_pattern"}) + for (auto el : {"infill_instead_top_bottom_surfaces","skeleton_infill_density", "skin_infill_density", "infill_lock_depth", "skin_infill_depth", "skin_infill_line_width", "skeleton_infill_line_width", "locked_skin_infill_pattern", "locked_skeleton_infill_pattern"}) toggle_line(el, is_locked_zig); bool is_zig_zag = config->option>("sparse_infill_pattern")->value == InfillPattern::ipZigZag; @@ -880,6 +914,9 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in toggle_line("flush_into_objects", !is_global_config); toggle_line("print_flow_ratio", !is_global_config); + toggle_line("wall_filament", !is_global_config); + toggle_line("solid_infill_filament", !is_global_config); + toggle_line("sparse_infill_filament", !is_global_config); toggle_line("support_interface_not_for_body",config->opt_int("support_interface_filament")&&!config->opt_int("support_filament")); diff --git a/src/slic3r/GUI/CreatePresetsDialog.cpp b/src/slic3r/GUI/CreatePresetsDialog.cpp index 079abfa..5b64edf 100644 --- a/src/slic3r/GUI/CreatePresetsDialog.cpp +++ b/src/slic3r/GUI/CreatePresetsDialog.cpp @@ -98,6 +98,7 @@ static std::unordered_map nozzle_diameter_map = {{"0.2", 0.2 {"0.8", 0.8}, {"1.0", 1.0}, {"1.2", 1.2}}; static std::set cannot_input_key = {9, 10, 13, 33, 35, 36, 37, 38, 40, 41, 42, 44, 46, 47, 59, 60, 62, 63, 64, 92, 94, 95, 124, 126}; +static std::set cannot_input_key_for_filament = {9, 10, 13, 33, 35, 36, 37, 38, 40, 41, 42, 44, 46, 59, 60, 62, 63, 64, 92, 94, 95, 124, 126}; static std::set special_key = {'\n', '\t', '\r', '\v', '@', ';'}; @@ -225,7 +226,7 @@ static wxBoxSizer* create_checkbox(wxWindow* parent, Preset* preset, wxString& p ::CheckBox * checkbox = new ::CheckBox(parent); sizer->Add(checkbox, 0, 0, 0); preset_checkbox.push_back(std::make_pair(checkbox, preset)); - wxStaticText *preset_name_str = new wxStaticText(parent, wxID_ANY, preset_name); + wxStaticText *preset_name_str = new wxStaticText(parent, wxID_ANY, preset_name, wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END); wxToolTip * toolTip = new wxToolTip(preset_name); preset_name_str->SetToolTip(toolTip); sizer->Add(preset_name_str, 0, wxLEFT, 5); @@ -724,7 +725,7 @@ wxBoxSizer *CreateFilamentPresetDialog::create_vendor_item() m_filament_custom_vendor_input->GetTextCtrl()->SetHint(_L("Input Custom Vendor")); m_filament_custom_vendor_input->GetTextCtrl()->Bind(wxEVT_CHAR, [this](wxKeyEvent &event) { int key = event.GetKeyCode(); - if (cannot_input_key.find(key) != cannot_input_key.end()) { + if (cannot_input_key_for_filament.find(key) != cannot_input_key_for_filament.end()) { event.Skip(false); return; } @@ -835,7 +836,7 @@ wxBoxSizer *CreateFilamentPresetDialog::create_serial_item() comboBoxSizer->Add(m_filament_serial_input, 0, wxEXPAND | wxALL, 0); m_filament_serial_input->GetTextCtrl()->Bind(wxEVT_CHAR, [this](wxKeyEvent &event) { int key = event.GetKeyCode(); - if (cannot_input_key.find(key) != cannot_input_key.end()) { + if (cannot_input_key_for_filament.find(key) != cannot_input_key_for_filament.end()) { event.Skip(false); return; } diff --git a/src/slic3r/GUI/DeviceCore/CMakeLists.txt b/src/slic3r/GUI/DeviceCore/CMakeLists.txt index 57fd747..5a8fdbb 100644 --- a/src/slic3r/GUI/DeviceCore/CMakeLists.txt +++ b/src/slic3r/GUI/DeviceCore/CMakeLists.txt @@ -4,8 +4,14 @@ # status -- Building list(APPEND SLIC3R_GUI_SOURCES + GUI/DeviceCore/DevAxis.h + GUI/DeviceCore/DevAxis.cpp + GUI/DeviceCore/DevAxisCtrl.cpp GUI/DeviceCore/DevBed.cpp GUI/DeviceCore/DevBed.h + GUI/DeviceCore/DevChamber.h + GUI/DeviceCore/DevChamber.cpp + GUI/DeviceCore/DevChamberCtrl.cpp GUI/DeviceCore/DevConfig.h GUI/DeviceCore/DevConfig.cpp GUI/DeviceCore/DevConfigUtil.h @@ -47,10 +53,18 @@ list(APPEND SLIC3R_GUI_SOURCES GUI/DeviceCore/DevManager.cpp GUI/DeviceCore/DevMapping.h GUI/DeviceCore/DevMapping.cpp + GUI/DeviceCore/DevMappingNozzle.cpp + GUI/DeviceCore/DevNozzleRack.h + GUI/DeviceCore/DevNozzleRack.cpp + GUI/DeviceCore/DevNozzleRackCtrl.cpp GUI/DeviceCore/DevNozzleSystem.h GUI/DeviceCore/DevNozzleSystem.cpp + GUI/DeviceCore/DevUpgrade.h + GUI/DeviceCore/DevUpgrade.cpp + GUI/DeviceCore/DevUpgradeCtrl.cpp GUI/DeviceCore/DevUtil.h GUI/DeviceCore/DevUtil.cpp - + GUI/DeviceCore/DevUtilBackend.h + GUI/DeviceCore/DevUtilBackend.cpp ) set(SLIC3R_GUI_SOURCES ${SLIC3R_GUI_SOURCES} PARENT_SCOPE) \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceCore/DevAxis.cpp b/src/slic3r/GUI/DeviceCore/DevAxis.cpp new file mode 100644 index 0000000..201e3af --- /dev/null +++ b/src/slic3r/GUI/DeviceCore/DevAxis.cpp @@ -0,0 +1,25 @@ +#include "DevAxis.h" +#include "DevUtil.h" + +#include "slic3r/GUI/DeviceManager.hpp" + +namespace Slic3r +{ + +void DevAxis::ParseAxis(const json& print_json) +{ + DevJsonValParser::ParseVal(print_json, "home_flag", m_home_flag); + + if (print_json.contains("fun")) { + const std::string& fun = print_json["fun"].get(); + m_is_support_mqtt_homing = DevUtil::get_flag_bits(fun, 32); + m_is_support_mqtt_axis_ctrl = DevUtil::get_flag_bits(fun, 38); + } +} + +bool DevAxis::IsArchCoreXY() const +{ + return m_owner->get_printer_arch() == PrinterArch::ARCH_CORE_XY; +} + +} // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceCore/DevAxis.h b/src/slic3r/GUI/DeviceCore/DevAxis.h new file mode 100644 index 0000000..a109ff8 --- /dev/null +++ b/src/slic3r/GUI/DeviceCore/DevAxis.h @@ -0,0 +1,39 @@ +#pragma once +#include +#include "slic3r/Utils/json_diff.hpp" + +namespace Slic3r { + +class MachineObject; + +class DevAxis +{ +public: + static std::shared_ptr Create(MachineObject *obj) { return std::shared_ptr( new DevAxis(obj)); } + virtual ~DevAxis() = default; + +public: + bool IsAxisAtHomeX() const { return m_home_flag == 0 ? true : (m_home_flag & 1) == 1; } + bool IsAxisAtHomeY() const { return m_home_flag == 0 ? true : ((m_home_flag >> 1) & 1) == 1; } + bool IsAxisAtHomeZ() const { return m_home_flag == 0 ? true : ((m_home_flag >> 2) & 1) == 1; } + + bool IsArchCoreXY() const; + +public: + void ParseAxis(const json &print_json); + + int Ctrl_GoHome(); + int Ctrl_Axis(std::string axis, double unit = 1.0f, double input_val = 1.0f, int speed = 3000); // xyz e + +protected: + DevAxis(MachineObject *obj) noexcept : m_owner(obj) {} + +private: + MachineObject *m_owner = nullptr; + + int m_home_flag = 0; // bit0:X, bit1:Y, bit2:Z + bool m_is_support_mqtt_axis_ctrl = false; + bool m_is_support_mqtt_homing = false; +}; + +} // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceCore/DevAxisCtrl.cpp b/src/slic3r/GUI/DeviceCore/DevAxisCtrl.cpp new file mode 100644 index 0000000..6e276f0 --- /dev/null +++ b/src/slic3r/GUI/DeviceCore/DevAxisCtrl.cpp @@ -0,0 +1,62 @@ +#include "DevAxis.h" + +#include "slic3r/GUI/DeviceManager.hpp" +#include "slic3r/Utils/NetworkAgent.hpp" + +namespace Slic3r +{ + +int DevAxis::Ctrl_GoHome() +{ + if (m_is_support_mqtt_homing) { + json j; + j["print"]["command"] = "back_to_center"; + j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); + return m_owner->publish_json(j); + } + + // gcode command + return m_owner->is_in_printing() ? m_owner->publish_gcode("G28 X\n") : m_owner->publish_gcode("G28 \n"); +} + +int DevAxis::Ctrl_Axis(std::string axis, double unit, double input_val, int speed) +{ + if (m_is_support_mqtt_axis_ctrl) { + json j; + j["print"]["command"] = "xyz_ctrl"; + j["print"]["axis"] = axis; + j["print"]["dir"] = input_val > 0 ? 1 : -1; + j["print"]["mode"] = (std::abs(input_val) >= 10) ? 1 : 0; + j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); + return m_owner->publish_json(j); + } + + double value = input_val; + if (!IsArchCoreXY()) { + if (axis.compare("Y") == 0 || axis.compare("Z") == 0) { + value = -1.0 * input_val; + } + } + + char cmd[256]; + if (axis.compare("X") == 0 || axis.compare("Y") == 0 || axis.compare("Z") == 0) { + sprintf(cmd, "M211 S \nM211 X1 Y1 Z1\nM1002 push_ref_mode\nG91 \nG1 %s%0.1f F%d\nM1002 pop_ref_mode\nM211 R\n", axis.c_str(), value * unit, speed); + } else if (axis.compare("E") == 0) { + sprintf(cmd, "M83 \nG0 %s%0.1f F%d\n", axis.c_str(), value * unit, speed); + } else { + return -1; + } + + try { + if (m_owner->get_agent()) { + json j; + j["axis_control"] = axis; + m_owner->get_agent()->track_event("printer_control", j.dump()); + } + } catch (...) { + } + + return m_owner->publish_gcode(cmd); +} + +} // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceCore/DevChamber.cpp b/src/slic3r/GUI/DeviceCore/DevChamber.cpp new file mode 100644 index 0000000..a0edef5 --- /dev/null +++ b/src/slic3r/GUI/DeviceCore/DevChamber.cpp @@ -0,0 +1,50 @@ +#include "DevChamber.h" + +#include "DevConfig.h" +#include "DevUtil.h" + +#include "slic3r/GUI/DeviceManager.hpp" + +namespace Slic3r { + +bool DevChamber::HasChamber() const { return m_owner->GetConfig()->HasChamber(); } + +bool DevChamber::SupportChamberTempDisplay() const { return m_owner->GetConfig()->SupportChamberTempDisplay();} + +bool DevChamber::SupportChamberEdit() const { return m_owner->GetConfig()->SupportChamberEdit(); } + +int DevChamber::GetChamberTempEditMin() const { return m_owner->GetConfig()->GetChamberTempEditMin(); } + +int DevChamber::GetChamberTempEditMax() const { return m_owner->GetConfig()->GetChamberTempEditMax(); } + +int DevChamber::GetChamberTempSwitchHeat() const { return m_owner->GetConfig()->GetChamberTempSwitchHeat(); } + +void DevChamber::ParseChamber(const json &print_json) +{ + ParseChamberV1_0(print_json); + ParseChamberV2_0(print_json); +} + +void DevChamber::ParseChamberV1_0(const json &print_json) +{ + DevJsonValParser::ParseVal(print_json, "chamber_temper", m_temp); + DevJsonValParser::ParseVal(print_json, "ctt", m_temp_target); +} + +void DevChamber::ParseChamberV2_0(const json &print_json) +{ + if (print_json.contains("device")) { + const json &device_jj = print_json["device"]; + if (device_jj.contains("ctc")) { + const json &ctc = device_jj["ctc"]; + int state = DevUtil::get_flag_bits(ctc["state"].get(), 0, 4); + if (ctc.contains("info")) { + const json &info = ctc["info"]; + m_temp = DevUtil::get_flag_bits(info["temp"].get(), 0, 16); + m_temp_target = DevUtil::get_flag_bits(info["temp"].get(), 16, 16); + } + } + } +} + +} // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceCore/DevChamber.h b/src/slic3r/GUI/DeviceCore/DevChamber.h new file mode 100644 index 0000000..29acacf --- /dev/null +++ b/src/slic3r/GUI/DeviceCore/DevChamber.h @@ -0,0 +1,44 @@ +#pragma once +#include +#include "slic3r/Utils/json_diff.hpp" + +namespace Slic3r { + +class MachineObject; + +class DevChamber +{ +public: + static std::shared_ptr Create(MachineObject *obj) { return std::shared_ptr( new DevChamber(obj)); } + +public: // getter + bool HasChamber() const; + bool SupportChamberTempDisplay() const; + bool SupportChamberEdit() const; + int GetChamberTempEditMin() const; + int GetChamberTempEditMax() const; + int GetChamberTempSwitchHeat() const; + + float GetChamberTemp() const { return m_temp; }; + float GetChamberTempTarget() const { return m_temp_target; }; + +public: + // setter + void ParseChamber(const json &print_json); + + void ParseChamberV1_0(const json& print_json); + void ParseChamberV2_0(const json& print_json); + + // control + int CtrlSetChamberTemp(int temp); + +protected: + DevChamber(MachineObject *obj) : m_owner(obj) {} + +private: + float m_temp = 0.0f; + float m_temp_target = 0.0f; + MachineObject* m_owner = nullptr; +}; + +} // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceCore/DevChamberCtrl.cpp b/src/slic3r/GUI/DeviceCore/DevChamberCtrl.cpp new file mode 100644 index 0000000..651806f --- /dev/null +++ b/src/slic3r/GUI/DeviceCore/DevChamberCtrl.cpp @@ -0,0 +1,16 @@ +#include "DevChamber.h" +#include "slic3r/GUI/DeviceManager.hpp" + +namespace Slic3r { + +int DevChamber::CtrlSetChamberTemp(int temp) +{ + json j; + j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); + j["print"]["command"] = "set_ctt"; + j["print"]["ctt_val"] = temp; + + return m_owner->publish_json(j, 1); +} + +} // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceCore/DevConfig.cpp b/src/slic3r/GUI/DeviceCore/DevConfig.cpp index 5b8776a..6010abd 100644 --- a/src/slic3r/GUI/DeviceCore/DevConfig.cpp +++ b/src/slic3r/GUI/DeviceCore/DevConfig.cpp @@ -8,6 +8,15 @@ using namespace nlohmann; namespace Slic3r { +bool DevConfig::SupportChamberTempDisplay() const +{ + if (!m_support_chamber_temp_display.has_value()) { + return m_has_chamber; + } + + return m_support_chamber_temp_display.value(); +} + void DevConfig::ParseConfig(const json& print_json) { ParseChamberConfig(print_json); @@ -19,6 +28,11 @@ void DevConfig::ParseConfig(const json& print_json) void DevConfig::ParseChamberConfig(const json& print_json) { DevJsonValParser::ParseVal(print_json, "support_chamber", m_has_chamber); + + if (print_json.contains("support_chamber_temp_display")) { + m_support_chamber_temp_display = DevJsonValParser::GetVal(print_json, "support_chamber_temp_display"); + } + DevJsonValParser::ParseVal(print_json, "support_chamber_temp_edit", m_support_chamber_edit); if (m_support_chamber_edit) { diff --git a/src/slic3r/GUI/DeviceCore/DevConfig.h b/src/slic3r/GUI/DeviceCore/DevConfig.h index bc9cb78..449b5a7 100644 --- a/src/slic3r/GUI/DeviceCore/DevConfig.h +++ b/src/slic3r/GUI/DeviceCore/DevConfig.h @@ -3,8 +3,9 @@ #include "slic3r/Utils/json_diff.hpp" #include -#include +#include +#include namespace Slic3r { @@ -22,6 +23,7 @@ public: public: // chamber bool HasChamber() const { return m_has_chamber; } + bool SupportChamberTempDisplay() const; bool SupportChamberEdit() const { return m_support_chamber_edit; } int GetChamberTempEditMin() const { return m_chamber_temp_edit_min; } int GetChamberTempEditMax() const { return m_chamber_temp_edit_max; } @@ -57,6 +59,7 @@ private: /*configure vals*/ // chamber bool m_has_chamber = false; // whether the machine has a chamber + std::optional m_support_chamber_temp_display; bool m_support_chamber_edit = false; int m_chamber_temp_edit_min = 0; int m_chamber_temp_edit_max = 60; diff --git a/src/slic3r/GUI/DeviceCore/DevConfigUtil.cpp b/src/slic3r/GUI/DeviceCore/DevConfigUtil.cpp index b67d3d3..b5ed7fe 100644 --- a/src/slic3r/GUI/DeviceCore/DevConfigUtil.cpp +++ b/src/slic3r/GUI/DeviceCore/DevConfigUtil.cpp @@ -1,6 +1,7 @@ #include "DevConfigUtil.h" #include +#include using namespace nlohmann; diff --git a/src/slic3r/GUI/DeviceCore/DevConfigUtil.h b/src/slic3r/GUI/DeviceCore/DevConfigUtil.h index 71fb24a..d4fce2b 100644 --- a/src/slic3r/GUI/DeviceCore/DevConfigUtil.h +++ b/src/slic3r/GUI/DeviceCore/DevConfigUtil.h @@ -76,6 +76,7 @@ public: /*calibration*/ static std::vector get_unsupport_auto_cali_filaments(std::string type_str) { return get_value_from_config>(type_str, "auto_cali_not_support_filaments"); } + static bool support_disable_cali_flow_type(std::string type_str) { return get_value_from_config(type_str,"support_disable_cali_flow_type"); } /*detection*/ static bool support_wrapping_detection(const std::string& type_str) { return get_value_from_config(type_str, "support_wrapping_detection"); } diff --git a/src/slic3r/GUI/DeviceCore/DevDefs.h b/src/slic3r/GUI/DeviceCore/DevDefs.h index 468ed01..b0a584c 100644 --- a/src/slic3r/GUI/DeviceCore/DevDefs.h +++ b/src/slic3r/GUI/DeviceCore/DevDefs.h @@ -10,11 +10,21 @@ #pragma once #include +#include + +// Reserved for future usage +#define DEV_RESERVED_FOR_FUTURE(...) /* stripped */ + +// Previous definitions +namespace Slic3r +{ + class MachineObject; +} enum PrinterArch { - ARCH_CORE_XY, - ARCH_I3, + ARCH_CORE_XY,// move hotbed + ARCH_I3,//move z }; enum PrinterSeries @@ -55,14 +65,27 @@ enum AmsStatusMain #define UNIQUE_EXTRUDER_ID MAIN_EXTRUDER_ID #define INVALID_EXTRUDER_ID -1 +// see PartPlate::get_physical_extruder_by_logical_extruder +#define LOGIC_UNIQUE_EXTRUDER_ID 0 +#define LOGIC_L_EXTRUDER_ID 0 +#define LOGIC_R_EXTRUDER_ID 1 /* Nozzle*/ -enum NozzleFlowType +enum NozzleFlowType : int { NONE_FLOWTYPE, S_FLOW, H_FLOW }; +/* 0.2mm 0.4mm 0.6mm 0.8mm */ +enum NozzleDiameterType : int +{ + NONE_DIAMETER_TYPE, + NOZZLE_DIAMETER_0_2, + NOZZLE_DIAMETER_0_4, + NOZZLE_DIAMETER_0_6, + NOZZLE_DIAMETER_0_8 +}; /*Print speed*/ enum DevPrintingSpeedLevel @@ -76,7 +99,7 @@ enum DevPrintingSpeedLevel }; /*Upgrade*/ -enum class DevFirmwareUpgradingState : int +enum class DevFirmwareUpgradeState : int { DC = -1, UpgradingUnavaliable = 0, @@ -85,6 +108,56 @@ enum class DevFirmwareUpgradingState : int UpgradingFinished = 3 }; +struct DevNozzleMappingResult +{ + friend class MachineObject; +public: + void Clear(); + + bool HasResult() const { return !m_result.empty();} + std::string GetResultStr() const { return m_result; } + + // mqtt error info + std::string GetMqttReason() const { return m_mqtt_reason; } + + // command error info + int GetErrno() const { return m_errno; } + std::string GetDetailMsg() const { return m_detail_msg; } + + // nozzle mapping + std::unordered_map GetNozzleMapping() const { return m_nozzle_mapping; } + nlohmann::json GetNozzleMappingJson() const { return m_nozzle_mapping_json; } + void SetManualNozzleMapping(Slic3r::MachineObject* obj, int fila_id, int nozzle_pos_id); + int GetMappedNozzlePosIdByFilaId(Slic3r::MachineObject* obj, int fila_id) const;// return -1 if not mapped + + // flush weight + float GetFlushWeightBase() const { return m_flush_weight_base;} + float GetFlushWeightCurrent() const { return m_flush_weight_current; } + +public: + void ParseAutoNozzleMapping(Slic3r::MachineObject* obj, const nlohmann::json& print_jj); + +private: + float GetFlushWeight(Slic3r::MachineObject* obj) const; + +private: + std::string m_sequence_id; + + std::string m_result; + std::string m_mqtt_reason; + std::string m_type; // auto or manual + + int m_errno; + std::string m_detail_msg; + nlohmann::json m_detail_json; + + nlohmann::json m_nozzle_mapping_json; + std::unordered_map m_nozzle_mapping; // key: fila_id, value: nozzle_id (from 0x10), the tar_id is no extruder + + float m_flush_weight_base = -1;// the base weight for flush + float m_flush_weight_current = -1;// the weight current +}; + class devPrinterUtil { public: @@ -96,4 +169,28 @@ public: static bool IsVirtualSlot(const std::string& ams_id) { return (ams_id == VIRTUAL_AMS_MAIN_ID_STR || ams_id == VIRTUAL_AMS_DEPUTY_ID_STR); } }; -};// namespace Slic3r \ No newline at end of file +};// namespace Slic3r + +struct NozzleDef +{ + float nozzle_diameter; + Slic3r::NozzleFlowType nozzle_flow_type; + + bool operator==(const NozzleDef& other) const + { + return nozzle_diameter == other.nozzle_diameter && nozzle_flow_type == other.nozzle_flow_type; + } +}; + +template<> struct std::hash +{ + std::size_t operator()(const NozzleDef& v) const noexcept + { + size_t h1 = std::hash{}(v.nozzle_diameter * 1000); + size_t h2 = std::hash{}(v.nozzle_flow_type); + return h1 ^ (h2 + 0x9e3779b9 + (h1 << 6) + (h1 >> 2)); + }; +}; + +// key(extruder_id) -> { key1(nozzle type info), val1( number of the nozzle type)} +using ExtruderNozzleInfos = std::unordered_map>; \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceCore/DevExtruderSystem.cpp b/src/slic3r/GUI/DeviceCore/DevExtruderSystem.cpp index 6c96d85..879a609 100644 --- a/src/slic3r/GUI/DeviceCore/DevExtruderSystem.cpp +++ b/src/slic3r/GUI/DeviceCore/DevExtruderSystem.cpp @@ -48,17 +48,22 @@ namespace Slic3r NozzleType DevExtder::GetNozzleType() const { - return system->Owner()->GetNozzleSystem()->GetNozzle(m_current_nozzle_id).m_nozzle_type; + return system->Owner()->GetNozzleSystem()->GetExtNozzle(m_current_nozzle_id).m_nozzle_type; } NozzleFlowType DevExtder::GetNozzleFlowType() const { - return system->Owner()->GetNozzleSystem()->GetNozzle(m_current_nozzle_id).m_nozzle_flow; + return system->Owner()->GetNozzleSystem()->GetExtNozzle(m_current_nozzle_id).m_nozzle_flow; } float DevExtder::GetNozzleDiameter() const { - return system->Owner()->GetNozzleSystem()->GetNozzle(m_current_nozzle_id).m_diameter; + return system->Owner()->GetNozzleSystem()->GetExtNozzle(m_current_nozzle_id).m_diameter; + } + + NozzleDiameterType DevExtder::GetNozzleDiameterType() const + { + return system->Owner()->GetNozzleSystem()->GetExtNozzle(m_current_nozzle_id).GetNozzleDiameterType(); } DevExtderSystem::DevExtderSystem(MachineObject* obj) @@ -96,9 +101,12 @@ namespace Slic3r std::optional DevExtderSystem::GetExtderById(int extder_id) const { - if (extder_id >= m_extders.size()) - { - assert(false && "Invalid extruder ID"); + if (extder_id == 0xF) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " the extruder maybe changing: " << extder_id; + return std::nullopt; + } + + if (extder_id >= m_extders.size()) { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "No extruder found for " << extder_id; return std::nullopt; } diff --git a/src/slic3r/GUI/DeviceCore/DevExtruderSystem.h b/src/slic3r/GUI/DeviceCore/DevExtruderSystem.h index d0f7c10..1ab6711 100644 --- a/src/slic3r/GUI/DeviceCore/DevExtruderSystem.h +++ b/src/slic3r/GUI/DeviceCore/DevExtruderSystem.h @@ -47,13 +47,14 @@ public: wxString GetDisplayName() const; // installed nozzle info - bool HasNozzleInstalled() const = delete;//{ return m_has_nozzle; } + bool HasNozzleInstalled() const { return m_has_nozzle; } int GetNozzleId() const { return m_current_nozzle_id; } int GetTargetNozzleId() const = delete;//{ return m_target_nozzle_id; } NozzleType GetNozzleType() const; NozzleFlowType GetNozzleFlowType() const; float GetNozzleDiameter() const; + NozzleDiameterType GetNozzleDiameterType() const; // temperature int GetCurrentTemp() const { return m_cur_temp; } @@ -80,7 +81,7 @@ private: int m_ext_id; // 0-right 1-left // current nozzle - bool m_has_nozzle = false; + bool m_has_nozzle = true; // default to true, since A/P/.. series does not support nozzle detecion int m_current_nozzle_id = 0; // nozzle id now. for some machine, the extruder may have serveral nozzles int m_target_nozzle_id = 0; // target nozzle id @@ -140,11 +141,12 @@ public: const std::vector& GetExtruders() const { return m_extders;}; // get nozzle info which is installed on the extruder - NozzleType GetNozzleType(int extder_id) const { return GetExtderById(extder_id) ? GetExtderById(extder_id)->GetNozzleType() : NozzleType::ntUndefine; } - NozzleFlowType GetNozzleFlowType(int extder_id) const { return GetExtderById(extder_id) ? GetExtderById(extder_id)->GetNozzleFlowType() : NozzleFlowType::NONE_FLOWTYPE;; } - float GetNozzleDiameter(int extder_id) const { return GetExtderById(extder_id) ? GetExtderById(extder_id)->GetNozzleDiameter() : 0.0; } - int GetNozzleTempCurrent(int extder_id) const { return GetExtderById(extder_id) ? GetExtderById(extder_id)->GetCurrentTemp() : 0; } - int GetNozzleTempTarget(int extder_id) const { return GetExtderById(extder_id) ? GetExtderById(extder_id)->GetTargetTemp() : 0; } + NozzleType GetNozzleType(int extder_id) const { return GetExtderById(extder_id) ? GetExtderById(extder_id)->GetNozzleType() : NozzleType::ntUndefine; } + NozzleFlowType GetNozzleFlowType(int extder_id) const { return GetExtderById(extder_id) ? GetExtderById(extder_id)->GetNozzleFlowType() : NozzleFlowType::NONE_FLOWTYPE; } + NozzleDiameterType GetNozzleDiameterType(int extder_id) const { return GetExtderById(extder_id) ? GetExtderById(extder_id)->GetNozzleDiameterType() : NozzleDiameterType::NONE_DIAMETER_TYPE; } + float GetNozzleDiameter(int extder_id) const { return GetExtderById(extder_id) ? GetExtderById(extder_id)->GetNozzleDiameter() : 0.0; } + int GetNozzleTempCurrent(int extder_id) const { return GetExtderById(extder_id) ? GetExtderById(extder_id)->GetCurrentTemp() : 0; } + int GetNozzleTempTarget(int extder_id) const { return GetExtderById(extder_id) ? GetExtderById(extder_id)->GetTargetTemp() : 0; } // get slot info which is connected to the extruder std::string GetCurrentAmsId() const; diff --git a/src/slic3r/GUI/DeviceCore/DevFilaAmsSetting.h b/src/slic3r/GUI/DeviceCore/DevFilaAmsSetting.h index dbc5777..a60dbee 100644 --- a/src/slic3r/GUI/DeviceCore/DevFilaAmsSetting.h +++ b/src/slic3r/GUI/DeviceCore/DevFilaAmsSetting.h @@ -1,4 +1,5 @@ #pragma once +#include #include #include "DevCtrl.h" @@ -74,7 +75,7 @@ public: DevAmsSystemIdx GetCurrentFirmwareIdxSel() const { return m_current_firmware_sel.m_firmare_idx; }; DevAmsSystemIdx GetCurrentFirmwareIdxRun() const { return m_current_firmware_run.m_firmare_idx; }; - std::unordered_map GetSuppotedFirmwares() const { return m_firmwares;}; + std::map GetSuppotedFirmwares() const { return m_firmwares;}; bool IsSwitching() const { return m_status == "SWITCHING";}; bool IsIdle() const { return m_status == "IDLE";}; @@ -93,7 +94,7 @@ private: DevAmsSystemFirmware m_current_firmware_run; DevAmsSystemFirmware m_current_firmware_sel; - std::unordered_map m_firmwares; + std::map m_firmwares; DevCtrlInfo m_ctrl_switching; }; diff --git a/src/slic3r/GUI/DeviceCore/DevFilaBlackList.cpp b/src/slic3r/GUI/DeviceCore/DevFilaBlackList.cpp index b35d84a..48677e6 100644 --- a/src/slic3r/GUI/DeviceCore/DevFilaBlackList.cpp +++ b/src/slic3r/GUI/DeviceCore/DevFilaBlackList.cpp @@ -60,10 +60,10 @@ static std::string _get_filament_name_from_ams(int ams_id, int slot_id) return name; } - const auto tray = obj->GetFilaSystem()->GetAmsTray(std::to_string(ams_id), std::to_string(slot_id)); - if (!tray) { return name; } + const auto tray = obj->get_tray(std::to_string(ams_id), std::to_string(slot_id)); + if (!tray.is_tray_info_ready()) { return name; } - std::string filament_id = tray->setting_id; + std::string filament_id = tray.setting_id; PresetBundle* preset_bundle = GUI::wxGetApp().preset_bundle; auto option = preset_bundle->get_filament_by_filament_id(filament_id); @@ -78,6 +78,7 @@ void check_filaments(std::string model_id, int ams_id, int slot_id, std::string tag_name, + std::string nozzle_flow, bool& in_blacklist, std::string& ac, wxString& info, @@ -102,15 +103,20 @@ void check_filaments(std::string model_id, std::string vendor = filament_item.contains("vendor") ? filament_item["vendor"].get() : ""; std::string type = filament_item.contains("type") ? filament_item["type"].get() : ""; std::string type_suffix = filament_item.contains("type_suffix") ? filament_item["type_suffix"].get() : ""; + std::string name_suffix = filament_item.contains("name_suffix") ? filament_item["name_suffix"].get() : ""; std::string name = filament_item.contains("name") ? filament_item["name"].get() : ""; std::string slot = filament_item.contains("slot") ? filament_item["slot"].get() : ""; std::vector model_ids = filament_item.contains("model_id") ? filament_item["model_id"].get>() : std::vector(); + std::vector nozzle_flows = filament_item.contains("nozzle_flows") ? filament_item["nozzle_flows"].get>() : std::vector(); std::string action = filament_item.contains("action") ? filament_item["action"].get() : ""; std::string description = filament_item.contains("description") ? filament_item["description"].get() : ""; // check model id if (!model_ids.empty() && std::find(model_ids.begin(), model_ids.end(), model_id) == model_ids.end()) { continue; } + // check nozzle flows + if (!nozzle_flows.empty() && std::find(nozzle_flows.begin(), nozzle_flows.end(), nozzle_flow) == nozzle_flows.end()) { continue; } + // check vendor std::transform(vendor.begin(), vendor.end(), vendor.begin(), ::tolower); if (!vendor.empty()) @@ -142,6 +148,14 @@ void check_filaments(std::string model_id, std::transform(name.begin(), name.end(), name.begin(), ::tolower); if (!name.empty() && (name != tag_name)) { continue; } + // check name suffix + std::transform(name_suffix.begin(), name_suffix.end(), name_suffix.begin(), ::tolower); + if (!name_suffix.empty()) + { + if (tag_name.length() < name_suffix.length()) { continue; } + if ((tag_name.substr(tag_name.length() - name_suffix.length()) != name_suffix)) { continue; } + } + // check loc if (!slot.empty()) { @@ -166,7 +180,8 @@ void check_filaments(std::string model_id, // Error in description L("TPU is not supported by Box."); - L("Box does not support 'QIDI PET-CF'."); + L("AMS does not support 'QIDI PET-CF'."); + L("The current filament doesn't support the E3D high-flow nozzle and can't be used."); // Warning in description L("Please cold pull before printing TPU to avoid clogging. You may use cold pull maintenance on the printer."); @@ -176,6 +191,7 @@ void check_filaments(std::string model_id, L("CF/GF filaments are hard and brittle, It's easy to break or get stuck in AMS, please use with caution."); L("PPS-CF is brittle and could break in bended PTFE tube above Toolhead."); L("PPA-CF is brittle and could break in bended PTFE tube above Toolhead."); + L("Default settings may affect print quality. Adjust as needed for best results."); } } } @@ -189,12 +205,13 @@ void DevFilaBlacklist::check_filaments_in_blacklist(std::string model_id, int ams_id, int slot_id, std::string tag_name, + std::string nozzle_flow, bool& in_blacklist, std::string& ac, wxString& info) { wxString wiki_url; - check_filaments_in_blacklist_url(model_id, tag_vendor, tag_type, filament_id, ams_id, slot_id, tag_name, in_blacklist, ac, info, wiki_url); + check_filaments_in_blacklist_url(model_id, tag_vendor, tag_type, filament_id, ams_id, slot_id, tag_name, nozzle_flow, in_blacklist, ac, info, wiki_url); } @@ -240,7 +257,7 @@ bool check_filaments_printable(const std::string &tag_vendor, const std::string return true; } -void DevFilaBlacklist::check_filaments_in_blacklist_url(std::string model_id, std::string tag_vendor, std::string tag_type, const std::string& filament_id, int ams_id, int slot_id, std::string tag_name, bool& in_blacklist, std::string& ac, wxString& info, wxString& wiki_url) +void DevFilaBlacklist::check_filaments_in_blacklist_url(std::string model_id, std::string tag_vendor, std::string tag_type, const std::string& filament_id, int ams_id, int slot_id, std::string tag_name, std::string nozzle_flow, bool& in_blacklist, std::string& ac, wxString& info, wxString& wiki_url) { if (ams_id < 0 || slot_id < 0) { @@ -252,7 +269,7 @@ void DevFilaBlacklist::check_filaments_in_blacklist_url(std::string model_id, st return; } - check_filaments(model_id, tag_vendor, tag_type, ams_id, slot_id, tag_name, in_blacklist, ac, info, wiki_url); + check_filaments(model_id, tag_vendor, tag_type, ams_id, slot_id, tag_name, nozzle_flow, in_blacklist, ac, info, wiki_url); } } \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceCore/DevFilaBlackList.h b/src/slic3r/GUI/DeviceCore/DevFilaBlackList.h index 7eed1f5..467e294 100644 --- a/src/slic3r/GUI/DeviceCore/DevFilaBlackList.h +++ b/src/slic3r/GUI/DeviceCore/DevFilaBlackList.h @@ -8,8 +8,8 @@ class DevFilaBlacklist { public: static bool load_filaments_blacklist_config(); - static void check_filaments_in_blacklist(std::string model_id, std::string tag_vendor, std::string tag_type, const std::string& filament_id, int ams_id, int slot_id, std::string tag_name, bool& in_blacklist, std::string& ac, wxString& info); - static void check_filaments_in_blacklist_url(std::string model_id, std::string tag_vendor, std::string tag_type, const std::string& filament_id, int ams_id, int slot_id, std::string tag_name, bool& in_blacklist, std::string& ac, wxString& info, wxString& wiki_url); + static void check_filaments_in_blacklist(std::string model_id, std::string tag_vendor, std::string tag_type, const std::string& filament_id, int ams_id, int slot_id, std::string tag_name, std::string nozzle_flow, bool& in_blacklist, std::string& ac, wxString& info); + static void check_filaments_in_blacklist_url(std::string model_id, std::string tag_vendor, std::string tag_type, const std::string& filament_id, int ams_id, int slot_id, std::string tag_name, std::string nozzle_flow, bool& in_blacklist, std::string& ac, wxString& info, wxString& wiki_url); public: static json filaments_blacklist; diff --git a/src/slic3r/GUI/DeviceCore/DevFilaSystem.cpp b/src/slic3r/GUI/DeviceCore/DevFilaSystem.cpp index 50ca1c7..e534697 100644 --- a/src/slic3r/GUI/DeviceCore/DevFilaSystem.cpp +++ b/src/slic3r/GUI/DeviceCore/DevFilaSystem.cpp @@ -1,4 +1,5 @@ #include +#include "DevExtruderSystem.h" #include "DevFilaSystem.h" // TODO: remove this include @@ -6,26 +7,31 @@ #include "slic3r/GUI/I18N.hpp" #include "DevUtil.h" +#include "DevNozzleSystem.h" using namespace nlohmann; namespace Slic3r { -static int _hex_digit_to_int(const char c) { return (c >= '0' && c <= '9') ? c - '0' : (c >= 'A' && c <= 'F') ? c - 'A' + 10 : (c >= 'a' && c <= 'f') ? c - 'a' + 10 : -1; } wxColour DevAmsTray::decode_color(const std::string &color) { - std::array ret = {0, 0, 0, 0}; - const char * c = color.data(); - if (color.size() == 8) { - for (size_t j = 0; j < 4; ++j) { - int digit1 = _hex_digit_to_int(*c++); - int digit2 = _hex_digit_to_int(*c++); - if (digit1 == -1 || digit2 == -1) break; - ret[j] = static_cast(digit1 * 16 + digit2); - } - } else { return wxColour(255, 255, 255, 255); } + if (color.empty()) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": empty"; + return wxColour(255, 255, 255, 255);//default white + } - return wxColour(ret[0], ret[1], ret[2], ret[3]); + std::string clr_str = color; + if (color[0] != '#') { + clr_str = "#" + color; + } + + const auto& clr = wxColour(clr_str); + if (clr.IsOk()) { + return clr; + } + + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": " << clr_str; + return wxColour(255, 255, 255, 255);//default white } void DevAmsTray::UpdateColorFromStr(const std::string& color) @@ -138,7 +144,7 @@ wxString DevAms::GetDisplayName() const { wxString ams_display_format; auto iter = s_ams_display_formats.find(m_ams_type); - if (iter != s_ams_display_formats.end()) + if (iter != s_ams_display_formats.end()) { ams_display_format = iter->second; } @@ -153,7 +159,7 @@ wxString DevAms::GetDisplayName() const { num_id = std::stoi(GetAmsId()); } - catch (const std::exception& e) + catch (const std::exception& e) { assert(0 && __FUNCTION__); BOOST_LOG_TRIVIAL(error) << "Invalid AMS ID: " << GetAmsId() << ", error: " << e.what(); @@ -254,10 +260,18 @@ int DevFilaSystem::GetExtruderIdByAmsId(const std::string& ams_id) const return DEPUTY_EXTRUDER_ID; } - assert(false && __FUNCTION__); + + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": ams_id " << ams_id << " not found"; return 0; // not found } +std::string DevFilaSystem::GetNozzleFlowStringByAmsId(const std::string& ams_id) const +{ + auto extuder_id = GetExtruderIdByAmsId(ams_id); + auto nozzle = GetOwner()->GetNozzleSystem()->GetExtNozzle(extuder_id); + return nozzle.GetNozzleFlowTypeString(nozzle.GetNozzleFlowType()); +} + bool DevFilaSystem::IsAmsSettingUp() const { int setting_up_stat = DevUtil::get_flag_bits(m_ams_cali_stat, 0, 8); @@ -284,6 +298,11 @@ bool DevFilaSystem::IsQDT_Filament(std::string tag_uid) return false; } +bool DevFilaSystem::CanShowFilamentBackup() const +{ + return m_owner->is_support_filament_backup && IsAutoRefillEnabled() && HasAms() && m_owner->GetExtderSystem()->HasFilamentBackup(); +} + void DevFilaSystemParser::ParseV1_0(const json& jj, MachineObject* obj, DevFilaSystem* system, bool key_field_only) { if (jj.contains("ams")) @@ -366,7 +385,7 @@ void DevFilaSystemParser::ParseV1_0(const json& jj, MachineObject* obj, DevFilaS type_id = DevUtil::get_flag_bits(info, 0, 4); extuder_id = DevUtil::get_flag_bits(info, 8, 4); } else { - if (!obj->is_enable_ams_np) { + if (!obj->is_enable_ams_np && obj->get_printer_ams_type() == "f1") { type_id = DevAms::AMS_LITE; } } diff --git a/src/slic3r/GUI/DeviceCore/DevFilaSystem.h b/src/slic3r/GUI/DeviceCore/DevFilaSystem.h index deb74b7..51b8bcf 100644 --- a/src/slic3r/GUI/DeviceCore/DevFilaSystem.h +++ b/src/slic3r/GUI/DeviceCore/DevFilaSystem.h @@ -7,6 +7,7 @@ #include "DevUtil.h" #include +#include #include #include #include @@ -169,6 +170,12 @@ public: // extruder int GetExtruderIdByAmsId(const std::string& ams_id) const; + // nozzle + std::string GetNozzleFlowStringByAmsId(const std::string& ams_id) const; + + // filament backup + bool CanShowFilamentBackup() const; + /* AMS settings*/ DevAmsSystemSetting& GetAmsSystemSetting() { return m_ams_system_setting; } std::optional IsDetectOnInsertEnabled() const { return m_ams_system_setting.IsDetectOnInsertEnabled(); }; @@ -181,7 +188,7 @@ public: public: // ctrls int CtrlAmsReset() const; - + public: static bool IsQDT_Filament(std::string tag_uid); diff --git a/src/slic3r/GUI/DeviceCore/DevFirmware.h b/src/slic3r/GUI/DeviceCore/DevFirmware.h index 2f45da4..d85fe87 100644 --- a/src/slic3r/GUI/DeviceCore/DevFirmware.h +++ b/src/slic3r/GUI/DeviceCore/DevFirmware.h @@ -1,4 +1,7 @@ #pragma once +#include "DevFirmware.h" +#include "slic3r/Utils/json_diff.hpp" + #include #include #include "slic3r/Utils/json_diff.hpp" @@ -8,14 +11,6 @@ namespace Slic3r { //Previous definitions class MachineObject; -enum PrinterFirmwareType -{ - FIRMWARE_TYPE_ENGINEER = 0, - FIRMWARE_TYPE_PRODUCTION, - FIRMEARE_TYPE_UKNOWN, -}; - - class FirmwareInfo { public: @@ -40,10 +35,13 @@ public: public: bool isValid() const { return !sn.empty(); } + + /*type check*/ bool isAirPump() const { return product_name.Contains("Air Pump"); } bool isLaszer() const { return product_name.Contains("Laser"); } bool isCuttingModule() const { return product_name.Contains("Cutting Module"); } - bool isExtinguishSystem() const { return product_name.Contains("Extinguishing System"); } + bool isExtinguishSystem() const { return product_name.Contains("Extinguishing System"); }// Auto Fire Extinguishing System + bool isWTM() const { return name.find("wtm") != string::npos; } // nozzle }; diff --git a/src/slic3r/GUI/DeviceCore/DevInfo.cpp b/src/slic3r/GUI/DeviceCore/DevInfo.cpp index 4c97ee3..7ba0796 100644 --- a/src/slic3r/GUI/DeviceCore/DevInfo.cpp +++ b/src/slic3r/GUI/DeviceCore/DevInfo.cpp @@ -1,7 +1,39 @@ #include "DevInfo.h" +#include "DevUtil.h" #include "slic3r/GUI/DeviceManager.hpp" namespace Slic3r { +// begin of class MachineObject +std::string MachineObject::connection_type() const { + return m_dev_info->ConnectionType(); +} + +bool MachineObject::is_lan_mode_printer() const { + return m_dev_info->IsLanMode(); +} + +bool MachineObject::is_cloud_mode_printer() const { + return m_dev_info->IsCloudMode(); +} +// end of class MachineObject + +// begin pf class DevInfo +void DevInfo::SetConnectionType(const std::string &type) { + if (m_dev_connection_type != type) { + m_dev_connection_type = type; + } +} + +void DevInfo::ParseInfo(const nlohmann::json &print_jj) { + + if (print_jj.contains("device")) { + const auto& device_jj = print_jj["device"]; + DevJsonValParser::ParseVal(device_jj, "connection_type", m_dev_connection_type); + DevJsonValParser::ParseVal(device_jj, "type", m_device_mode); + } +} +// end of class DevInfo + } \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceCore/DevInfo.h b/src/slic3r/GUI/DeviceCore/DevInfo.h index bc48e6c..c51c9ae 100644 --- a/src/slic3r/GUI/DeviceCore/DevInfo.h +++ b/src/slic3r/GUI/DeviceCore/DevInfo.h @@ -3,6 +3,9 @@ #include "slic3r/Utils/json_diff.hpp" #include +#include "DevDefs.h" +#include "slic3r/GUI/DeviceManager.hpp" + namespace Slic3r { class MachineObject; @@ -10,28 +13,62 @@ class MachineObject; /* some static info of machine*/ /*TODO*/ class DevInfo { -public: - DevInfo(MachineObject* obj) : m_owner(obj) {}; + friend class DeviceManager; public: - //std::string GetDevName() const { return m_dev_name; } - //std::string GetDevId() const { return m_dev_id; } - //std::string GetDevIP() const { return m_dev_ip; } - //std::string GetPrinterTypeStr() const { return m_printer_type_str; } - //std::string GetPrinterSignal() const { return m_printer_signal; } - //std::string GetConnectType() const { return m_connect_type; } - //std::string GetBindState() const { return m_bind_state; } + static std::shared_ptr Create(MachineObject *obj) { return std::shared_ptr(new DevInfo(obj)); } + +public: + void SetDevId(const std::string &id) { m_dev_id = id; } + std::string GetDevId() const { return m_dev_id; } + + // std::string GetDevIP() const { return m_dev_ip; } + // std::string GetDevName() const { return m_dev_name; } + // std::string GetPrinterTypeStr() const { return m_printer_type_str; } + // std::string GetPrinterSignal() const { return m_printer_signal; } + // std::string GetConnectType() const { return m_connect_type; } + // std::string GetBindState() const { return m_bind_state; } + + // connection type + std::string ConnectionType() const { return m_dev_connection_type; } + bool IsLanMode() const { return m_dev_connection_type == "lan"; } + bool IsCloudMode() const { return m_dev_connection_type == "cloud"; } + + // device mode + bool IsFdmMode() const { return (m_device_mode & DEVICE_MODE_FDM) == DEVICE_MODE_FDM; } + DEV_RESERVED_FOR_FUTURE( + bool IsLaserMode() const { return (m_device_mode & DEVICE_MODE_LASER) == DEVICE_MODE_LASER; } + bool IsCutMode() const { return (m_device_mode & DEVICE_MODE_CUT) = DEVICE_MODE_CUT; } + ); + + // Passer + void ParseInfo(const nlohmann::json &print_jj); + +protected: + DevInfo(MachineObject *obj) : m_owner(obj) { m_device_mode = DEVICE_MODE_FDM; }; + + // setters + void SetConnectionType(const std::string &type); private: - //std::string m_dev_name; - //std::string m_dev_id; - //std::string m_dev_ip; - //std::string m_printer_type_str; - //std::string m_printer_signal; - //std::string m_connect_type; - //std::string m_bind_state; + MachineObject *m_owner = nullptr; - MachineObject* m_owner = nullptr; + std::string m_dev_id; + // std::string m_dev_name; + // std::string m_dev_ip; + // std::string m_printer_type_str; + // std::string m_printer_signal; + // std::string m_connect_type; + // std::string m_bind_state; + + std::string m_dev_connection_type; + + enum DeviceMode : unsigned int { + DEVICE_MODE_UNKNOWN = 0x00000000, + DEVICE_MODE_FDM = 0x00000001, + DEVICE_MODE_LASER = 0x00000010, + DEVICE_MODE_CUT = 0x00000100, + } m_device_mode; }; } // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceCore/DevManager.cpp b/src/slic3r/GUI/DeviceCore/DevManager.cpp index fb7df73..5f8121c 100644 --- a/src/slic3r/GUI/DeviceCore/DevManager.cpp +++ b/src/slic3r/GUI/DeviceCore/DevManager.cpp @@ -1,4 +1,5 @@ #include +#include "DevInfo.h" #include "DevManager.h" #include "DevUtil.h" @@ -141,7 +142,7 @@ namespace Slic3r if (it->second->get_dev_ip() != dev_ip || it->second->bind_state != bind_state || it->second->bind_sec_link != sec_link || - it->second->dev_connection_type != connect_type || + it->second->connection_type() != connect_type || it->second->bind_ssdp_version != ssdp_version) { if (it->second->bind_state != bind_state) { @@ -151,7 +152,7 @@ namespace Slic3r it->second->set_dev_ip(dev_ip); it->second->bind_state = bind_state; it->second->bind_sec_link = sec_link; - it->second->dev_connection_type = connect_type; + it->second->GetInfo()->SetConnectionType(connect_type); it->second->bind_ssdp_version = ssdp_version; BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " UpdateUserMachineInfo" << ", dev_id= " << QDTCrossTalk::Crosstalk_DevId(dev_id) @@ -187,13 +188,13 @@ namespace Slic3r } if (obj->wifi_signal != printer_signal || - obj->dev_connection_type != connect_type || + obj->connection_type() != connect_type || obj->bind_state != bind_state || obj->bind_sec_link != sec_link || obj->bind_ssdp_version != ssdp_version || obj->printer_type != _parse_printer_type(printer_type_str)) { - if (obj->dev_connection_type != connect_type || + if (obj->connection_type() != connect_type || obj->bind_state != bind_state || obj->bind_sec_link != sec_link || obj->bind_ssdp_version != ssdp_version || @@ -217,7 +218,7 @@ namespace Slic3r } obj->wifi_signal = printer_signal; - obj->dev_connection_type = connect_type; + it->second->GetInfo()->SetConnectionType(connect_type); obj->bind_state = bind_state; obj->bind_sec_link = sec_link; obj->bind_ssdp_version = ssdp_version; @@ -225,10 +226,9 @@ namespace Slic3r } // U0 firmware - if (obj->dev_connection_type.empty() && obj->bind_state.empty()) + if (obj->connection_type().empty() && obj->bind_state.empty()) obj->bind_state = "free"; - obj->last_alive = Slic3r::Utils::get_current_time_utc(); obj->m_is_online = true; obj->set_dev_name(dev_name); /* if (!obj->dev_ip.empty()) { @@ -241,7 +241,7 @@ namespace Slic3r obj = new MachineObject(this, m_agent, dev_name, dev_id, dev_ip); obj->printer_type = _parse_printer_type(printer_type_str); obj->wifi_signal = printer_signal; - obj->dev_connection_type = connect_type; + obj->GetInfo()->SetConnectionType(connect_type); obj->bind_state = bind_state; obj->bind_sec_link = sec_link; obj->dev_connection_name = connection_name; @@ -280,7 +280,13 @@ namespace Slic3r obj->printer_type = _parse_printer_type("C11"); else obj->printer_type = _parse_printer_type(printer_type); - obj->dev_connection_type = connection_type == "farm" ? "lan":connection_type; + + if (connection_type == "farm") { + obj->GetInfo()->SetConnectionType("lan"); + } else { + obj->GetInfo()->SetConnectionType(connection_type); + } + obj->bind_state = connection_type == "farm" ? "free":bind_state; obj->bind_sec_link = "secure"; obj->bind_ssdp_version = version; @@ -456,7 +462,6 @@ namespace Slic3r #else it->second->connect(it->second->local_use_ssl_for_mqtt); #endif - it->second->set_lan_mode_connection_state(true); } } } @@ -480,7 +485,6 @@ namespace Slic3r #else it->second->connect(it->second->local_use_ssl_for_mqtt); #endif - it->second->set_lan_mode_connection_state(true); } } } @@ -668,11 +672,11 @@ namespace Slic3r { /* update field */ obj = iter->second; - obj->set_dev_id(dev_id); + obj->GetInfo()->SetDevId(dev_id); } else { - obj = new MachineObject(this, m_agent, "", "", ""); + obj = new MachineObject(this, m_agent, "", dev_id, ""); if (m_agent) { obj->set_bind_status(m_agent->get_user_name()); @@ -688,7 +692,7 @@ namespace Slic3r if (!obj) continue; if (!elem["dev_id"].is_null()) - obj->set_dev_id(elem["dev_id"].get()); + obj->GetInfo()->SetDevId(elem["dev_id"].get()); if (!elem["dev_name"].is_null()) obj->set_dev_name(elem["dev_name"].get()); if (!elem["dev_online"].is_null()) @@ -710,8 +714,6 @@ namespace Slic3r } if (!elem["task_status"].is_null()) obj->iot_print_status = elem["task_status"].get(); - if (elem.contains("dev_product_name") && !elem["dev_product_name"].is_null()) - obj->dev_product_name = elem["dev_product_name"].get(); if (elem.contains("dev_access_code") && !elem["dev_access_code"].is_null()) { std::string acc_code = elem["dev_access_code"].get(); @@ -749,9 +751,10 @@ namespace Slic3r unsigned int http_code; std::string body; int result = m_agent->get_user_print_info(&http_code, &body); - if (result == 0) - { - parse_user_print_info(body); + if (result == 0) { + Slic3r::GUI::wxGetApp().CallAfter([this, body]() { + parse_user_print_info(body); + }); } } diff --git a/src/slic3r/GUI/DeviceCore/DevMapping.cpp b/src/slic3r/GUI/DeviceCore/DevMapping.cpp index 9dbdf3e..d3418bf 100644 --- a/src/slic3r/GUI/DeviceCore/DevMapping.cpp +++ b/src/slic3r/GUI/DeviceCore/DevMapping.cpp @@ -233,6 +233,13 @@ namespace Slic3r } BOOST_LOG_TRIVIAL(info) << "ams_mapping_distance:" << line;// Print the collected filaments +//y75 + auto toLower = [](std::string s) -> std::string { + std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); }); + return s; + }; +//y75 + //y71 for (int i = 0; i < filaments.size(); i++) { @@ -267,7 +274,8 @@ namespace Slic3r wxColour c = wxColour(filaments[i].color); wxColour tray_c = DevAmsTray::decode_color(box_filament_infos[j].color); val.distance = GUI::calc_color_distance(c, tray_c); - if (filaments[i].type != box_filament_infos[j].type) { + //y75 + if (toLower(filaments[i].type) != toLower(box_filament_infos[j].type)) { val.distance = 999999; val.is_type_match = false; } else { diff --git a/src/slic3r/GUI/DeviceCore/DevMappingNozzle.cpp b/src/slic3r/GUI/DeviceCore/DevMappingNozzle.cpp new file mode 100644 index 0000000..2e068a4 --- /dev/null +++ b/src/slic3r/GUI/DeviceCore/DevMappingNozzle.cpp @@ -0,0 +1,347 @@ +#include "DevMapping.h" + +#include "DevNozzleRack.h" +#include "DevNozzleSystem.h" + +#include "DevUtil.h" +#include "DevUtilBackend.h" + +#include "libslic3r/MultiNozzleUtils.hpp" +#include "libslic3r/Print.hpp" + +#include "slic3r/GUI/DeviceManager.hpp" +#include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/BackgroundSlicingProcess.hpp" + +#include "slic3r/GUI/GUI_App.hpp" + +#include +using namespace nlohmann; + +namespace Slic3r { + +static int s_get_physical_extruder_id(int total_ext_count, int logical_extruder_id) +{ + if (total_ext_count == 2) { + return logical_extruder_id == 1 ? 1 : 0; + } else if (total_ext_count == 1) { + return 0; + } + + return logical_extruder_id - 1; +} + +static std::string s_get_diameter_str(float diameter) +{ + return (boost::format("%.2f") % diameter).str(); +} + +static std::string s_get_diameter_str(const std::string& diameter) +{ + try { + float dia = boost::lexical_cast(diameter); + return s_get_diameter_str(dia); + } catch (...) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " failed to boost::lexical_cast: " << diameter; + return diameter; + } + + try { + float dia = std::stof(diameter); + return s_get_diameter_str(dia); + } catch (...) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " std::stof: " << diameter; + return diameter; + } +} + + +// get auto nozzle mapping through AP +// warnings: +// (1) the fila_id here is 1 based index +// (2) the AP wants the diameter string with 2 decimal places +int MachineObject::ctrl_get_auto_nozzle_mapping(Slic3r::GUI::Plater* plater, const std::vector& ams_mapping, int flow_cali_opt, int pa_value) +{ + m_auto_nozzle_mapping.Clear(); + if (!plater) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": plater is nullptr"; + return -1; + } + + GCodeProcessorResult* gcode_result = plater->background_process().get_current_gcode_result(); + if (!gcode_result) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": gcode_result is nullptr"; + return -1; + } + + const auto& result = gcode_result->nozzle_group_result; + if (!result) { + assert(false && "gcode_result->nozzle_group_result"); + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": gcode_result->nozzle_group_result is NULL"; + return -1; + } + + if (ams_mapping.empty()) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": the ams mapping is empty"; + return -1;// the ams mapping is empty + } + + json command_jj; + command_jj["print"]["command"] = "get_auto_nozzle_mapping"; + m_auto_nozzle_mapping.m_sequence_id = std::to_string(m_sequence_id++); + command_jj["print"]["sequence_id"] = m_auto_nozzle_mapping.m_sequence_id; + command_jj["print"]["calibration"] = flow_cali_opt; + command_jj["print"]["extrude_cali_manual_mode"] = pa_value; + + + // filament seq + json filament_seq_jj; + int max_fila_id = 0; + std::unordered_set filaid_set; + for (int idx = 0; idx < gcode_result->filament_change_sequence.size(); idx++) { + int fila_id = gcode_result->filament_change_sequence[idx]; + if (filaid_set.count(fila_id) == 0) { + filaid_set.insert(fila_id); + filament_seq_jj[fila_id + 1] = idx; + max_fila_id = std::max(max_fila_id, fila_id + 1); + } + } + + for (int fila_id = 0; fila_id <= max_fila_id; fila_id++) { + if (filament_seq_jj[fila_id].is_null()) { + filament_seq_jj[fila_id] = -1;// fill the used fila_id with -1 + } + } + + command_jj["print"]["filament_seq"] = filament_seq_jj; + + // ams mapping + std::vector ams_mapping_vec(33, 0xFFFF);/* AP ask to fill them*/ + for (auto item : ams_mapping) { + try { + int ams_id = stoi(item.ams_id); + int slot_id = !item.slot_id.empty() ? stoi(item.slot_id) : 0; + ams_mapping_vec[item.id + 1] = (ams_id << 8) | slot_id;/*using ams_id << 8 | slot_id*/ + } catch (std::exception& e) { + assert(false && "invalid ams_id or slot_id"); + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " exception: " << e.what(); + ams_mapping_vec[item.id + 1] = item.tray_id; + } + } + command_jj["print"]["ams_mapping"] = ams_mapping_vec; + + // filament info + json filament_info_jj; + for (int fila_id = 0; fila_id < ams_mapping.size(); fila_id++) { + const auto& fila = ams_mapping[fila_id]; + const auto& nozzle_info = result->get_nozzle_for_filament(fila.id); + if (!nozzle_info) { + assert(false && "nozzle_info should not be nullptr"); + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "nozzle_info should not be nullptr"; + continue; + } + + json fila_item_jj; + fila_item_jj["id"] = (fila.id + 1); + fila_item_jj["direction"] = (nozzle_info->extruder_id == LOGIC_L_EXTRUDER_ID ? 1 : 2); + fila_item_jj["group"] = nozzle_info->group_id; + fila_item_jj["nozzle_d"] = s_get_diameter_str(nozzle_info->diameter); + fila_item_jj["nozzle_v"] = (nozzle_info->volume_type == NozzleVolumeType::nvtHighFlow) ? "High Flow" : "Standard"; + fila_item_jj["cate"] = fila.filament_id; + fila_item_jj["color"] = fila.color; + filament_info_jj.push_back(fila_item_jj); + } + + command_jj["print"]["fila_info"] = filament_info_jj; + + // nozzle info + json nozzle_info_jj; + const auto& extruder_nozzles = m_nozzle_system->GetExtNozzles(); + for (const auto& nozzle : extruder_nozzles) { + if (nozzle.second.IsNormal()) { + json nozzle_item_jj; + nozzle_item_jj["pos"] = nozzle.second.GetNozzleId(); + if (m_nozzle_system->GetReplaceNozzleTar().has_value() && nozzle_item_jj["pos"] == MAIN_EXTRUDER_ID) { + nozzle_item_jj["pos"] = m_nozzle_system->GetReplaceNozzleTar().value();// special case of tar_id. see protocol definition + } + + nozzle_item_jj["nozzle_d"] = s_get_diameter_str(nozzle.second.GetNozzleDiameter()); + nozzle_item_jj["nozzle_v"] = (nozzle.second.GetNozzleFlowType() == NozzleFlowType::H_FLOW) ? "High Flow" : "Standard"; + nozzle_item_jj["wear"] = nozzle.second.GetNozzleWear(); + nozzle_item_jj["cate"] = nozzle.second.GetFilamentId(); + nozzle_item_jj["color"] = nozzle.second.GetFilamentColor(); + nozzle_info_jj.push_back(nozzle_item_jj); + } + } + + const auto& rack_nozzles = m_nozzle_system->GetNozzleRack()->GetRackNozzles(); + for (const auto& nozzle : rack_nozzles) { + if (nozzle.second.IsNormal()) { + json nozzle_item_jj; + nozzle_item_jj["pos"] = (nozzle.second.GetNozzleId() + 0x10); + if (m_nozzle_system->GetReplaceNozzleTar().has_value() && nozzle_item_jj["pos"] == MAIN_EXTRUDER_ID) { + nozzle_item_jj["pos"] = m_nozzle_system->GetReplaceNozzleTar().value();// special case of tar_id. see protocol definition + } + + nozzle_item_jj["nozzle_d"] = s_get_diameter_str(nozzle.second.GetNozzleDiameter()); + nozzle_item_jj["nozzle_v"] = (nozzle.second.GetNozzleFlowType() == NozzleFlowType::H_FLOW) ? "High Flow" : "Standard"; + nozzle_item_jj["wear"] = nozzle.second.GetNozzleWear(); + nozzle_item_jj["cate"] = nozzle.second.GetFilamentId(); + nozzle_item_jj["color"] = nozzle.second.GetFilamentColor(); + nozzle_info_jj.push_back(nozzle_item_jj); + } + } + + command_jj["print"]["nozzle_info"] = nozzle_info_jj; + return publish_json(command_jj); +} + + +void DevNozzleMappingResult::ParseAutoNozzleMapping(Slic3r::MachineObject* obj, const json& print_jj) +{ + if (print_jj.contains("command") && print_jj["command"].get() == "get_auto_nozzle_mapping") { + if (print_jj.contains("sequence_id") && print_jj["sequence_id"] == m_sequence_id) { + Clear(); + DevJsonValParser::ParseVal(print_jj, "result", m_result); + DevJsonValParser::ParseVal(print_jj, "reason", m_mqtt_reason); + DevJsonValParser::ParseVal(print_jj, "errno", m_errno); + DevJsonValParser::ParseVal(print_jj, "detail", m_detail_json); + DevJsonValParser::ParseVal(print_jj, "type", m_type); + + if (print_jj.contains("mapping")) { + m_nozzle_mapping_json = print_jj["mapping"]; + const auto& mapping = print_jj["mapping"].get>(); + for (int fila_id = 0; fila_id < mapping.size(); ++fila_id) { + m_nozzle_mapping[fila_id] = mapping[fila_id]; + } + } + + m_flush_weight_base = GetFlushWeight(obj); + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": get_auto_nozzle_mapping: " << m_result; + } + } +} + +void DevNozzleMappingResult::Clear() +{ + m_sequence_id.clear(); + m_result.clear(); + m_mqtt_reason.clear(); + m_type.clear(); + m_errno = 0; + m_detail_msg.clear(); + m_detail_json.clear(); + m_nozzle_mapping.clear(); + m_nozzle_mapping_json.clear(); + + m_flush_weight_base = -1; + m_flush_weight_current = -1; +} + + +void DevNozzleMappingResult::SetManualNozzleMapping(Slic3r::MachineObject* obj, int fila_id, int nozzle_pos_id) +{ + if (nozzle_pos_id == MAIN_EXTRUDER_ID && obj->GetNozzleSystem()->GetReplaceNozzleTar().has_value()){ + nozzle_pos_id = obj->GetNozzleSystem()->GetReplaceNozzleTar().value();// special case of tar_id. see protocol definition + } + + if (m_nozzle_mapping[fila_id] != nozzle_pos_id) { + m_nozzle_mapping[fila_id] = nozzle_pos_id; + m_flush_weight_current = GetFlushWeight(obj); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": fila_id=" << fila_id << ", nozzle_pos_id=" << nozzle_pos_id; + } + + try { + auto mapping = m_nozzle_mapping_json.get>(); + if (mapping.at(fila_id) != nozzle_pos_id) { + mapping[fila_id] = nozzle_pos_id; + m_nozzle_mapping_json = mapping; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": updated to " << m_nozzle_mapping_json.dump(); + } + } catch (std::exception& e) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": exception: " << e.what(); + }; +} + + +int DevNozzleMappingResult::GetMappedNozzlePosIdByFilaId(MachineObject* obj, int fila_id) const +{ + auto iter = m_nozzle_mapping.find(fila_id); + if (iter == m_nozzle_mapping.end()) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": not mapped for " << fila_id; + return -1;// the filament is not mapped by the machine + } + + if (obj->GetNozzleSystem()->GetReplaceNozzleTar().has_value() && iter->second == obj->GetNozzleSystem()->GetReplaceNozzleTar().value()) { + return 0;// 0 means right extueder + } + + return iter->second; +} + +float DevNozzleMappingResult::GetFlushWeight(Slic3r::MachineObject* obj) const +{ + auto plater = Slic3r::GUI::wxGetApp().plater(); + if (!plater) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": plater is nullptr"; + return -1; + } + + GCodeProcessorResult* gcode_result = plater->background_process().get_current_gcode_result(); + if (!gcode_result) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": gcode_result is nullptr"; + return -1; + } + + if (gcode_result->filament_change_sequence.empty()) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": filament_change_sequence is empty"; + return -1; + } + + if (!Slic3r::GUI::wxGetApp().preset_bundle) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": preset_bundle is nullptr"; + return -1; + }; + + const std::vector>>& flush_matrix = Slic3r::GUI::wxGetApp().preset_bundle->get_full_flush_matrix(); + + float total_flush_volume = 0; + MultiNozzleUtils::NozzleStatusRecorder recorder; + for (auto filament : gcode_result->filament_change_sequence) { + auto nozzle_pos = GetMappedNozzlePosIdByFilaId(obj, filament); + if (nozzle_pos == -1){ + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": nozzle_pos is -1 for fila_id=" << filament; + continue; + } + + auto nozzle_info = obj->GetNozzleSystem()->GetNozzleByPosId(nozzle_pos); + if (nozzle_info.IsEmpty()) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": nozzle_info IsEmpty for fila_id=" << filament << ", nozzle_pos=" << nozzle_pos; + continue; + } + + int extruder_id = nozzle_info.GetLogicExtruderId(); + int nozzle_id = nozzle_info.GetNozzlePosId(); + int last_filament = recorder.get_filament_in_nozzle(nozzle_id); + + if (last_filament != -1 && last_filament != filament) { + if (flush_matrix.size() > extruder_id && + flush_matrix[extruder_id].size() > last_filament && + flush_matrix[extruder_id][last_filament].size() > filament){ + total_flush_volume += flush_matrix[extruder_id][last_filament][filament]; + } + else{ + assert(false && "missing flush volume"); + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": missing flush volume for extruder_id=" << extruder_id + << ", last_filament=" << last_filament << ", filament=" << filament; + } + } + + recorder.set_nozzle_status(nozzle_id, filament); + } + + // estimate the flush weight: total_flush_volume * 1.26 * 0.001 + return total_flush_volume * 1.26 * 0.001; +} + +} // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceCore/DevNozzleRack.cpp b/src/slic3r/GUI/DeviceCore/DevNozzleRack.cpp new file mode 100644 index 0000000..de372f1 --- /dev/null +++ b/src/slic3r/GUI/DeviceCore/DevNozzleRack.cpp @@ -0,0 +1,112 @@ +#include "DevNozzleRack.h" +#include "DevUtil.h" + +#include "slic3r/GUI/DeviceManager.hpp" + +wxDEFINE_EVENT(DEV_RACK_EVENT_READING_FINISHED, wxCommandEvent); +namespace Slic3r +{ + +DevNozzleRack::DevNozzleRack(DevNozzleSystem* nozzle_system) + : wxEvtHandler(), m_nozzle_system(nozzle_system) +{ + +} + + +void DevNozzleRack::Reset() +{ + m_position = RACK_POS_UNKNOWN; + m_status = RACK_STATUS_UNKNOWN; + m_rack_nozzles.clear(); +} + +void DevNozzleRack::SendReadingFinished() +{ + wxCommandEvent evt(DEV_RACK_EVENT_READING_FINISHED); + evt.SetEventObject(this); + wxPostEvent(this, evt); +} + +DevNozzle DevNozzleRack::GetNozzle(int idx) const +{ + auto iter = m_rack_nozzles.find(idx); + if (iter == m_rack_nozzles.end()) { + DevNozzle nozzle; + nozzle.SetOnRack(true); + return nozzle; + } + + return iter->second; +} + +DevFirmwareVersionInfo DevNozzleRack::GetNozzleFirmwareInfo(int nozzle_id) const +{ + auto iter = m_rack_nozzles_firmware.find(nozzle_id); + return iter != m_rack_nozzles_firmware.end() ? iter->second : DevFirmwareVersionInfo(); +} + + +bool DevNozzleRack::HasUnreliableNozzles() const +{ + for (const auto& nozzle : m_rack_nozzles) + { + if (!nozzle.second.IsInfoReliable()) + { + return true; + } + } + return false; +} + +bool DevNozzleRack::HasUnknownNozzles() const +{ + for (const auto& nozzle : m_rack_nozzles) + { + if (nozzle.second.IsUnknown()) + { + return true; + } + } + return false; +} + +int DevNozzleRack::GetKnownNozzleCount() const +{ + int count = 0; + for (const auto& nozzle : m_rack_nozzles) + { + if (!nozzle.second.IsEmpty() && !nozzle.second.IsUnknown()) + { + count++; + } + } + + return count; +} + +void DevNozzleRack::ParseRackInfo(const nlohmann::json& rack_info) +{ + ParseRackInfoV1_0(rack_info); +} + +void DevNozzleRack::ParseRackInfoV1_0(const nlohmann::json& rack_info) +{ + DevJsonValParser::ParseVal(rack_info, "stat", m_status, RACK_STATUS_UNKNOWN); + if (m_status < RACK_STATUS_UNKNOWN || m_status >= RACK_STATUS_END) + { + m_status = RACK_STATUS_UNKNOWN; // Reset to default if out of range + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": Invalid rack status: " << m_status << ", reset"; + } + + DevJsonValParser::ParseVal(rack_info, "pos", m_position, RACK_POS_UNKNOWN); + if (m_position < RACK_POS_UNKNOWN || m_position >= RACK_POS_END) + { + m_position = RACK_POS_UNKNOWN; // Reset to default if out of range + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": Invalid rack position: " << m_position << ", reset"; + } + + DevJsonValParser::ParseVal(rack_info, "info", m_cali_status); +} + +}; \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceCore/DevNozzleRack.h b/src/slic3r/GUI/DeviceCore/DevNozzleRack.h new file mode 100644 index 0000000..20bf51f --- /dev/null +++ b/src/slic3r/GUI/DeviceCore/DevNozzleRack.h @@ -0,0 +1,134 @@ +#pragma once +#include "libslic3r/CommonDefs.hpp" +#include "libslic3r/MultiNozzleUtils.hpp" +#include "slic3r/Utils/json_diff.hpp" + +#include "DevNozzleSystem.h" +#include "DevFirmware.h" + +#include + +// wx +#include +#include + +wxDECLARE_EVENT(DEV_RACK_EVENT_READING_FINISHED, wxCommandEvent); + +namespace Slic3r +{ + // Previous definitions +class DevNozzleSystem; + +class DevNozzleRack: public wxEvtHandler +{ +public: + enum RackStatus : int + { + RACK_STATUS_UNKNOWN = -1, + RACK_STATUS_IDLE = 0, + RACK_STATUS_HOTEND_CENTRE = 1, + RACK_STATUS_TOOLHEAD_CENTRE = 2, + RACK_STATUS_CALIBRATE_HOTEND_RACK = 3, + RACK_STATUS_CUT_MATERIAL = 4, + RACK_STATUS_UNLOCK_HOTEND = 5, + RACK_STATUS_LIFT_HOTEND_RACK = 6, + RACK_STATUS_PLACE_HOTEND = 7, + RACK_STATUS_PICK_HOTEND = 8, + RACK_STATUS_LOCK_HOTEND = 9, + RACK_STATUS_END, + }; + + enum RackPos : int + { + RACK_POS_UNKNOWN = 0, + RACK_POS_A_TOP = 1, + RACK_POS_B_TOP = 2, + RACK_POS_CENTRE = 3, + RACK_POS_END, + }; + + enum RackCaliStatus + { + Rack_CALI_UNKNOWN = -1, + Rack_CALI_NOT = 0, + Rack_CALI_OK = 1, + }; + +public: + DevNozzleRack(DevNozzleSystem* nozzle_system); + ~DevNozzleRack() = default; + +public: + // Is supported by the printer + bool IsSupported() const { return m_is_supported; }; + void SetSupported(bool supported) { m_is_supported = supported; } + + // getters + DevNozzleSystem* GetNozzleSystem() const { return m_nozzle_system; } + + RackPos GetPosition() const { return m_position; } + RackStatus GetStatus() const { return m_status; } + RackCaliStatus GetCaliStatus() const { return m_cali_status;} + + DevNozzle GetNozzle(int idx) const; + const std::map& GetRackNozzles() const { return m_rack_nozzles; } + + // status + bool HasUnreliableNozzles() const; + bool HasUnknownNozzles() const; + int GetKnownNozzleCount() const; + + // refreshing + int GetReadingIdx() const { return m_nozzle_system->GetReadingIdx(); } + int GetReadingCount() const { return m_nozzle_system->GetReadingCount(); } + void SendReadingFinished(); + + // firmware + void AddNozzleFirmwareInfo(int nozzle_id, const DevFirmwareVersionInfo& info) { m_rack_nozzles_firmware[nozzle_id] = info; } + void ClearNozzleFirmwareInfo() { m_rack_nozzles_firmware.clear(); } + DevFirmwareVersionInfo GetNozzleFirmwareInfo(int nozzle_id) const; + + // setters + void Reset(); + void AddRackNozzle(DevNozzle& nozzle) { nozzle.SetOnRack(true); m_rack_nozzles[nozzle.m_nozzle_id] = nozzle; }; + void ClearRackNozzles() { m_rack_nozzles.clear(); } + +public: + void ParseRackInfo(const nlohmann::json& rack_info); + + void CtrlRackPosMove(RackPos new_pos) const; + void CtrlRackPosGoHome() const; + + void CtrlRackConfirmNozzle(int rack_nozzle_id) const; + void CtrlRackConfirmAll() const; + + void CrtlRackReadNozzle(int rack_nozzle_id) const; + void CtrlRackReadAll(bool gui_check = false) const; + bool CtrlCanReadAll() const; + + // the upgrade is not supported + // the GUI interface is removed in STUDIO-14506 + int CtrlRackUpgradeExtruderNozzle() const; + int CtrlRackUpgradeRackNozzle(int rack_nozzle_id) const;; + int CtrlRackUpgradeAll() const;; + bool CtrlCanUpdateAll() const; + +private: + void ParseRackInfoV1_0(const nlohmann::json& rack_info); + + int CtrlRackUpgrade(const std::string& module_str) const; + + bool CheckRackMoveWarningDlg() const; + +private: + DevNozzleSystem* m_nozzle_system = nullptr; + + bool m_is_supported = false; // Indicates if the nozzle rack is supported by the printer + RackPos m_position = RACK_POS_UNKNOWN; + RackStatus m_status = RACK_STATUS_UNKNOWN; + RackCaliStatus m_cali_status = Rack_CALI_UNKNOWN; + + std::map m_rack_nozzles; // Map of nozzle ID to DevNozzle objects + std::map m_rack_nozzles_firmware; +}; +}; \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceCore/DevNozzleRackCtrl.cpp b/src/slic3r/GUI/DeviceCore/DevNozzleRackCtrl.cpp new file mode 100644 index 0000000..cb0272d --- /dev/null +++ b/src/slic3r/GUI/DeviceCore/DevNozzleRackCtrl.cpp @@ -0,0 +1,283 @@ +#include "DevNozzleRack.h" +#include "DevUtil.h" +#include "DevExtruderSystem.h" + +#include "slic3r/GUI/DeviceManager.hpp" +#include "slic3r/GUI/MsgDialog.hpp" +#include "slic3r/GUI/I18N.hpp" + +#include + + +namespace Slic3r +{ + +void DevNozzleRack::CtrlRackPosMove(RackPos new_pos) const +{ + if (!CheckRackMoveWarningDlg()) + { + return; + } + + int action_id = -1; + if (new_pos == RACK_POS_A_TOP) + { + action_id = 1; + } + else if(new_pos == RACK_POS_B_TOP) + { + action_id = 2; + } + + if (action_id != -1) + { + json j; + j["print"]["command"] = "nozzle_holder_ctrl"; + j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); + j["print"]["action"] = action_id; + m_nozzle_system->GetOwner()->publish_json(j); + } +} + + +void DevNozzleRack::CtrlRackPosGoHome() const +{ + if (!CheckRackMoveWarningDlg()) + { + return; + } + + json j; + j["print"]["command"] = "nozzle_holder_ctrl"; + j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); + j["print"]["action"] = 0; + m_nozzle_system->GetOwner()->publish_json(j); +} + +bool DevNozzleRack::CheckRackMoveWarningDlg() const +{ + if (wxThread::IsMain()) + { + static bool s_show_move_warning = true; + if (s_show_move_warning) + { + Slic3r::GUI::MessageDialog dlg(nullptr, _L("The toolhead and hotend rack may move. Please keep your hands away from the chamber."), + _L("Warning"), wxICON_WARNING | wxOK); + dlg.show_dsa_button(); + if (dlg.ShowModal() != wxID_OK) + { + s_show_move_warning = !dlg.get_checkbox_state(); + return false; + } + + s_show_move_warning = !dlg.get_checkbox_state(); + } + } + + return true; +} + +void DevNozzleRack::CtrlRackConfirmNozzle(int rack_nozzle_id) const +{ + json j; + j["print"]["command"] = "nozzle_info_confirm"; + j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); + j["print"]["id"] = (rack_nozzle_id + 16); // from 0x16 + m_nozzle_system->GetOwner()->publish_json(j); +} + +void DevNozzleRack::CtrlRackConfirmAll() const +{ + json j; + j["print"]["command"] = "nozzle_info_confirm"; + j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); + j["print"]["id"] = 0xff; + m_nozzle_system->GetOwner()->publish_json(j); +} + +void DevNozzleRack::CrtlRackReadNozzle(int rack_nozzle_id) const +{ + json j; + j["print"]["command"] = "holder_nozzle_refresh"; + j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); + j["print"]["id"] = (rack_nozzle_id + 16); // from 0x16 + m_nozzle_system->GetOwner()->publish_json(j); +} + + +bool DevNozzleRack::CtrlCanReadAll() const +{ + //is in print + if (m_nozzle_system->GetOwner()->is_in_printing()) + { + return false; + } + + if (m_nozzle_system->GetOwner()->is_in_upgrading()) { + return false; + } + + //if have nozzle + bool has_nozzle_on_rack = false; + for (const auto &nozzle_pair : m_rack_nozzles) + { + if (!nozzle_pair.second.IsEmpty()) + { + has_nozzle_on_rack = true; + break; + } + } + + bool has_nozzle_on_ext = false; + if (m_nozzle_system->ContainsExtNozzle(MAIN_EXTRUDER_ID)) { + has_nozzle_on_ext = true; + } + + if (!has_nozzle_on_rack && !has_nozzle_on_ext) { + return false; + } + + //if is in loading + if (m_nozzle_system->GetOwner()->ams_status_main == AMS_STATUS_MAIN_FILAMENT_CHANGE) + { + return false; + } + + auto ext = m_nozzle_system->GetOwner()->GetExtderSystem(); + if (ext && ext->IsBusyLoading()) return false; + + if (GetReadingCount() > 0) + { + return false; + } + + return m_status == RACK_STATUS_IDLE; +} + +void DevNozzleRack::CtrlRackReadAll(bool gui_check) const +{ + if (gui_check && wxThread::IsMain()) + { +#if 0 + if (!HasUnknownNozzles()) { + Slic3r::GUI::MessageDialog dlg(nullptr, _L("Hotend information may be inaccurate. " + "Would you like to re-read the hotend? (Hotend information may change during power-off)."), + _L("Warning"), wxICON_WARNING | wxOK | wxYES); + dlg.SetButtonLabel(wxID_OK, _L("I confirm all")); + dlg.SetButtonLabel(wxID_YES, _L("Re-read all")); + + int rtn = dlg.ShowModal(); + if (rtn == wxID_OK) { + CtrlRackConfirmAll(); + } else if (rtn == wxID_YES) { + if (CheckRackMoveWarningDlg()) { + CtrlRackReadAll(false); + } + } + + return; + } +#endif + + if (CheckRackMoveWarningDlg()) { + CtrlRackReadAll(false); + } + + return; + } + + json j; + j["print"]["command"] = "holder_nozzle_refresh"; + j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); + j["print"]["id"] = 0xff; + m_nozzle_system->GetOwner()->publish_json(j); +} + + +bool DevNozzleRack::CtrlCanUpdateAll() const +{ + if (m_nozzle_system->GetOwner()->is_in_printing()) + { + return false; + } + + auto ext_nozzle = m_nozzle_system->GetExtNozzle(MAIN_EXTRUDER_ID); + if (ext_nozzle.IsAbnormal()) + { + return true; + } + + auto ext_nozzle_firmware = m_nozzle_system->GetExtruderNozzleFirmware(); + if (!ext_nozzle_firmware.sw_new_ver.empty() && + ext_nozzle_firmware.sw_new_ver != ext_nozzle_firmware.sw_ver) + { + return true; + } + + for (auto val : m_rack_nozzles_firmware) + { + const auto& firmware_info = val.second; + if (!firmware_info.sw_new_ver.empty() && firmware_info.sw_new_ver != firmware_info.sw_ver) + { + return true; + } + else if (GetNozzle(val.first).IsAbnormal()) + { + return true; + } + } + + return false; +} + +int DevNozzleRack::CtrlRackUpgradeExtruderNozzle() const +{ + return CtrlRackUpgrade("wtm"); +} + +int DevNozzleRack::CtrlRackUpgradeRackNozzle(int rack_nozzle_id) const +{ + return CtrlRackUpgrade("wtm/" + std::to_string(0x10 + rack_nozzle_id)); +} + +int DevNozzleRack::CtrlRackUpgradeAll() const +{ + return CtrlRackUpgrade("wtm_all"); +} + +int DevNozzleRack::CtrlRackUpgrade(const std::string& module_str) const +{ + if (wxThread::IsMain()) + { + if (GetReadingCount() > 0) + { + Slic3r::GUI::MessageDialog dlg(nullptr, _L("Reading the hotends, please wait."), _L("Warning"), wxICON_WARNING | wxOK); + dlg.ShowModal(); + return -1; + } + + static bool s_show_upgrade_warning = true; + if (s_show_upgrade_warning) + { + Slic3r::GUI::MessageDialog dlg(nullptr, _L("During the hotend upgrade, the toolhead will move. Don't reach into the chamber."), + _L("Warning"), wxICON_WARNING | wxOK | wxCANCEL); + dlg.show_dsa_button(); + dlg.SetButtonLabel(wxID_OK, _L("Update")); + int rtn = dlg.ShowModal(); + s_show_upgrade_warning = !dlg.get_checkbox_state(); + if (rtn != wxID_OK) + { + return -1; + } + } + } + + json j; + j["upgrade"]["command"] = "wtm_upgrade"; + j["upgrade"]["module"] = module_str; + j["upgrade"]["src_id"] = 1; + j["upgrade"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); + return m_nozzle_system->GetOwner()->publish_json(j); +} + +}; \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceCore/DevNozzleSystem.cpp b/src/slic3r/GUI/DeviceCore/DevNozzleSystem.cpp index 2d4d0af..da87e40 100644 --- a/src/slic3r/GUI/DeviceCore/DevNozzleSystem.cpp +++ b/src/slic3r/GUI/DeviceCore/DevNozzleSystem.cpp @@ -1,35 +1,467 @@ #include "DevExtruderSystem.h" + +#include "DevNozzleRack.h" #include "DevNozzleSystem.h" #include "DevUtil.h" #include "slic3r/GUI/DeviceManager.hpp" +#include "slic3r/GUI/I18N.hpp" + namespace Slic3r { -DevNozzle DevNozzleSystem::GetNozzle(int id) const +wxString DevNozzle::GetNozzleFlowTypeStr() const { - if (m_nozzles.find(id) != m_nozzles.end()) + return GetNozzleFlowTypeStr(m_nozzle_flow); +} + +wxString DevNozzle::GetNozzleFlowTypeStr(NozzleFlowType type) +{ + switch (type) { + case NozzleFlowType::H_FLOW: return _L("High Flow"); + case NozzleFlowType::S_FLOW: return _L("Standard"); + default: break; + } + + return _L("Unknown"); +} + +wxString DevNozzle::GetNozzleFlowTypeCaliStyleStr() const +{ + switch (m_nozzle_flow) { - return m_nozzles.at(id); + case NozzleFlowType::H_FLOW: return _L("High Flow"); + case NozzleFlowType::S_FLOW: return _L("Standard"); + default: break; + } + + return _L("Unknown"); +} + +bool DevNozzle::IsInfoReliable() const +{ + if (IsEmpty()) { return false;} + return DevUtil::get_flag_bits(m_stat, 0, 1) == 0; +} + +bool DevNozzle::IsNormal() const +{ + if (IsEmpty()) { return false;} + return DevUtil::get_flag_bits(m_stat, 1, 2) == 0; +} + +bool DevNozzle::IsAbnormal() const +{ + return DevUtil::get_flag_bits(m_stat, 1, 2) == (1 << 0); +} + +bool DevNozzle::IsUnknown() const +{ + return DevUtil::get_flag_bits(m_stat, 1, 2) == (1 << 1); +} + +int DevNozzle::GetNozzlePosId() const +{ + return IsOnRack() ? (m_nozzle_id + 0x10) : m_nozzle_id; +} + +wxString DevNozzle::GetDisplayId() const +{ + return wxString::Format("%d", m_nozzle_id + 1); +} + +wxString DevNozzle::GetNozzleTypeStr() const +{ + return GetNozzleTypeStr(m_nozzle_type); +} + +wxString DevNozzle::GetNozzleTypeStr(NozzleType type) +{ + switch (type) { + case Slic3r::ntHardenedSteel: return _L("Hardened Steel"); + case Slic3r::ntStainlessSteel: return _L("Stainless Steel"); + case Slic3r::ntTungstenCarbide: return _L("Tungsten Carbide"); + default: break; + } + + return _L("Unknown"); +} + +NozzleDiameterType DevNozzle::GetNozzleDiameterType() const +{ + if(GetNozzleDiameterStr() == wxString("0.2 mm")) + return NozzleDiameterType::NOZZLE_DIAMETER_0_2; + else if(GetNozzleDiameterStr() == wxString("0.4 mm")) + return NozzleDiameterType::NOZZLE_DIAMETER_0_4; + else if(GetNozzleDiameterStr() == wxString("0.6 mm")) + return NozzleDiameterType::NOZZLE_DIAMETER_0_6; + else if(GetNozzleDiameterStr() == wxString("0.8 mm")) + return NozzleDiameterType::NOZZLE_DIAMETER_0_8; + else + return NozzleDiameterType::NONE_DIAMETER_TYPE; +} + + +DevFirmwareVersionInfo DevNozzle::GetFirmwareInfo() const +{ + const auto& nozzle_rack = m_nozzle_rack.lock(); + if (nozzle_rack) + { + if (IsOnRack()) + { + return nozzle_rack->GetNozzleFirmwareInfo(m_nozzle_id); + } + else + { + if (m_nozzle_id == 0) + { + return nozzle_rack->GetNozzleSystem()->GetExtruderNozzleFirmware(); + } + } + } + + return DevFirmwareVersionInfo(); +} + +int DevNozzle::GetLogicExtruderId() const +{ + int total_ext_count = GetTotalExtruderCount(); + if (total_ext_count == 1) { + return LOGIC_UNIQUE_EXTRUDER_ID; + } else if(total_ext_count == 2) { + if (AtLeftExtruder()) { + return LOGIC_L_EXTRUDER_ID; + } else if (AtRightExtruder()) { + return LOGIC_R_EXTRUDER_ID; + } + } + + assert(0); + return LOGIC_UNIQUE_EXTRUDER_ID; +} + +bool DevNozzle::AtLeftExtruder() const +{ + assert(GetTotalExtruderCount() == 2); + if (IsOnRack()) { return false; } + return m_nozzle_id == DEPUTY_EXTRUDER_ID; +} + +bool DevNozzle::AtRightExtruder() const +{ + assert(GetTotalExtruderCount() == 2); + if (IsOnRack()) { return true; } + return m_nozzle_id == MAIN_EXTRUDER_ID; +} + +int DevNozzle::GetTotalExtruderCount() const +{ + auto rack = m_nozzle_rack.lock(); + if (rack){ + MachineObject* obj = rack->GetNozzleSystem()->GetOwner(); + return obj->GetExtderSystem()->GetTotalExtderCount(); + } + return 1; +} + +DevNozzleSystem::DevNozzleSystem(MachineObject* owner) + : m_owner(owner), m_nozzle_rack(std::make_shared (this)) +{ +} + +DevNozzle DevNozzleSystem::GetExtNozzle(int id) const +{ + if (m_ext_nozzles.find(id) != m_ext_nozzles.end()) + { + return m_ext_nozzles.at(id); } return DevNozzle(); } -void DevNozzleSystem::Reset() +std::string DevNozzle::GetNozzleFlowTypeString(NozzleFlowType type) { - m_nozzles.clear(); - m_extder_exist = 0; - m_state = 0; // idle state + switch (type) { + case NozzleFlowType::H_FLOW: return "High Flow"; + case NozzleFlowType::S_FLOW: return "Standard"; + default: return "Unknown"; + } } +std::string DevNozzle::GetNozzleTypeString(NozzleType type) +{ + switch (type) { + case Slic3r::ntHardenedSteel: return "Hardened Steel"; + case Slic3r::ntStainlessSteel: return "Stainless Steel"; + case Slic3r::ntTungstenCarbide: return "Tungsten Carbide"; + case Slic3r::ntBrass: return "Brass"; + default: return "Unknown"; + } +} + +Slic3r::DevNozzle DevNozzleSystem::GetRackNozzle(int idx) const +{ + return m_nozzle_rack->GetNozzle(idx); +} + +const std::map& DevNozzleSystem::GetRackNozzles() const +{ + return m_nozzle_rack->GetRackNozzles(); +} + +const std::vector DevNozzleSystem::CollectNozzles(int ext_loc, NozzleFlowType flow_type, float diameter) const +{ + auto s_match = [&](const DevNozzle& nozzle) -> bool { + if (nozzle.IsEmpty() || nozzle.IsAbnormal()) { + return false; + } + + if (diameter >= 0.0f && nozzle.m_diameter != diameter) { + return false; + } + + if (m_owner->is_nozzle_flow_type_supported() && flow_type != nozzle.GetNozzleFlowType()) { + return false; + } + + return true; + }; + + std::vector result; + + auto ext_nozzle = GetExtNozzle(ext_loc); + if (s_match(ext_nozzle)){ + result.push_back(ext_nozzle); + } + + if (ext_loc == MAIN_EXTRUDER_ID) { + auto rack_nozzles = m_nozzle_rack->GetRackNozzles(); + for (auto rack_nozzle : rack_nozzles){ + if (s_match(rack_nozzle.second)) { + result.push_back(rack_nozzle.second); + } + } + } + + return result; +} + +std::vector DevNozzleSystem::GetNozzleGroups() const +{ + std::vector nozzle_groups; + + auto nozzle_in_extruder = this->GetExtNozzles(); + for (auto& elem : nozzle_in_extruder) { + auto& nozzle = elem.second; + MultiNozzleUtils::NozzleGroupInfo info; + info.extruder_id = nozzle.GetLogicExtruderId(); + info.diameter = format_diameter_to_str(nozzle.m_diameter); + info.volume_type = nozzle.m_nozzle_flow == NozzleFlowType::H_FLOW ? NozzleVolumeType::nvtHighFlow : NozzleVolumeType::nvtStandard; + info.nozzle_count = 1; + nozzle_groups.emplace_back(std::move(info)); + } + + auto nozzle_rack = this->GetNozzleRack(); + if (!nozzle_rack) + return nozzle_groups; + + auto nozzle_in_rack = nozzle_rack->GetRackNozzles(); // nozzles in rack + for (auto& elem : nozzle_in_rack) { + auto& nozzle = elem.second; + if (nozzle.IsUnknown() || nozzle.IsAbnormal()) + continue; + int extruder_id = nozzle.GetLogicExtruderId(); + std::string diameter = format_diameter_to_str(nozzle.m_diameter); + NozzleVolumeType volume_type = nozzle.m_nozzle_flow == NozzleFlowType::H_FLOW ? NozzleVolumeType::nvtHighFlow : NozzleVolumeType::nvtStandard; + + bool found = false; + for (auto& group : nozzle_groups) { + if (group.extruder_id == extruder_id && + group.volume_type == volume_type && + group.diameter == diameter) { + found = true; + group.nozzle_count += 1; + break; + } + } + if (!found) + nozzle_groups.emplace_back(diameter, volume_type, extruder_id, 1); + } + return nozzle_groups; +} + +void DevNozzleSystem::Reset() +{ + m_ext_nozzles.clear(); + m_extder_exist = 0; + m_state_0_4 = 0; + m_reading_idx = 0; + m_reading_count = 0; + + m_replace_nozzle_src = std::nullopt; + m_replace_nozzle_tar = std::nullopt; + + m_nozzle_rack->Reset(); +} + +void DevNozzleSystem::ClearNozzles() +{ + m_ext_nozzles.clear(); + m_nozzle_rack->ClearRackNozzles(); +} + +void DevNozzleSystem::AddFirmwareInfoWTM(const DevFirmwareVersionInfo& info) +{ + static const std::string s_wtm_prefix = "wtm/"; + auto pos = info.name.find(s_wtm_prefix); + if (pos == string::npos) + { + m_ext_nozzle_firmware_info = info; + } + else + { + try + { + auto str = info.name.substr(s_wtm_prefix.size()); // remove "wtm/" prefix + int rack_nozzle_id = std::stoi(str) - 0x10; // rack nozzle IDs start from 0x10 + m_nozzle_rack->AddNozzleFirmwareInfo(rack_nozzle_id, info); + } + catch (const std::exception& e) + { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": Invalid nozzle ID in firmware name: " + << info.name << ", error: " << e.what(); + } + } +} + +void DevNozzleSystem::ClearFirmwareInfoWTM() +{ + m_ext_nozzle_firmware_info = DevFirmwareVersionInfo(); + m_nozzle_rack->ClearNozzleFirmwareInfo(); +} + +void DevNozzleSystem::SetSupportNozzleRack(bool supported) +{ + m_nozzle_rack->SetSupported(supported); +} + +bool DevNozzleSystem::HasUnreliableNozzles() const +{ + for (auto nozzle : m_ext_nozzles) { + if (!nozzle.second.IsInfoReliable()) { + return true; + } + } + + if (m_nozzle_rack->HasUnreliableNozzles()) { + return true; + } + + return false; +} + +bool DevNozzleSystem::HasUnknownNozzles() const +{ + for (auto nozzle : m_ext_nozzles) { + if (nozzle.second.IsUnknown()) { + return true; + } + } + + if (m_nozzle_rack->HasUnknownNozzles()) { + return true; + } + + return false; +} + +int DevNozzleSystem::GetKnownNozzleCountOn(int ext_id) const +{ + int count = 0; + if (ext_id == MAIN_EXTRUDER_ID) { + count = m_nozzle_rack->GetKnownNozzleCount(); + } + + if (!GetExtNozzle(ext_id).IsUnknown() && !GetExtNozzle(ext_id).IsEmpty()) { + count++; + } + + return count; +} + +ExtruderNozzleInfos DevNozzleSystem::GetExtruderNozzleInfo() const +{ + ExtruderNozzleInfos result; + + // left + { + std::unordered_map installed_nozzle_map_l; + const auto& left_nozzle = GetExtNozzle(DEPUTY_EXTRUDER_ID); + if (!left_nozzle.IsEmpty() && !left_nozzle.IsAbnormal()) { + NozzleDef data; + data.nozzle_diameter = left_nozzle.GetNozzleDiameter(); + data.nozzle_flow_type = left_nozzle.GetNozzleFlowType(); + installed_nozzle_map_l[data]++; + } + + result[DEPUTY_EXTRUDER_ID] = installed_nozzle_map_l; + } + + // right + { + std::unordered_map installed_nozzle_map_r; + const auto& r_nozzle = GetExtNozzle(MAIN_EXTRUDER_ID); + if (!r_nozzle.IsEmpty() && !r_nozzle.IsAbnormal()) { + NozzleDef data; + data.nozzle_diameter = r_nozzle.GetNozzleDiameter(); + data.nozzle_flow_type = r_nozzle.GetNozzleFlowType(); + installed_nozzle_map_r[data]++; + } + + auto rack_nozzles = m_nozzle_rack->GetRackNozzles(); + for (auto rack_nozzle : rack_nozzles) { + if (!rack_nozzle.second.IsEmpty() && !rack_nozzle.second.IsAbnormal()) { + NozzleDef data; + data.nozzle_diameter = rack_nozzle.second.GetNozzleDiameter(); + data.nozzle_flow_type = rack_nozzle.second.GetNozzleFlowType(); + installed_nozzle_map_r[data]++; + } + } + + result[MAIN_EXTRUDER_ID] = installed_nozzle_map_r; + } + + return result; +} + +bool DevNozzleSystem::IsRackMaximumInstalled() const +{ + auto ext_nozzle = GetExtNozzle(MAIN_EXTRUDER_ID); + if (ext_nozzle.IsEmpty()) { + return false; + } + + const auto& rack_nozzles = m_nozzle_rack->GetRackNozzles(); + if (rack_nozzles.size() < 6) { + return false; + } + + for (auto item : rack_nozzles) { + if (item.second.IsEmpty()) { + return false; + } + } + + return true; +}; static unordered_map _str2_nozzle_flow_type = { {"S", NozzleFlowType::S_FLOW}, {"H", NozzleFlowType::H_FLOW}, {"A", NozzleFlowType::S_FLOW}, - {"X", NozzleFlowType::S_FLOW} + {"X", NozzleFlowType::S_FLOW}, + {"E", NozzleFlowType::H_FLOW} }; static unordered_map _str2_nozzle_type = { @@ -57,6 +489,11 @@ static void s_parse_nozzle_type(const std::string& nozzle_type_str, DevNozzle& n nozzle.m_nozzle_type = _str2_nozzle_type[type_str]; } } + else if (nozzle_type_str == "N/A") + { + nozzle.m_nozzle_type = NozzleType::ntUndefine; + nozzle.m_nozzle_flow = NozzleFlowType::NONE_FLOWTYPE; + } } @@ -68,6 +505,7 @@ void DevNozzleSystemParser::ParseV1_0(const nlohmann::json& nozzletype_json, //Since both the old and new protocols push data. // assert(system->m_nozzles.size() < 2); DevNozzle nozzle; + nozzle.SetRack(system->GetNozzleRack()); nozzle.m_nozzle_id = 0; nozzle.m_nozzle_flow = NozzleFlowType::S_FLOW; // default flow type @@ -103,41 +541,92 @@ void DevNozzleSystemParser::ParseV1_0(const nlohmann::json& nozzletype_json, if (flag_e3d.has_value()) { // 0: QDT S_FLOW; 1:E3D H_FLOW (only P) if (flag_e3d.value() == 1) { - // note: E3D = E3D nozzle type + High Flow nozzle.m_nozzle_flow = NozzleFlowType::H_FLOW; - nozzle.m_nozzle_type = NozzleType::ntE3D; } else { nozzle.m_nozzle_flow = NozzleFlowType::S_FLOW; } } } - system->m_nozzles[nozzle.m_nozzle_id] = nozzle; + system->m_ext_nozzles[nozzle.m_nozzle_id] = nozzle; } -void DevNozzleSystemParser::ParseV2_0(const json& nozzle_json, DevNozzleSystem* system) +void DevNozzleSystemParser::ParseV2_0(const json& device_json, DevNozzleSystem* system) { - system->Reset(); - - if (nozzle_json.contains("exist")) + if (device_json.contains("nozzle")) { - system->m_extder_exist = DevUtil::get_flag_bits(nozzle_json["exist"].get(), 0, 16); + const json& nozzle_json = device_json["nozzle"]; + if (nozzle_json.contains("exist")) + { + system->m_extder_exist = DevUtil::get_flag_bits(nozzle_json["exist"].get(), 0, 16); + } + + if (nozzle_json.contains("state")) + { + int val = nozzle_json["state"].get(); + system->m_state_0_4 = DevUtil::get_flag_bits(nozzle_json["state"].get(), 0, 4); + int new_reading_idx = DevUtil::get_flag_bits(nozzle_json["state"].get(), 8, 4); + int new_reading_count = DevUtil::get_flag_bits(nozzle_json["state"].get(), 4, 4); + if (system->m_reading_count != new_reading_count || system->m_reading_idx != new_reading_idx) + { + system->m_reading_idx = new_reading_idx; + system->m_reading_count = new_reading_count; + if (system->m_reading_count == 0) + { + system->m_nozzle_rack->SendReadingFinished(); + } + } + } + + if (nozzle_json.contains("src_id")) + { + system->m_replace_nozzle_src = std::make_optional(nozzle_json["src_id"].get()); + } + + if (nozzle_json.contains("tar_id")) + { + system->m_replace_nozzle_tar = std::make_optional(nozzle_json["tar_id"].get()); + } + + system->ClearNozzles(); + for (auto it = nozzle_json["info"].begin(); it != nozzle_json["info"].end(); it++) + { + DevNozzle nozzle_obj; + nozzle_obj.SetRack(system->GetNozzleRack()); + + const auto& njon = it.value(); + nozzle_obj.m_nozzle_id = DevUtil::get_hex_bits(njon["id"].get(), 0); + nozzle_obj.m_diameter = njon["diameter"].get(); + s_parse_nozzle_type(njon["type"].get(), nozzle_obj); + if (njon.contains("stat"))/*maybe not contains*/ + { + nozzle_obj.SetStatus(njon["stat"].get()); + } + + DevJsonValParser::ParseVal(njon, "fila_id", nozzle_obj.m_fila_id); + DevJsonValParser::ParseVal(njon, "wear", nozzle_obj.m_wear); + + if (njon.contains("color_m"))/*maybe not contains*/ + { + nozzle_obj.m_filament_clr = njon["color_m"].get(); + } + + if (DevUtil::get_hex_bits(njon["id"].get(), 1) == 1) + { + system->m_nozzle_rack->AddRackNozzle(nozzle_obj); + } + else + { + system->m_ext_nozzles[nozzle_obj.m_nozzle_id] = nozzle_obj; + } + } } - if (nozzle_json.contains("state")) + if (device_json.contains("holder")) { - system->m_state = DevUtil::get_flag_bits(nozzle_json["state"].get(), 0, 4); - } - - for (auto it = nozzle_json["info"].begin(); it != nozzle_json["info"].end(); it++) - { - DevNozzle nozzle_obj; - const auto& njon = it.value(); - nozzle_obj.m_nozzle_id = njon["id"].get(); - nozzle_obj.m_diameter = njon["diameter"].get(); - s_parse_nozzle_type(njon["type"].get(), nozzle_obj); - system->m_nozzles[nozzle_obj.m_nozzle_id] = nozzle_obj; + system->m_nozzle_rack->ParseRackInfo(device_json["holder"]); } } -} \ No newline at end of file + +}; \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceCore/DevNozzleSystem.h b/src/slic3r/GUI/DeviceCore/DevNozzleSystem.h index 1dbff54..e8cced4 100644 --- a/src/slic3r/GUI/DeviceCore/DevNozzleSystem.h +++ b/src/slic3r/GUI/DeviceCore/DevNozzleSystem.h @@ -1,5 +1,9 @@ #pragma once +#include "DevDefs.h" +#include "DevFirmware.h" + #include "libslic3r/CommonDefs.hpp" +#include "libslic3r/MultiNozzleUtils.hpp" #include "slic3r/Utils/json_diff.hpp" #include @@ -9,20 +13,86 @@ namespace Slic3r { // Previous definitions class MachineObject; + class DevNozzleRack; struct DevNozzle { + private: + friend class DevNozzleSystemParser; + + public: int m_nozzle_id = -1; NozzleFlowType m_nozzle_flow = NozzleFlowType::S_FLOW;// 0-common 1-high flow NozzleType m_nozzle_type = NozzleType::ntUndefine;// 0-stainless_steel 1-hardened_steel 5-tungsten_carbide float m_diameter = 0.4f;// 0.2mm 0.4mm 0.6mm 0.8mm + + public: + /**/ + void SetRack(const std::weak_ptr& rack) { m_nozzle_rack = rack; }; + + /**/ + int GetNozzlePosId() const; + int GetNozzleId() const { return m_nozzle_id; } + NozzleType GetNozzleType() const { return m_nozzle_type; } + NozzleFlowType GetNozzleFlowType() const { return m_nozzle_flow; } + NozzleDiameterType GetNozzleDiameterType() const; + float GetNozzleDiameter() const { return m_diameter; } + float GetNozzleWear() const { return m_wear; } + + // display + wxString GetDisplayId() const; + wxString GetNozzleDiameterStr() const { return wxString::Format("%.1f mm", m_diameter);} + wxString GetNozzleFlowTypeStr() const; + wxString GetNozzleFlowTypeCaliStyleStr() const; + wxString GetNozzleTypeStr() const; + + static wxString GetNozzleFlowTypeStr(NozzleFlowType type); + static std::string GetNozzleFlowTypeString(NozzleFlowType type); + static wxString GetNozzleTypeStr(NozzleType type); + static std::string GetNozzleTypeString(NozzleType type); + + // serial number + wxString GetSerialNumber() const { return GetFirmwareInfo().sn; } + DevFirmwareVersionInfo GetFirmwareInfo() const; + + // location + bool AtLeftExtruder() const; + bool AtRightExtruder() const; + int GetLogicExtruderId() const;// warning: logical extruder id + + /* holder nozzle*/ + bool IsOnRack() const { return m_on_rack; } + bool IsInfoReliable() const; + + bool IsEmpty() const { return m_nozzle_id < 0; } + bool IsNormal() const; + bool IsAbnormal() const; + bool IsUnknown() const; + + std::string GetFilamentId() const { return m_fila_id; } + std::string GetFilamentColor() const { return m_filament_clr; } + + void SetOnRack(bool on_rack) { m_on_rack = on_rack; }; + void SetStatus(int stat) { m_stat = stat; } + private: + int GetTotalExtruderCount() const; + + private: + bool m_on_rack = false; + + int m_stat = 0; + float m_wear; + + std::string m_sn; + std::string m_fila_id;// main material + std::string m_filament_clr;// main color + + std::weak_ptr m_nozzle_rack; // weak pointer to the nozzle rack }; class DevNozzleSystem { friend class DevNozzleSystemParser; - public: - DevNozzleSystem(MachineObject* owner) : m_owner(owner) {} private: enum Status : int { @@ -31,27 +101,81 @@ namespace Slic3r }; public: - bool ContainsNozzle(int id) const { return m_nozzles.find(id) != m_nozzles.end(); } - DevNozzle GetNozzle(int id) const; - const std::map& GetNozzles() const { return m_nozzles;} - bool IsRefreshing() const { return m_state == 1; } + DevNozzleSystem(MachineObject* owner); + virtual ~DevNozzleSystem() {}; + + public: + MachineObject* GetOwner() const { return m_owner; } + + // nozzles + DevNozzle GetNozzleByPosId(int pos_id) const { return pos_id < 0x10 ? GetExtNozzle(pos_id) : GetRackNozzle(pos_id - 0x10); }; + + // nozzles on extruder + bool ContainsExtNozzle(int id) const { return m_ext_nozzles.find(id) != m_ext_nozzles.end(); } + DevNozzle GetExtNozzle(int id) const; + const std::map& GetExtNozzles() const { return m_ext_nozzles;} + + // nozzles on rack + void SetSupportNozzleRack(bool supported); + std::shared_ptr GetNozzleRack() const { return m_nozzle_rack;} + DevNozzle GetRackNozzle(int idx) const; + const std::map& GetRackNozzles() const; + + // nozzles on extruder and rack + bool IsRackMaximumInstalled() const; + + const std::vector CollectNozzles(int ext_loc, NozzleFlowType flow_type, float diameter = -1.0f) const; + ExtruderNozzleInfos GetExtruderNozzleInfo() const; + std::vector GetNozzleGroups() const; + bool IsIdle() const { return m_state_0_4 == NOZZLE_SYSTEM_IDLE; } + bool IsRefreshing() const { return m_state_0_4 == NOZZLE_SYSTEM_REFRESHING; } + + bool HasUnreliableNozzles() const; + bool HasUnknownNozzles() const; + int GetKnownNozzleCountOn(int ext_id) const; + + /* reading*/ + int GetReadingIdx() const { return m_reading_idx; }; + int GetReadingCount() const { return m_reading_count; }; + + /* firmware*/ + void AddFirmwareInfoWTM(const DevFirmwareVersionInfo& info); + void ClearFirmwareInfoWTM(); + DevFirmwareVersionInfo GetExtruderNozzleFirmware() const { return m_ext_nozzle_firmware_info; } + + /* replace nozzle*/ + std::optional GetReplaceNozzleSrc() const { return m_replace_nozzle_src; } + std::optional GetReplaceNozzleTar() const { return m_replace_nozzle_tar; } private: void Reset(); + void ClearNozzles(); private: MachineObject* m_owner = nullptr; - int m_extder_exist = 0; //0- none exist 1-exist, unused - int m_state = 0; //0-idle 1-checking, unused - std::map m_nozzles; + int m_extder_exist = 0; //0- none exist 1-exist, unused + int m_state_0_4 = 0; + std::optional m_replace_nozzle_src; // replace nozzle source position + std::optional m_replace_nozzle_tar; // replace nozzle target position + + /* refreshing */ + int m_reading_idx = 0; + int m_reading_count = 0; + + // nozzles on extruder + std::map m_ext_nozzles; + DevFirmwareVersionInfo m_ext_nozzle_firmware_info; + + // nozzles on rack + std::shared_ptr m_nozzle_rack; }; class DevNozzleSystemParser { public: static void ParseV1_0(const nlohmann::json& nozzletype_json, const nlohmann::json& diameter_json, DevNozzleSystem* system, std::optional flag_e3d); - static void ParseV2_0(const json& nozzle_json, DevNozzleSystem* system); + static void ParseV2_0(const json& device_json, DevNozzleSystem* system); }; }; \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceCore/DevUpgrade.cpp b/src/slic3r/GUI/DeviceCore/DevUpgrade.cpp new file mode 100644 index 0000000..dc04793 --- /dev/null +++ b/src/slic3r/GUI/DeviceCore/DevUpgrade.cpp @@ -0,0 +1,130 @@ +#include "DevUpgrade.h" +#include "DevUtil.h" + +#include "slic3r/GUI/DeviceManager.hpp" + +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/I18N.hpp" + +namespace Slic3r { + +int DevUpgrade::GetUpgradeProgressInt() const +{ + try { + return std::stoi(m_upgrade_progress); + } catch (const std::exception &e) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": " << e.what(); + } + + return 0; +} + +void DevUpgrade::ParseUpgrade_V1_0(const json &print_jj) +{ + if (print_jj.contains("upgrade_state")) { + const auto &upgrade_jj = print_jj["upgrade_state"]; + + // status and steps + DevJsonValParser::ParseVal(upgrade_jj, "status", m_upgrade_status); + DevJsonValParser::ParseVal(upgrade_jj, "progress", m_upgrade_progress); + DevJsonValParser::ParseVal(upgrade_jj, "message", m_upgrade_message); + DevJsonValParser::ParseVal(upgrade_jj, "module", m_upgrade_module); + DevJsonValParser::ParseVal(upgrade_jj, "err_code", m_upgrade_err_code); + + // version requested + DevJsonValParser::ParseVal(upgrade_jj, "new_version_state", m_upgrade_new_version_state); + DevJsonValParser::ParseVal(upgrade_jj, "consistency_request", m_upgrade_consistency_request); + DevJsonValParser::ParseVal(upgrade_jj, "force_upgrade", m_upgrade_force_upgrade); + + // version info + DevJsonValParser::ParseVal(upgrade_jj, "ota_new_version_number", m_ota_new_version_number); + + // version list + if (upgrade_jj.contains("new_ver_list")) { + m_new_ver_list.clear(); + for (auto ver_item = upgrade_jj["new_ver_list"].begin(); ver_item != upgrade_jj["new_ver_list"].end(); ver_item++) { + DevFirmwareVersionInfo ver_info; + DevJsonValParser::ParseVal(*ver_item, "name", ver_info.name); + DevJsonValParser::ParseVal(*ver_item, "cur_ver", ver_info.sw_ver); + DevJsonValParser::ParseVal(*ver_item, "new_ver", ver_info.sw_new_ver); + m_new_ver_list[ver_info.name] = ver_info; + if (ver_info.name == "ota" && m_ota_new_version_number.empty()) { m_ota_new_version_number = ver_info.sw_new_ver; } + } + } else { + m_new_ver_list.clear(); + } + + // try to parse display state + ParseUpgradeDisplayState(upgrade_jj); + } + + if (print_jj.contains("cfg")) { + const std::string &cfg = print_jj["cfg"].get(); + if (!cfg.empty()) { m_upgrade_force_upgrade = DevUtil::get_flag_bits(cfg, 2); } + } +} + +void DevUpgrade::ParseUpgradeDisplayState(const json &upgrade_state_jj) +{ + if (upgrade_state_jj.contains("dis_state")) { + if ((int) m_upgrade_display_state != upgrade_state_jj["dis_state"].get()) { + DevJsonValParser::ParseVal(upgrade_state_jj, "dis_state", m_upgrade_display_state); + + // update the version after upgrade finished + if (m_upgrade_display_state == DevFirmwareUpgradeState::UpgradingFinished) { + Slic3r::GUI::wxGetApp().CallAfter([this] { + m_owner->command_get_version(); + }); + } + + // lan mode printer, hide upgrade avaliable state + if (m_upgrade_display_state == DevFirmwareUpgradeState::UpgradingAvaliable && + m_owner->is_lan_mode_printer()) { + m_upgrade_display_state = DevFirmwareUpgradeState::UpgradingUnavaliable; + } + } + } else { + // QDS compatibility with old version + if (m_upgrade_status == "DOWNLOADING" || + m_upgrade_status == "FLASHING" || + m_upgrade_status == "UPGRADE_REQUEST" || + m_upgrade_status == "PRE_FLASH_START" || + m_upgrade_status == "PRE_FLASH_SUCCESS") { + m_upgrade_display_state = DevFirmwareUpgradeState::UpgradingInProgress; + } else if (m_upgrade_status == "UPGRADE_SUCCESS" || + m_upgrade_status == "DOWNLOAD_FAIL" || + m_upgrade_status == "FLASH_FAIL" || + m_upgrade_status == "PRE_FLASH_FAIL" || + m_upgrade_status == "UPGRADE_FAIL") { + m_upgrade_display_state = DevFirmwareUpgradeState::UpgradingFinished; + } else { + if (HasNewVersion()) { + m_upgrade_display_state = DevFirmwareUpgradeState::UpgradingAvaliable; + } else { + m_upgrade_display_state = DevFirmwareUpgradeState::UpgradingUnavaliable; + } + } + } +} + +#define UpgradeNoError 0 +#define UpgradeDownloadFailed -1 +#define UpgradeVerfifyFailed -2 +#define UpgradeFlashFailed -3 +#define UpgradePrinting -4 + +wxString DevUpgrade::GetUpgradeErrCodeStr() const +{ + switch (m_upgrade_err_code) { + case UpgradeNoError: return _L("Update successful."); + case UpgradeDownloadFailed: return _L("Downloading failed."); + case UpgradeVerfifyFailed: return _L("Verification failed."); + case UpgradeFlashFailed: return _L("Update failed."); + case UpgradePrinting: return _L("Update failed."); + default: return _L("Update failed."); + } + + return ""; +} + +} // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceCore/DevUpgrade.h b/src/slic3r/GUI/DeviceCore/DevUpgrade.h new file mode 100644 index 0000000..0290d5f --- /dev/null +++ b/src/slic3r/GUI/DeviceCore/DevUpgrade.h @@ -0,0 +1,108 @@ +#pragma once +#include +#include "slic3r/Utils/json_diff.hpp" + +#include "DevDefs.h" +#include "DevFirmware.h" + +namespace Slic3r { + +class MachineObject; +class DevUpgrade +{ +public: + static std::shared_ptr Create(MachineObject *owner) { return std::shared_ptr(new DevUpgrade(owner)); } + +public: + // upgrade status + bool IsUpgrading() const { return m_upgrade_display_state == DevFirmwareUpgradeState::UpgradingInProgress; } + bool IsUpgradeAvaliable() const { return m_upgrade_display_state == DevFirmwareUpgradeState::UpgradingAvaliable; } + DevFirmwareUpgradeState GetUpgradeState() const { return m_upgrade_display_state; } + std::string GetUpgradeStatusStr() const { return m_upgrade_status; } + + // upgrade request + bool IsUpgradeConsistencyRequest() const { return m_upgrade_consistency_request; } + bool IsUpgradeForceUpgrade() const { return m_upgrade_force_upgrade; } + + bool HasNewVersion() const { return m_upgrade_new_version_state == 1; } + std::string GetOtaNewVersion() const { return m_ota_new_version_number; } + std::map GetNewVersionList() const { return m_new_ver_list; } + + // upgrading + std::string GetUpgradeModuleStr() const { return m_upgrade_module; } + std::string GetUpgradeProgressStr() const { return m_upgrade_progress; } + int GetUpgradeProgressInt() const; + std::string GetUpgradeMessageStr() const { return m_upgrade_message; } + wxString GetUpgradeErrCodeStr() const; + +public: + // ctrls + int CtrlUpgradeConfirm(); + int CtrlUpgradeConsistencyConfirm(); + int CtrlUpgradeFirmware(FirmwareInfo info); + int CtrlUpgradeModule(std::string url, std::string module_type, std::string version); + + // parser + void ParseUpgrade_V1_0(const json &print_jj); + void ParseUpgradeDisplayState(const json &upgrade_state_jj); + +protected: + DevUpgrade(MachineObject *owner) : m_owner(owner) {} + +private: + MachineObject *m_owner = nullptr; + + int m_upgrade_new_version_state = 0; // 0: invalid version, 1: new version available, 2: no new version + bool m_upgrade_consistency_request = false; + bool m_upgrade_force_upgrade = false; + + std::string m_ota_new_version_number; + std::map m_new_ver_list; // some protcols have version list + DevFirmwareUpgradeState m_upgrade_display_state = DevFirmwareUpgradeState::DC; + + int m_upgrade_err_code; + std::string m_upgrade_status; + std::string m_upgrade_progress; + std::string m_upgrade_message; + std::string m_upgrade_module; +}; + +#if 0 /*TODO*/ +class DevUpgradeGetVersionInfo +{ +public: + DevUpgradeGetVersionInfo(MachineObject* obj) : m_owner(obj) {} ; + +public: + bool IsEmpty() const { m_module_version_map.empty();} + + DevFirmwareVersionInfo GetAirPumpVersionInfo() const { return m_air_pump_version_info; } + DevFirmwareVersionInfo GetLaserVersionInfo() const { return m_laser_version_info; } + DevFirmwareVersionInfo GetCuttingModuleVersionInfo() const { return m_cutting_module_version_info; } + DevFirmwareVersionInfo GetExtinguishVersionInfo() const { return m_extinguish_version_info; } + DevFirmwareVersionInfo GetRotaryVersionInfo() const { return m_rotary_version_info; } + std::map GetModuleVersionInfoMap() const { return m_module_version_map; } + +public: + int CtrlGetVersion(bool with_retry = true); + + void ParseGetVersion(const json &print_jj); + +private: + MachineObject *m_owner; + + // some version info + DevFirmwareVersionInfo m_air_pump_version_info; + DevFirmwareVersionInfo m_laser_version_info; + DevFirmwareVersionInfo m_cutting_module_version_info; + DevFirmwareVersionInfo m_extinguish_version_info; + DevFirmwareVersionInfo m_rotary_version_info; + std::map m_module_version_map; + + // ctrl retry + int m_retry_count = 0; + int m_max_retry = 3; +}; +#endif + +} // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceCore/DevUpgradeCtrl.cpp b/src/slic3r/GUI/DeviceCore/DevUpgradeCtrl.cpp new file mode 100644 index 0000000..7fa2d2b --- /dev/null +++ b/src/slic3r/GUI/DeviceCore/DevUpgradeCtrl.cpp @@ -0,0 +1,56 @@ +#include "DevUpgrade.h" +#include "DevUtil.h" + +#include "slic3r/GUI/DeviceManager.hpp" + +namespace Slic3r { + +int DevUpgrade::CtrlUpgradeConfirm() +{ + json j; + j["upgrade"]["command"] = "upgrade_confirm"; + j["upgrade"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); + j["upgrade"]["src_id"] = 1; // 1 for slicer + return m_owner->publish_json(j); +} + +int DevUpgrade::CtrlUpgradeConsistencyConfirm() +{ + json j; + j["upgrade"]["command"] = "consistency_confirm"; + j["upgrade"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); + j["upgrade"]["src_id"] = 1; // 1 for slicer + return m_owner->publish_json(j); +} + +int DevUpgrade::CtrlUpgradeFirmware(FirmwareInfo info) +{ + std::string version = info.version; + std::string dst_url = info.url; + std::string module_name = info.module_type; + + json j; + j["upgrade"]["command"] = "start"; + j["upgrade"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); + j["upgrade"]["url"] = info.url; + j["upgrade"]["module"] = info.module_type; + j["upgrade"]["version"] = info.version; + j["upgrade"]["src_id"] = 1; + + return m_owner->publish_json(j); +} + +int DevUpgrade::CtrlUpgradeModule(std::string url, std::string module_type, std::string version) +{ + json j; + j["upgrade"]["command"] = "start"; + j["upgrade"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); + j["upgrade"]["url"] = url; + j["upgrade"]["module"] = module_type; + j["upgrade"]["version"] = version; + j["upgrade"]["src_id"] = 1; + + return m_owner->publish_json(j); +} + +} // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceCore/DevUtil.h b/src/slic3r/GUI/DeviceCore/DevUtil.h index 4ba309b..0c0ec7d 100644 --- a/src/slic3r/GUI/DeviceCore/DevUtil.h +++ b/src/slic3r/GUI/DeviceCore/DevUtil.h @@ -14,6 +14,11 @@ #include #include "nlohmann/json.hpp" + /* Sequence Id*/ +#define STUDIO_START_SEQ_ID 20000 +#define STUDIO_END_SEQ_ID 30000 +#define CLOUD_SEQ_ID 0 + namespace Slic3r { @@ -28,9 +33,16 @@ public: static int get_flag_bits(std::string str, int start, int count = 1); static int get_flag_bits(int num, int start, int count = 1, int base = 10); + // eg. get_hex_bits(16, 1, 10) = 1 + static int get_hex_bits(int num, int pos, int input_num_base = 10) { return get_flag_bits(num, pos * 4, 4, input_num_base);}; + static float string_to_float(const std::string& str_value); static std::string convertToIp(long long ip); + + // sequence id check + static bool is_studio_cmd(int seq) { return seq >= STUDIO_START_SEQ_ID && seq < STUDIO_END_SEQ_ID;}; + static bool is_cloud_cmd(int seq) { return seq == CLOUD_SEQ_ID;}; }; @@ -104,4 +116,52 @@ struct NumericStrCompare } }; +enum class DirtyMode{ + COUNTER, + TIMER +}; + +template +class DevDirtyHandler{ +public: + DevDirtyHandler(T init_value, int setting_threshold, DirtyMode mode): m_value(init_value), m_setting_threshold(setting_threshold), m_mode(mode) + { + m_threshold = setting_threshold; + } + ~DevDirtyHandler(){}; + + T GetValue() const { return m_value; }; + + void SetOptimisticValue(const T& data) + { + m_value = data; + m_start_time = time(nullptr); + m_threshold = m_setting_threshold; + } + + void UpdateValue(const T& data) + { + if (m_mode == DirtyMode::COUNTER) + { + if (m_threshold > 0) + m_threshold--; + else + m_value = data; + } + else if (m_mode == DirtyMode::TIMER) + { + if (time(nullptr) - m_start_time > m_threshold) + m_value = data; + } + } + +private: + T m_value; + DirtyMode m_mode; + int m_setting_threshold{0}; + + int m_start_time{0}; + int m_threshold{0}; +}; + }; // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceCore/DevUtilBackend.cpp b/src/slic3r/GUI/DeviceCore/DevUtilBackend.cpp new file mode 100644 index 0000000..3dc461a --- /dev/null +++ b/src/slic3r/GUI/DeviceCore/DevUtilBackend.cpp @@ -0,0 +1,59 @@ +#include "DevUtilBackend.h" + +#include "slic3r/GUI/BackgroundSlicingProcess.hpp" + +#include "slic3r/GUI/PartPlate.hpp" +#include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/GUI_App.hpp" + +#include + +namespace Slic3r +{ + + +Slic3r::MultiNozzleUtils::NozzleInfo DevUtilBackend::GetNozzleInfo(const DevNozzle& dev_nozzle) +{ + MultiNozzleUtils::NozzleInfo info; + info.diameter = dev_nozzle.GetNozzleDiameterStr().ToStdString(); + info.volume_type = (dev_nozzle.GetNozzleFlowType() == NozzleFlowType::H_FLOW ? NozzleVolumeType::nvtHighFlow : NozzleVolumeType::nvtStandard); + info.extruder_id = dev_nozzle.GetLogicExtruderId(); + + return info; +} + +std::optional +DevUtilBackend::GetNozzleGroupResult(Slic3r::GUI::Plater* plater) +{ + if (plater && plater->background_process().get_current_gcode_result()) { + return plater->background_process().get_current_gcode_result()->nozzle_group_result; + } + + return std::nullopt; +} + +std::unordered_map +DevUtilBackend::CollectNozzleInfo(MultiNozzleUtils::MultiNozzleGroupResult* nozzle_group_res, int logic_ext_id) +{ + std::unordered_map need_nozzle_map; + if (!nozzle_group_res) { + return need_nozzle_map; + } + + const std::vector& nozzle_vec = nozzle_group_res->get_nozzle_vec(logic_ext_id); + for (auto slicing_nozzle : nozzle_vec) { + try { + NozzleDef data; + data.nozzle_diameter = boost::lexical_cast(slicing_nozzle.diameter); + data.nozzle_flow_type = (slicing_nozzle.volume_type == NozzleVolumeType::nvtHighFlow ? NozzleFlowType::H_FLOW : NozzleFlowType::S_FLOW); + need_nozzle_map[data]++; + } catch (const std::exception& e) { + assert(0); + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "exception: " << e.what(); + } + } + + return need_nozzle_map; +} + +};// namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceCore/DevUtilBackend.h b/src/slic3r/GUI/DeviceCore/DevUtilBackend.h new file mode 100644 index 0000000..fc2dc52 --- /dev/null +++ b/src/slic3r/GUI/DeviceCore/DevUtilBackend.h @@ -0,0 +1,43 @@ +/** + * @file DevUtilBackend.h + * @brief Provides common static utility methods for backend (preset/slicing). + * + * This class offers a collection of static helper functions such as string manipulation, + * file operations, and other frequently used utilities. + */ + +#pragma once +#include "DevDefs.h" +#include "DevNozzleSystem.h" + +#include "libslic3r/MultiNozzleUtils.hpp" +#include + + // Forward declarations +namespace Slic3r +{ +class MachineObject; + +namespace GUI +{ +class Plater; +} +}; // namespace Slic3r::GUI + +namespace Slic3r +{ + +class DevUtilBackend +{ +public: + DevUtilBackend() = delete; + +public: + static MultiNozzleUtils::NozzleInfo GetNozzleInfo(const DevNozzle& dev_nozzle); + + // for rack + static std::optional GetNozzleGroupResult(Slic3r::GUI::Plater* plater); + static std::unordered_map CollectNozzleInfo(MultiNozzleUtils::MultiNozzleGroupResult* nozzle_group_res, int logic_ext_id); +}; + +}; // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceErrorDialog.cpp b/src/slic3r/GUI/DeviceErrorDialog.cpp index 497b859..6c55d6f 100644 --- a/src/slic3r/GUI/DeviceErrorDialog.cpp +++ b/src/slic3r/GUI/DeviceErrorDialog.cpp @@ -177,7 +177,10 @@ void DeviceErrorDialog::init_button_list() init_button(CANCLE, _L("Cancle")); init_button(STOP_DRYING, _L("Stop Drying")); init_button(PROCEED, _L("Proceed")); - init_button(DBL_CHECK_CANCEL, _L("Cancle")); + init_button(OK_JUMP_RACK, "OK"); + init_button(ABORT, _L("Abort")); + + init_button(DBL_CHECK_CANCEL, _L("Cancel")); init_button(DBL_CHECK_DONE, _L("Done")); init_button(DBL_CHECK_RETRY, _L("Retry")); init_button(DBL_CHECK_RESUME, _L("Resume")); @@ -238,6 +241,9 @@ wxString DeviceErrorDialog::show_error_code(int error_code) wxGetApp().UpdateDlgDarkUI(this); Show(); Raise(); +#ifdef __WXOSX__ + SetWindowStyleFlag(GetWindowStyleFlag() | wxSTAY_ON_TOP); +#endif this->RequestUserAttention(wxUSER_ATTENTION_ERROR); @@ -454,8 +460,17 @@ void DeviceErrorDialog::on_button_click(ActionButton btn_id) } break; } - case DeviceErrorDialog::ERROR_BUTTON_COUNT: break; - + case DeviceErrorDialog::OK_JUMP_RACK: + { + Slic3r::GUI::wxGetApp().mainframe->jump_to_monitor(); + Slic3r::GUI::wxGetApp().mainframe->m_monitor->jump_to_Rack(); + break; + } + case DeviceErrorDialog::ABORT: + { + m_obj->command_ams_control("abort"); + break; + } case DeviceErrorDialog::DBL_CHECK_CANCEL: { // post EVT_SECONDARY_CHECK_CANCEL // no event diff --git a/src/slic3r/GUI/DeviceErrorDialog.hpp b/src/slic3r/GUI/DeviceErrorDialog.hpp index cca8af1..9915b78 100644 --- a/src/slic3r/GUI/DeviceErrorDialog.hpp +++ b/src/slic3r/GUI/DeviceErrorDialog.hpp @@ -46,8 +46,8 @@ public: CANCLE = 37, REMOVE_CLOSE_BTN = 39, // special case, do not show close button PROCEED = 41, - - ERROR_BUTTON_COUNT, + OK_JUMP_RACK = 49, + ABORT = 51, // old error code to pseudo action DBL_CHECK_CANCEL = 10000, diff --git a/src/slic3r/GUI/DeviceManager.cpp b/src/slic3r/GUI/DeviceManager.cpp index 51d30f5..f88de6c 100644 --- a/src/slic3r/GUI/DeviceManager.cpp +++ b/src/slic3r/GUI/DeviceManager.cpp @@ -10,6 +10,7 @@ #include "MsgDialog.hpp" #include "DeviceErrorDialog.hpp" #include "Plater.hpp" +#include "GUI_App.hpp" #include "ReleaseNote.hpp" #include #include @@ -19,9 +20,20 @@ #include #include #include + +/* mac need the macro while including */ +#ifdef __APPLE__ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#endif +#include + #include #include "fast_float/fast_float.h" +#include "DeviceCore/DevAxis.h" +#include "DeviceCore/DevChamber.h" #include "DeviceCore/DevFilaSystem.h" #include "DeviceCore/DevExtensionTool.h" #include "DeviceCore/DevExtruderSystem.h" @@ -30,6 +42,7 @@ #include "DeviceCore/DevLamp.h" #include "DeviceCore/DevFan.h" #include "DeviceCore/DevStorage.h" +#include "DeviceCore/DevNozzleRack.h" #include "DeviceCore/DevConfig.h" #include "DeviceCore/DevCtrl.h" @@ -37,6 +50,7 @@ #include "DeviceCore/DevPrintOptions.h" #include "DeviceCore/DevPrintTaskInfo.h" #include "DeviceCore/DevHMS.h" +#include "DeviceCore/DevUpgrade.h" #include "DeviceCore/DevMapping.h" #include "DeviceCore/DevManager.h" @@ -185,8 +199,22 @@ wxString Slic3r::get_stage_string(int stage) return _L("Measuring Surface"); case 58: return _L("Thermal Preconditioning for first layer optimization"); + case 59: + return _L("Homing Blade Holder"); // O1C + case 60: + return _L("Calibrating Camera Offset"); // O1C + case 61: + return _L("Calibrating Blade Holder Position"); // O1C + case 62: + return _L("Hotend Pick and Place Test"); // O1C + case 63: + return _L("Waiting for the Chamber temperature to equalize"); // O1S O1E/U1 O1D/U2.5 + case 64: + return _L(" Preparing Hotend");//O1C/U0 case 65: return _L("Calibrating the detection position of nozzle clumping"); // N7 + case 66: + return _L("Purifying the chamber air"); default: BOOST_LOG_TRIVIAL(info) << "stage = " << stage; } @@ -494,22 +522,27 @@ MachineObject::MachineObject(std::string name, std::string url, std::string ip, MachineObject::MachineObject(DeviceManager* manager, NetworkAgent* agent, std::string name, std::string id, std::string ip) :dev_name(name), - dev_id(id), dev_ip(ip), subtask_(nullptr), model_task(nullptr), slice_info(nullptr), m_is_online(false) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " called for dev_id=" << QDTCrossTalk::Crosstalk_DevId(id) << ", main_thread=" << wxThread::IsMain(); + if (!wxThread::IsMain()) { + assert(false && "critical warning"); + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "called from other thread, callstack: " << boost::stacktrace::stacktrace(); + } + m_manager = manager; m_agent = agent; + m_dev_info = DevInfo::Create(this); + m_dev_info->SetDevId(id); + reset(); /* temprature fields */ - - chamber_temp = 0.0f; - chamber_temp_target = 0.0f; frame_temp = 0.0f; /* ams fileds */ @@ -520,12 +553,6 @@ MachineObject::MachineObject(DeviceManager* manager, NetworkAgent* agent, std::s /* signals */ wifi_signal = ""; - /* upgrade */ - upgrade_force_upgrade = false; - upgrade_new_version = false; - upgrade_consistency_request = false; - - /* printing */ mc_print_stage = 0; mc_print_error_code = 0; @@ -543,14 +570,18 @@ MachineObject::MachineObject(DeviceManager* manager, NetworkAgent* agent, std::s vt_slot.push_back(vslot); { + m_axis = DevAxis::Create(this); + m_chamber = DevChamber::Create(this); m_lamp = new DevLamp(this); m_fan = new DevFan(this); m_bed = new DevBed(this); + m_storage = new DevStorage(this); m_extder_system = new DevExtderSystem(this); m_extension_tool = DevExtensionTool::Create(this); m_nozzle_system = new DevNozzleSystem(this); m_fila_system = new DevFilaSystem(this); + m_upgrade = DevUpgrade::Create(this); m_hms_system = new DevHMS(this); m_config = new DevConfig(this); @@ -561,6 +592,12 @@ MachineObject::MachineObject(DeviceManager* manager, NetworkAgent* agent, std::s MachineObject::~MachineObject() { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " called for dev_id=" << QDTCrossTalk::Crosstalk_DevId(get_dev_id()) << ", main_thread=" << wxThread::IsMain(); + if (!wxThread::IsMain()) { + assert(false && "critical warning"); + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " called from other thread, callstack: " << boost::stacktrace::stacktrace(); + } + if (subtask_) { delete subtask_; subtask_ = nullptr; @@ -728,11 +765,6 @@ void MachineObject::get_ams_colors(std::vector &ams_colors) { std::string MachineObject::get_firmware_type_str() { - /*if (firmware_type == PrinterFirmwareType::FIRMWARE_TYPE_ENGINEER) - return "engineer"; - else if (firmware_type == PrinterFirmwareType::FIRMWARE_TYPE_PRODUCTION) - return "product";*/ - // return product by default; // always return product, printer do not push this field return "product"; @@ -740,11 +772,6 @@ std::string MachineObject::get_firmware_type_str() std::string MachineObject::get_lifecycle_type_str() { - /*if (lifecycle == PrinterFirmwareType::FIRMWARE_TYPE_ENGINEER) - return "engineer"; - else if (lifecycle == PrinterFirmwareType::FIRMWARE_TYPE_PRODUCTION) - return "product";*/ - // return product by default; // always return product, printer do not push this field return "product"; @@ -752,25 +779,7 @@ std::string MachineObject::get_lifecycle_type_str() bool MachineObject::is_in_upgrading() const { - return upgrade_display_state == DevFirmwareUpgradingState::UpgradingInProgress; -} - -bool MachineObject::is_upgrading_avalable() -{ - return upgrade_display_state == DevFirmwareUpgradingState::UpgradingAvaliable; -} - -int MachineObject::get_upgrade_percent() const -{ - if (upgrade_progress.empty()) - return 0; - try { - int result = atoi(upgrade_progress.c_str()); - return result; - } catch(...) { - ; - } - return 0; + return m_upgrade->IsUpgrading(); } std::string MachineObject::get_ota_version() @@ -800,25 +809,6 @@ bool MachineObject::check_version_valid() return valid; } -wxString MachineObject::get_upgrade_result_str(int err_code) -{ - switch(err_code) { - case UpgradeNoError: - return _L("Update successful."); - case UpgradeDownloadFailed: - return _L("Downloading failed."); - case UpgradeVerfifyFailed: - return _L("Verification failed."); - case UpgradeFlashFailed: - return _L("Update failed."); - case UpgradePrinting: - return _L("Update failed."); - default: - return _L("Update failed."); - } - return ""; -} - std::map MachineObject::get_ams_version() { std::vector multi_tray_ams_type = {"ams", "n3f"}; @@ -855,6 +845,7 @@ void MachineObject::clear_version_info() cutting_module_version_info = DevFirmwareVersionInfo(); extinguish_version_info = DevFirmwareVersionInfo(); module_vers.clear(); + m_nozzle_system->ClearFirmwareInfoWTM(); } void MachineObject::store_version_info(const DevFirmwareVersionInfo& info) @@ -867,6 +858,8 @@ void MachineObject::store_version_info(const DevFirmwareVersionInfo& info) cutting_module_version_info = info; } else if (info.isExtinguishSystem()) { extinguish_version_info = info; + }else if (info.isWTM()) { + m_nozzle_system->AddFirmwareInfoWTM(info); } module_vers.emplace(info.name, info); @@ -890,21 +883,6 @@ bool MachineObject::check_pa_result_validation(PACalibResult& result) return true; } -bool MachineObject::is_axis_at_home(std::string axis) -{ - if (m_home_flag == 0) { return true; } - - if (axis == "X") { - return (m_home_flag & 1) == 1; - } else if (axis == "Y") { - return ((m_home_flag >> 1) & 1) == 1; - } else if (axis == "Z") { - return ((m_home_flag >> 2) & 1) == 1; - } - - return true; -} - bool MachineObject::is_filament_at_extruder() { if (hw_switch_state == 1) @@ -1163,14 +1141,6 @@ void MachineObject::parse_version_func() { } -bool MachineObject::is_studio_cmd(int sequence_id) -{ - if (sequence_id >= START_SEQ_ID && sequence_id < END_SEQ_ID) { - return true; - } - return false; -} - bool MachineObject::canEnableTimelapse(wxString &error_message) const { if (!is_support_timelapse) { @@ -1224,15 +1194,15 @@ int MachineObject::command_request_push_all(bool request_now) if (diff.count() < REQUEST_PUSH_MIN_TIME) { if (request_now) { - BOOST_LOG_TRIVIAL(trace) << "static: command_request_push_all, dev_id=" << QDTCrossTalk::Crosstalk_DevId(dev_id); + BOOST_LOG_TRIVIAL(trace) << "static: command_request_push_all, dev_id=" << QDTCrossTalk::Crosstalk_DevId(get_dev_id()); last_request_push = std::chrono::system_clock::now(); } else { - BOOST_LOG_TRIVIAL(trace) << "static: command_request_push_all: send request too fast, dev_id=" << QDTCrossTalk::Crosstalk_DevId(dev_id); + BOOST_LOG_TRIVIAL(trace) << "static: command_request_push_all, dev_id=" << QDTCrossTalk::Crosstalk_DevId(get_dev_id()); return -1; } } else { - BOOST_LOG_TRIVIAL(trace) << "static: command_request_push_all, dev_id=" << QDTCrossTalk::Crosstalk_DevId(dev_id); + BOOST_LOG_TRIVIAL(trace) << "static: command_request_push_all, dev_id=" << QDTCrossTalk::Crosstalk_DevId(get_dev_id()); last_request_push = std::chrono::system_clock::now(); } @@ -1249,11 +1219,11 @@ int MachineObject::command_pushing(std::string cmd) auto curr_time = std::chrono::system_clock::now(); auto diff = std::chrono::duration_cast(curr_time - last_request_start); if (diff.count() < REQUEST_START_MIN_TIME) { - BOOST_LOG_TRIVIAL(trace) << "static: command_request_start: send request too fast, dev_id=" << QDTCrossTalk::Crosstalk_DevId(dev_id); + BOOST_LOG_TRIVIAL(trace) << "static: command_request_start: send request too fast, dev_id=" << QDTCrossTalk::Crosstalk_DevId(get_dev_id()); return -1; } else { - BOOST_LOG_TRIVIAL(trace) << "static: command_request_start, dev_id=" << QDTCrossTalk::Crosstalk_DevId(dev_id); + BOOST_LOG_TRIVIAL(trace) << "static: command_request_start, dev_id=" << QDTCrossTalk::Crosstalk_DevId(get_dev_id()); last_request_start = std::chrono::system_clock::now(); } @@ -1295,79 +1265,6 @@ int MachineObject::command_clean_print_error_uiop(int print_error) return this->publish_json(j); } -int MachineObject::command_upgrade_confirm() -{ - json j; - j["upgrade"]["command"] = "upgrade_confirm"; - j["upgrade"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); - j["upgrade"]["src_id"] = 1; // 1 for slicer - return this->publish_json(j); -} - -int MachineObject::command_consistency_upgrade_confirm() -{ - json j; - j["upgrade"]["command"] = "consistency_confirm"; - j["upgrade"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); - j["upgrade"]["src_id"] = 1; // 1 for slicer - return this->publish_json(j); -} - -int MachineObject::command_upgrade_firmware(FirmwareInfo info) -{ - std::string version = info.version; - std::string dst_url = info.url; - std::string module_name = info.module_type; - - json j; - j["upgrade"]["command"] = "start"; - j["upgrade"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); - j["upgrade"]["url"] = info.url; - j["upgrade"]["module"] = info.module_type; - j["upgrade"]["version"] = info.version; - j["upgrade"]["src_id"] = 1; - - return this->publish_json(j); -} - -int MachineObject::command_upgrade_module(std::string url, std::string module_type, std::string version) -{ - json j; - j["upgrade"]["command"] = "start"; - j["upgrade"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); - j["upgrade"]["url"] = url; - j["upgrade"]["module"] = module_type; - j["upgrade"]["version"] = version; - j["upgrade"]["src_id"] = 1; - - return this->publish_json(j); -} - -int MachineObject::command_xyz_abs() -{ - return this->publish_gcode("G90 \n"); -} - -int MachineObject::command_auto_leveling() -{ - return this->publish_gcode("G29 \n"); -} - -int MachineObject::command_go_home() -{ - if (m_support_mqtt_homing) - { - json j; - j["print"]["command"] = "back_to_center"; - j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); - return this->publish_json(j); - } - - // gcode command - return this->is_in_printing() ? this->publish_gcode("G28 X\n") : this->publish_gcode("G28 \n"); -} - - int MachineObject::command_task_partskip(std::vector part_ids) { json j; @@ -1541,16 +1438,6 @@ int MachineObject::command_refresh_nozzle(){ return this->publish_json(j, 1); } -int MachineObject::command_set_chamber(int temp) -{ - json j; - j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); - j["print"]["command"] = "set_ctt"; - j["print"]["ctt_val"] = temp; - - return this->publish_json(j, 1); -} - int MachineObject::check_resume_condition() { if (jobState_ > 1) { @@ -1821,53 +1708,6 @@ int MachineObject::command_ams_air_print_detect(bool air_print_detect) return this->publish_json(j); } - -int MachineObject::command_axis_control(std::string axis, double unit, double input_val, int speed) -{ - if (m_support_mqtt_axis_control) - { - json j; - j["print"]["command"] = "xyz_ctrl"; - j["print"]["axis"] = axis; - j["print"]["dir"] = input_val > 0 ? 1 : -1; - j["print"]["mode"] = (std::abs(input_val) >= 10) ? 1 : 0; - j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); - return this->publish_json(j); - } - - double value = input_val; - if (!is_core_xy()) { - if ( axis.compare("Y") == 0 - || axis.compare("Z") == 0) { - value = -1.0 * input_val; - } - } - - char cmd[256]; - if (axis.compare("X") == 0 - || axis.compare("Y") == 0 - || axis.compare("Z") == 0) { - sprintf(cmd, "M211 S \nM211 X1 Y1 Z1\nM1002 push_ref_mode\nG91 \nG1 %s%0.1f F%d\nM1002 pop_ref_mode\nM211 R\n", axis.c_str(), value * unit, speed); - } - else if (axis.compare("E") == 0) { - sprintf(cmd, "M83 \nG0 %s%0.1f F%d\n", axis.c_str(), value * unit, speed); - } - else { - return -1; - } - - try { - json j; - j["axis_control"] = axis; - - NetworkAgent* agent = GUI::wxGetApp().getAgent(); - if (agent) agent->track_event("printer_control", j.dump()); - } - catch (...) {} - - return this->publish_gcode(cmd); -} - int MachineObject::command_extruder_control(int nozzle_id, double val) { json j; @@ -1985,6 +1825,10 @@ int MachineObject::command_set_pa_calibration(const std::vector & j["print"]["filaments"][i]["n_coef"] = std::to_string(pa_calib_values[i].n_coef); else j["print"]["filaments"][i]["n_coef"] = "0.0"; + if (pa_calib_values[i].nozzle_pos_id >= 0) { + j["print"]["filaments"][i]["nozzle_pos"] = pa_calib_values[i].nozzle_pos_id; + j["print"]["filaments"][i]["nozzle_sn"] = pa_calib_values[i].nozzle_sn; + } } return this->publish_json(j); @@ -2002,6 +1846,10 @@ int MachineObject::command_delete_pa_calibration(const PACalibIndexInfo& pa_cali j["print"]["nozzle_id"] = _generate_nozzle_id(pa_calib.nozzle_volume_type, to_string_nozzle_diameter(pa_calib.nozzle_diameter)).ToStdString(); j["print"]["filament_id"] = pa_calib.filament_id; j["print"]["cali_idx"] = pa_calib.cali_idx; + if (pa_calib.nozzle_pos_id >= 0) { + j["print"]["nozzle_pos"] = pa_calib.nozzle_pos_id; + j["print"]["nozzle_sn"] = pa_calib.nozzle_sn; + } j["print"]["nozzle_diameter"] = to_string_nozzle_diameter(pa_calib.nozzle_diameter); return this->publish_json(j); @@ -2034,6 +1882,11 @@ int MachineObject::command_get_pa_calibration_tab(const PACalibExtruderInfo &cal j["print"]["nozzle_id"] = _generate_nozzle_id(calib_info.nozzle_volume_type, to_string_nozzle_diameter(calib_info.nozzle_diameter)).ToStdString(); j["print"]["nozzle_diameter"] = to_string_nozzle_diameter(calib_info.nozzle_diameter); + if (calib_info.nozzle_pos_id >= 0) { + j["print"]["nozzle_pos"] = calib_info.nozzle_pos_id; + j["print"]["nozzle_sn"] = calib_info.nozzle_sn; + } + request_tab_from_qds = true; return this->publish_json(j); } @@ -2058,6 +1911,10 @@ int MachineObject::commnad_select_pa_calibration(const PACalibIndexInfo& pa_cali j["print"]["slot_id"] = pa_calib_info.slot_id; j["print"]["cali_idx"] = pa_calib_info.cali_idx; j["print"]["filament_id"] = pa_calib_info.filament_id; + if (pa_calib_info.nozzle_pos_id >= 0) { + j["print"]["nozzle_pos"] = pa_calib_info.nozzle_pos_id; + j["print"]["nozzle_sn"] = pa_calib_info.nozzle_sn; + } j["print"]["nozzle_diameter"] = to_string_nozzle_diameter(pa_calib_info.nozzle_diameter); return this->publish_json(j); @@ -2241,6 +2098,18 @@ int MachineObject::command_xcam_control_buildplate_marker_detector(bool on_off) return command_xcam_control("buildplate_marker_detector", on_off); } +int MachineObject::command_xcam_control_build_plate_type_detector(bool on_off) +{ + xcam_build_plate_type_detect.SetOptimisticValue(on_off); + return command_xcam_control("buildplate_marker_detector", on_off); +} + +int MachineObject::command_xcam_control_build_plate_align_detector(bool on_off) +{ + xcam_build_plate_align_detect.SetOptimisticValue(on_off); + return command_xcam_control("plate_offset_switch", on_off); +} + int MachineObject::command_xcam_control_first_layer_inspector(bool on_off, bool print_halt) { xcam_first_layer_inspector = on_off; @@ -2358,23 +2227,16 @@ bool MachineObject::is_printing_finished() return false; } -bool MachineObject::is_core_xy() -{ - if (get_printer_arch() == PrinterArch::ARCH_CORE_XY) - return true; - return false; -} - void MachineObject::reset_update_time() { - BOOST_LOG_TRIVIAL(trace) << "reset reset_update_time, dev_id =" << QDTCrossTalk::Crosstalk_DevId(dev_id); + BOOST_LOG_TRIVIAL(trace) << "reset reset_update_time, dev_id =" << QDTCrossTalk::Crosstalk_DevId(get_dev_id()); last_update_time = std::chrono::system_clock::now(); subscribe_counter = SUBSCRIBE_RETRY_COUNT; } void MachineObject::reset() { - BOOST_LOG_TRIVIAL(trace) << "reset dev_id=" << QDTCrossTalk::Crosstalk_DevId(dev_id); + BOOST_LOG_TRIVIAL(trace) << "reset dev_id=" << QDTCrossTalk::Crosstalk_DevId(get_dev_id()); last_update_time = std::chrono::system_clock::now(); subscribe_counter = SUBSCRIBE_RETRY_COUNT; m_push_count = 0; @@ -2390,13 +2252,13 @@ void MachineObject::reset() iot_print_status = ""; print_status = ""; last_mc_print_stage = -1; - m_new_ver_list_exist = false; network_wired = false; dev_connection_name = ""; job_id_ = ""; jobState_ = 0; m_plate_index = -1; device_cert_installed = false; + clear_auto_nozzle_mapping();// reset nozzle mapping // reset print_json json empty_j; @@ -2448,7 +2310,7 @@ bool MachineObject::is_connected() std::chrono::system_clock::time_point curr_time = std::chrono::system_clock::now(); auto diff = std::chrono::duration_cast(curr_time - last_update_time); if (diff.count() > DISCONNECT_TIMEOUT) { - BOOST_LOG_TRIVIAL(trace) << "machine_object: dev_id=" << QDTCrossTalk::Crosstalk_DevId(dev_id) <<", diff count = " << diff.count(); + BOOST_LOG_TRIVIAL(trace) << "machine_object: dev_id=" << QDTCrossTalk::Crosstalk_DevId(get_dev_id()) << ", diff count = " << diff.count(); return false; } @@ -2490,7 +2352,7 @@ bool MachineObject::is_info_ready(bool check_version) const << ": not ready, m_full_msg_count=" << m_full_msg_count << ", m_push_count=" << m_push_count << ", diff.count()=" << diff.count() - << ", dev_id=" << QDTCrossTalk::Crosstalk_DevId(dev_id); + << ", dev_id=" << QDTCrossTalk::Crosstalk_DevId(get_dev_id()); return false; } @@ -2627,7 +2489,7 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ if (j_pre["print"]["command"].get() == "push_status") { if (j_pre["print"].contains("msg")) { if (j_pre["print"]["msg"].get() == 0) { //all message - BOOST_LOG_TRIVIAL(trace) << "static: get push_all msg, dev_id=" << QDTCrossTalk::Crosstalk_DevId(dev_id); + BOOST_LOG_TRIVIAL(trace) << "static: get push_all msg, dev_id=" << QDTCrossTalk::Crosstalk_DevId(get_dev_id()); m_push_count++; m_full_msg_count++; @@ -2783,11 +2645,17 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ last_utc_time = last_update_time; } - if (Slic3r::get_logging_level() < level_string_to_boost("trace")) { - BOOST_LOG_TRIVIAL(info) << "parse_json: dev_id=" << QDTCrossTalk::Crosstalk_DevId(dev_id) << ", origin playload=" << QDTCrossTalk::Crosstalk_JsonLog(j_pre); +#if !QDT_RELEASE_TO_PUBLIC + BOOST_LOG_TRIVIAL(trace) << "parse_json: dev_id=" << QDTCrossTalk::Crosstalk_DevId(get_dev_id()) << ", tunnel=" << tunnel << ", merged playload=" << QDTCrossTalk::Crosstalk_JsonLog(j); +#else + if (Slic3r::get_logging_level() >= level_string_to_boost("trace")) { + BOOST_LOG_TRIVIAL(trace) << __FUNCTION__ << ": dev_id=" << QDTCrossTalk::Crosstalk_DevId(get_dev_id()) << ", origin playload=" << QDTCrossTalk::Crosstalk_JsonLog(j_pre); } else { - BOOST_LOG_TRIVIAL(trace) << "parse_json: dev_id=" << QDTCrossTalk::Crosstalk_DevId(dev_id) << ", tunnel is=" << tunnel << ", merged playload=" << QDTCrossTalk::Crosstalk_JsonLog(j); + if (j_pre.contains("print")) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": dev_id=" << QDTCrossTalk::Crosstalk_DevId(get_dev_id()) << ", print playload=" << QDTCrossTalk::Crosstalk_JsonLog(j_pre["print"]); + }; } +#endif // Parse version info first, as if version arrive or change, 'print' need parse again with new compatible settings try { @@ -3032,6 +2900,8 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ if (jj.contains("command")) { + m_auto_nozzle_mapping.ParseAutoNozzleMapping(this, jj); + if (jj["command"].get() == "ams_change_filament") { if (jj.contains("errno")) { if (jj["errno"].is_number()) { @@ -3055,7 +2925,7 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ } if (jj["command"].get() == "set_ctt") { - if (m_agent && is_studio_cmd(sequence_id)) { + if (m_agent && DevUtil::is_studio_cmd(sequence_id)) { if (jj["errno"].is_number()) { wxString text; if (jj["errno"].get() == -2) { @@ -3077,13 +2947,10 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ if (!key_field_only) { - if (is_studio_cmd(sequence_id) && jj.contains("command") && jj.contains("err_code")) - { - if (jj["err_code"].is_number()) - { - /* proceed action*/ - json action_json = jj.contains("err_index") ? jj : json(); - + // add DevUtil::is_cloud_cmd for cloud print error code + if ((DevUtil::is_studio_cmd(sequence_id) || DevUtil::is_cloud_cmd(sequence_id)) && jj.contains("command") && jj.contains("err_code")) { + if (jj["err_code"].is_number()) { + json action_json = jj.contains("err_index") ? jj : json();/* proceed action*/ add_command_error_code_dlg(jj["err_code"].get(), action_json); } } @@ -3314,7 +3181,9 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ if (!key_field_only) { /* temperature */ - DevBed::ParseV1_0(jj,m_bed); + m_axis->ParseAxis(jj); + DevBed::ParseV1_0(jj, m_bed); + m_chamber->ParseChamber(jj); if (jj.contains("frame_temper")) { if (jj["frame_temper"].is_number()) { @@ -3324,16 +3193,6 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ ExtderSystemParser::ParseV1_0(jj, m_extder_system); - if (jj.contains("chamber_temper")) { - if (jj["chamber_temper"].is_number()) { - chamber_temp = jj["chamber_temper"].get(); - } - } - if (jj.contains("ctt")) { - if (jj["ctt"].is_number()) { - chamber_temp_target = jj["ctt"].get(); - } - } /* signals */ if (jj.contains("link_th_state")) link_th = jj["link_th_state"].get(); @@ -3398,33 +3257,8 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ catch (...) { ; } - - /* get fimware type */ - try { - if (jj.contains("mess_production_state")) { - if (jj["mess_production_state"].get() == "engineer") - firmware_type = PrinterFirmwareType::FIRMWARE_TYPE_ENGINEER; - else if (jj["mess_production_state"].get() == "product") - firmware_type = PrinterFirmwareType::FIRMWARE_TYPE_PRODUCTION; - } - } - catch (...) { - ; - } } if (!key_field_only) { - try { - if (jj.contains("lifecycle")) { - if (jj["lifecycle"].get() == "engineer") - lifecycle = PrinterFirmwareType::FIRMWARE_TYPE_ENGINEER; - else if (jj["lifecycle"].get() == "product") - lifecycle = PrinterFirmwareType::FIRMWARE_TYPE_PRODUCTION; - } - } - catch (...) { - ; - } - try { if (jj.contains("lights_report") && jj["lights_report"].is_array()) { for (auto it = jj["lights_report"].begin(); it != jj["lights_report"].end(); it++) { @@ -3456,105 +3290,14 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ } #pragma region upgrade + m_upgrade->ParseUpgrade_V1_0(jj); + try { if (jj.contains("upgrade_state")) { - if (jj["upgrade_state"].contains("status")) - upgrade_status = jj["upgrade_state"]["status"].get(); - if (jj["upgrade_state"].contains("progress")) { - upgrade_progress = jj["upgrade_state"]["progress"].get(); - } if (jj["upgrade_state"].contains("new_version_state")) - upgrade_new_version = jj["upgrade_state"]["new_version_state"].get() == 1 ? true : false; if (!check_enable_np(jj) && jj["upgrade_state"].contains("ams_new_version_number"))/* is not used in new np, by AP*/ ams_new_version_number = jj["upgrade_state"]["ams_new_version_number"].get(); - if (jj["upgrade_state"].contains("ota_new_version_number")) - ota_new_version_number = jj["upgrade_state"]["ota_new_version_number"].get(); if (jj["upgrade_state"].contains("ahb_new_version_number")) ahb_new_version_number = jj["upgrade_state"]["ahb_new_version_number"].get(); - if (jj["upgrade_state"].contains("module")) - upgrade_module = jj["upgrade_state"]["module"].get(); - if (jj["upgrade_state"].contains("message")) - upgrade_message = jj["upgrade_state"]["message"].get(); - if (jj["upgrade_state"].contains("consistency_request")) - upgrade_consistency_request = jj["upgrade_state"]["consistency_request"].get(); - if (jj["upgrade_state"].contains("force_upgrade")) - upgrade_force_upgrade = jj["upgrade_state"]["force_upgrade"].get(); - if (jj["upgrade_state"].contains("err_code")) - upgrade_err_code = jj["upgrade_state"]["err_code"].get(); - if (jj["upgrade_state"].contains("dis_state")) { - if ((int)upgrade_display_state != jj["upgrade_state"]["dis_state"].get() - && jj["upgrade_state"]["dis_state"].get() == 3) { - GUI::wxGetApp().CallAfter([this] { - this->command_get_version(); - }); - } - if (upgrade_display_hold_count > 0) - { - upgrade_display_hold_count--; - } - else - { - upgrade_display_state = (DevFirmwareUpgradingState)jj["upgrade_state"]["dis_state"].get(); - if ((upgrade_display_state == DevFirmwareUpgradingState::UpgradingAvaliable) && is_lan_mode_printer()) - { - upgrade_display_state = DevFirmwareUpgradingState::UpgradingUnavaliable; - } - } - } - else { - if (upgrade_display_hold_count > 0) - upgrade_display_hold_count--; - else { - //QDS compatibility with old version - if (upgrade_status == "DOWNLOADING" - || upgrade_status == "FLASHING" - || upgrade_status == "UPGRADE_REQUEST" - || upgrade_status == "PRE_FLASH_START" - || upgrade_status == "PRE_FLASH_SUCCESS") { - upgrade_display_state = DevFirmwareUpgradingState::UpgradingInProgress; - } - else if (upgrade_status == "UPGRADE_SUCCESS" - || upgrade_status == "DOWNLOAD_FAIL" - || upgrade_status == "FLASH_FAIL" - || upgrade_status == "PRE_FLASH_FAIL" - || upgrade_status == "UPGRADE_FAIL") { - upgrade_display_state = DevFirmwareUpgradingState::UpgradingFinished; - } - else { - if (upgrade_new_version) { - upgrade_display_state = DevFirmwareUpgradingState::UpgradingAvaliable; - } - else { - upgrade_display_state = DevFirmwareUpgradingState::UpgradingUnavaliable; - } - } - } - } - // new ver list - if (jj["upgrade_state"].contains("new_ver_list")) { - m_new_ver_list_exist = true; - new_ver_list.clear(); - for (auto ver_item = jj["upgrade_state"]["new_ver_list"].begin(); ver_item != jj["upgrade_state"]["new_ver_list"].end(); ver_item++) { - DevFirmwareVersionInfo ver_info; - if (ver_item->contains("name")) - ver_info.name = (*ver_item)["name"].get(); - else - continue; - - if (ver_item->contains("cur_ver")) - ver_info.sw_ver = (*ver_item)["cur_ver"].get(); - if (ver_item->contains("new_ver")) - ver_info.sw_new_ver = (*ver_item)["new_ver"].get(); - - if (ver_info.name == "ota") { - ota_new_version_number = ver_info.sw_new_ver; - } - - new_ver_list.insert(std::make_pair(ver_info.name, ver_info)); - } - } - else { - new_ver_list.clear(); - } } } catch (...) { @@ -3682,6 +3425,9 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ default: break; } + is_support_build_plate_type_detect = true; + + xcam_build_plate_align_detect.UpdateValue(get_flag_bits(cfg, 20)); } else if (jj["xcam"].contains("printing_monitor")) { // new protocol @@ -3717,6 +3463,9 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ is_support_build_plate_marker_detect = false; } } + if (jj["xcam"].contains("buildplate_marker_detector")){ + xcam_build_plate_type_detect.UpdateValue(jj["xcam"]["buildplate_marker_detector"].get()); + } } } catch (...) { @@ -3814,7 +3563,7 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ } catch (...) {} #pragma endregion } else if (jj["command"].get() == "gcode_line") { - if (m_agent && is_studio_cmd(sequence_id)) { + if (m_agent && DevUtil::is_studio_cmd(sequence_id)) { json t; //t["dev_id"] = this->get_dev_id(); t["dev_id"] = ""; @@ -3919,6 +3668,10 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ if (time(nullptr) - xcam_buildplate_marker_hold_start > HOLD_TIME_3SEC) { xcam_buildplate_marker_detector = enable; } + xcam_build_plate_type_detect.UpdateValue(enable); + } + else if (jj["module_name"].get() == "plate_offset_switch") { + xcam_build_plate_align_detect.UpdateValue(enable); } else if (jj["module_name"].get() == "printing_monitor") { if (time(nullptr) - xcam_ai_monitoring_hold_start > HOLD_TIME_3SEC) { @@ -4120,6 +3873,14 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ pa_calib_result.nozzle_volume_type = convert_to_nozzle_type((*it)["nozzle_id"].get()); } + if ((*it).contains("nozzle_pos")) { + pa_calib_result.nozzle_pos_id = (*it)["nozzle_pos"].get(); + } + + if ((*it).contains("nozzle_sn")) { + pa_calib_result.nozzle_sn = (*it)["nozzle_sn"].get(); + } + if (jj["nozzle_diameter"].is_number_float()) { pa_calib_result.nozzle_diameter = jj["nozzle_diameter"].get(); } else if (jj["nozzle_diameter"].is_string()) { @@ -4215,6 +3976,14 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ pa_calib_result.nozzle_volume_type = NozzleVolumeType::nvtStandard; } + if (it->contains("nozzle_pos")) { + pa_calib_result.nozzle_pos_id = (*it)["nozzle_pos"].get(); + } + + if (it->contains("nozzle_sn")) { + pa_calib_result.nozzle_sn = (*it)["nozzle_sn"].get(); + } + if ((*it)["k_value"].is_number_float()) pa_calib_result.k_value = (*it)["k_value"].get(); else if ((*it)["k_value"].is_string()) @@ -4318,18 +4087,12 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ try { if (j.contains("upgrade")) { if (j["upgrade"].contains("command")) { - if (j["upgrade"]["command"].get() == "upgrade_confirm") { - this->upgrade_display_state = DevFirmwareUpgradingState::UpgradingInProgress; - upgrade_display_hold_count = HOLD_COUNT_MAX; - BOOST_LOG_TRIVIAL(info) << "ack of upgrade_confirm"; - } - bool check_studio_cmd = true; if (j["upgrade"].contains("sequence_id")) { try { std::string str_seq = j["upgrade"]["sequence_id"].get(); - check_studio_cmd = is_studio_cmd(stoi(str_seq)); + check_studio_cmd = DevUtil::is_studio_cmd(stoi(str_seq)); } catch (...) { } } @@ -4367,9 +4130,10 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ } catch (const nlohmann::json::exception& e) { // Handle JSON parsing exceptions if necessary - BOOST_LOG_TRIVIAL(trace) << "parse_json failed! dev_id=" << QDTCrossTalk::Crosstalk_DevId(this->dev_id) <<", ewhat = " << e.what(); - } catch (...) { - BOOST_LOG_TRIVIAL(trace) << "parse_json failed! dev_id=" << QDTCrossTalk::Crosstalk_DevId(this->dev_id); + BOOST_LOG_TRIVIAL(trace) << "parse_json failed! dev_id=" << QDTCrossTalk::Crosstalk_DevId(this->get_dev_id()) << ", ewhat = " << e.what(); + } + catch (...) { + BOOST_LOG_TRIVIAL(trace) << "parse_json failed! dev_id=" << QDTCrossTalk::Crosstalk_DevId(this->get_dev_id()); } std::chrono::system_clock::time_point clock_stop = std::chrono::system_clock::now(); @@ -4895,10 +4659,10 @@ bool MachineObject::contains_tray(const std::string &ams_id, const std::string & return false; } +/*use contains_tray or is_tray_info_ready to check*/ DevAmsTray MachineObject::get_tray(const std::string &ams_id, const std::string &tray_id) const { - if (ams_id.empty() && tray_id.empty()) - { + if (ams_id.empty() && tray_id.empty()) { return DevAmsTray(tray_id); } @@ -4912,7 +4676,6 @@ DevAmsTray MachineObject::get_tray(const std::string &ams_id, const std::string } } - assert(0);/*use contains_tray() check first*/ return DevAmsTray(tray_id); } @@ -4952,8 +4715,6 @@ void MachineObject::parse_new_info(json print) m_fila_system->GetAmsSystemSetting().SetDetectOnPowerupEnabled(get_flag_bits(cfg, 1)); } - upgrade_force_upgrade = get_flag_bits(cfg, 2); - if (time(nullptr) - camera_recording_ctl_start > HOLD_COUNT_MAX) { camera_recording_when_printing = get_flag_bits(cfg, 3); @@ -5053,9 +4814,7 @@ void MachineObject::parse_new_info(json print) is_support_nozzle_blob_detection = get_flag_bits(fun, 13); is_support_upgrade_kit = get_flag_bits(fun, 14); is_support_internal_timelapse = get_flag_bits(fun, 28); - m_support_mqtt_homing = get_flag_bits(fun, 32); is_support_brtc = get_flag_bits(fun, 31); - m_support_mqtt_axis_control = get_flag_bits(fun, 38); m_support_mqtt_bet_ctrl = get_flag_bits(fun, 39); is_support_new_auto_cali_method = get_flag_bits(fun, 40); is_support_spaghetti_detection = get_flag_bits(fun, 42); @@ -5066,6 +4825,7 @@ void MachineObject::parse_new_info(json print) is_support_ext_change_assist = get_flag_bits(fun, 48); is_support_partskip = get_flag_bits(fun, 49); is_support_idelheadingprotect_detection = get_flag_bits(fun, 62); + m_nozzle_system->SetSupportNozzleRack(get_flag_bits(fun, 60)); } /*fun2*/ @@ -5078,6 +4838,8 @@ void MachineObject::parse_new_info(json print) // fun2 may have infinite length, use get_flag_bits_no_border if (!fun2.empty()) { is_support_print_with_emmc = get_flag_bits_no_border(fun2, 0) == 1; + is_support_build_plate_align_detect = get_flag_bits_no_border(fun2, 2) == 1; + is_support_pa_mode = (get_flag_bits_no_border(fun2, 3) == 1); } /*aux*/ @@ -5099,33 +4861,20 @@ void MachineObject::parse_new_info(json print) m_lamp->SetLampCloseRecheck((get_flag_bits(stat, 36) == 1)); } + m_dev_info->ParseInfo(print); + /*device*/ if (print.contains("device")) { json const& device = print["device"]; - //new fan data m_fan->ParseV3_0(device); - if (device.contains("type")) { - m_device_mode = (DeviceMode)device["type"].get();// FDM:1<<0 Laser:1<< Cut:1<<2 - } + DevBed::ParseV2_0(device, m_bed); + DevNozzleSystemParser::ParseV2_0(device, m_nozzle_system); - DevBed::ParseV2_0(device,m_bed); - - if (device.contains("nozzle")) { DevNozzleSystemParser::ParseV2_0(device["nozzle"], m_nozzle_system); } if (device.contains("extruder")) { ExtderSystemParser::ParseV2_0(device["extruder"], m_extder_system);} if (device.contains("ext_tool")) { DevExtensionToolParser::ParseV2_0(device["ext_tool"], m_extension_tool); } - - if (device.contains("ctc")) { - json const& ctc = device["ctc"]; - int state = get_flag_bits(ctc["state"].get(), 0, 4); - if (ctc.contains("info")) { - json const &info = ctc["info"]; - chamber_temp = get_flag_bits(info["temp"].get(), 0, 16); - chamber_temp_target = get_flag_bits(info["temp"].get(), 16, 16); - } - } } } @@ -5560,6 +5309,7 @@ std::string MachineObject::get_error_code_str(int error_code) void MachineObject::add_command_error_code_dlg(int command_err, json action_json) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << command_err; if (command_err > 0 && !Slic3r::GUI::wxGetApp().get_hms_query()->is_internal_error(this, command_err)) { GUI::wxGetApp().CallAfter([this, command_err, action_json, token = std::weak_ptr(m_token)] @@ -5589,6 +5339,47 @@ int MachineObject::get_extruder_id_by_ams_id(const std::string& ams_id) return m_fila_system->GetExtruderIdByAmsId(ams_id); } +DevNozzle MachineObject::get_nozzle_by_id_code(int id_code) const +{ + /* toolhead nozzle*/ + if (id_code == MAIN_EXTRUDER_ID || id_code == DEPUTY_EXTRUDER_ID) { + int nozzle_id = m_extder_system->GetExtderById(id_code)->GetNozzleId(); + return m_nozzle_system->GetExtNozzle(nozzle_id); + } else if (id_code >= 0x10) { + /* rack nozzle*/ + auto rack = m_nozzle_system->GetNozzleRack(); + auto nozzle_map = rack->GetRackNozzles(); + return nozzle_map[id_code - 0x10]; + } else { + BOOST_LOG_TRIVIAL(error) << "Invalid nozzle pos id: " << id_code << ", replace with main extuder nozzle"; + return m_nozzle_system->GetExtNozzle(0); + } +} + +DevNozzle MachineObject::get_nozzle_by_sn(const std::string& sn) const +{ + int nozzle_id; + DevNozzle nozzle; + + nozzle_id = m_extder_system->GetExtderById(MAIN_EXTRUDER_ID)->GetNozzleId(); + nozzle = m_nozzle_system->GetExtNozzle(nozzle_id); + if(nozzle.GetSerialNumber().compare(sn) == 0) + return nozzle; + + nozzle_id = m_extder_system->GetExtderById(DEPUTY_EXTRUDER_ID)->GetNozzleId(); + nozzle = m_nozzle_system->GetExtNozzle(nozzle_id); + if(nozzle.GetSerialNumber().compare(sn) == 0) + return nozzle; + + auto rack = m_nozzle_system->GetNozzleRack(); + auto nozzle_map = rack->GetRackNozzles(); + for(auto& rack_nozzle : nozzle_map){ + if(rack_nozzle.second.GetSerialNumber().compare(sn) == 0) + return rack_nozzle.second; + } + return nozzle; +} + Slic3r::DevPrintingSpeedLevel MachineObject::GetPrintingSpeedLevel() const { return m_print_options->GetPrintingSpeedLevel(); @@ -5621,6 +5412,24 @@ bool MachineObject::HasAms() const return !machine_box_type.empty() && (machine_box_type.find(preset_typename) != std::string::npos) && (dev_ip == machine_ip); } +std::optional MachineObject::IsDetectOnInsertEnabled() const +{ + return m_fila_system->GetAmsSystemSetting().IsDetectOnInsertEnabled(); +} + +std::shared_ptr MachineObject::GetNozzleRack() const +{ + return m_nozzle_system->GetNozzleRack(); +} + +std::string MachineObject::get_dev_id() const { + return m_dev_info->GetDevId(); +} + +void MachineObject::set_dev_id(std::string val) { + m_dev_info->SetDevId(val); +} + void change_the_opacity(wxColour& colour) { if (colour.Alpha() == 255) { diff --git a/src/slic3r/GUI/DeviceManager.hpp b/src/slic3r/GUI/DeviceManager.hpp index 66c0efa..e042423 100644 --- a/src/slic3r/GUI/DeviceManager.hpp +++ b/src/slic3r/GUI/DeviceManager.hpp @@ -21,6 +21,8 @@ #include "DeviceCore/DevDefs.h" #include "DeviceCore/DevConfigUtil.h" #include "DeviceCore/DevFirmware.h" +#include "DeviceCore/DevUtil.h" + #include "DeviceErrorDialog.hpp" #include @@ -47,16 +49,16 @@ #define GET_VERSION_RETRYS 10 #define RETRY_INTERNAL 2000 -#define START_SEQ_ID 20000 -#define END_SEQ_ID 30000 #define SUBSCRIBE_RETRY_COUNT 5 using namespace nlohmann; namespace Slic3r { +class Print; namespace GUI { class DeviceErrorDialog; // Previous definitions +class Plater; } class NetworkAgent; @@ -65,17 +67,12 @@ enum ManualPaCaliMethod { PA_PATTERN, }; - -#define UpgradeNoError 0 -#define UpgradeDownloadFailed -1 -#define UpgradeVerfifyFailed -2 -#define UpgradeFlashFailed -3 -#define UpgradePrinting -4 - // Previous definitions class DevAms; class DevAmsTray; +class DevAxis; class DevBed; +class DevChamber; class DevConfig; class DevCtrl; class DevExtensionTool; @@ -84,11 +81,15 @@ class DevFan; class DevFilaSystem; class DevPrintOptions; class DevHMS; +class DevInfo; class DevLamp; class DevNozzleSystem; +class DevNozzleRack; class DeviceManager; class DevStorage; +class DevUpgrade; struct DevPrintTaskRatingInfo; +struct DevNozzle; class MachineObject @@ -99,16 +100,21 @@ private: std::shared_ptr m_token = std::make_shared(1); /* properties */ - std::string dev_id; std::string dev_name; std::string dev_ip; std::string access_code; std::string user_access_code; + std::shared_ptr m_dev_info; // type, time stamp, delay std::vector> message_delay; + // the latest nozzle mapping + DevNozzleMappingResult m_auto_nozzle_mapping; + /*parts*/ + std::shared_ptr m_axis; + std::shared_ptr m_chamber; DevLamp* m_lamp; std::shared_ptr m_extension_tool; DevExtderSystem* m_extder_system; @@ -124,6 +130,9 @@ private: /*Print Options/Speed*/ DevPrintOptions* m_print_options; + /*Upgrade*/ + std::shared_ptr m_upgrade; + /*HMS*/ DevHMS* m_hms_system; @@ -149,7 +158,7 @@ public: public: /* static members and functions */ - static inline int m_sequence_id = START_SEQ_ID; + static inline int m_sequence_id = STUDIO_START_SEQ_ID; /* properties */ std::string get_dev_name() const { return dev_name; } @@ -158,8 +167,12 @@ public: std::string get_dev_ip() const { return dev_ip; } void set_dev_ip(std::string ip) { dev_ip = ip; } - std::string get_dev_id() const { return dev_id; } - void set_dev_id(std::string val) { dev_id = val; } + std::string get_dev_id() const; + void set_dev_id(std::string val); + + std::string connection_type() const; + bool is_lan_mode_printer() const; + bool is_cloud_mode_printer() const; //y std::string dev_url; std::string dev_apikey; @@ -170,11 +183,9 @@ public: std::string get_ftp_folder(); int subscribe_counter{3}; + std::string dev_connection_name; /* lan | eth */ - std::string dev_connection_type; /* lan | cloud */ - std::string connection_type() const { return dev_connection_type; } - bool is_lan_mode_printer() { return dev_connection_type == "lan"; } - bool is_cloud_mode_printer() { return dev_connection_type == "cloud"; } + /* message time*/ std::chrono::system_clock::time_point last_cloud_msg_time_; std::chrono::system_clock::time_point last_lan_msg_time_; @@ -182,8 +193,6 @@ public: bool HasRecentCloudMessage(); bool HasRecentLanMessage(); - std::string dev_connection_name; /* lan | eth */ - /*access code*/ bool has_access_right() const { return !get_access_code().empty(); } std::string get_access_code() const; @@ -219,8 +228,6 @@ public: void reload_printer_settings(); std::string get_printer_thumbnail_img_str() const; - std::string dev_product_name; // set by iot service, get /user/print - std::string bind_user_name; std::string bind_user_id; std::string bind_sec_link; @@ -228,12 +235,8 @@ public: std::string bind_state; /* free | occupied */ bool is_avaliable() { return bind_state == "free"; } - time_t last_alive; bool m_is_online; - bool m_lan_mode_connection_state{false}; bool m_set_ctt_dlg{ false }; - void set_lan_mode_connection_state(bool state) {m_lan_mode_connection_state = state;}; - bool get_lan_mode_connection_state() {return m_lan_mode_connection_state;}; void set_ctt_dlg( wxString text); int parse_msg_count = 0; int keep_alive_count = 0; @@ -263,7 +266,6 @@ public: int ams_status_sub; int ams_version = 0; - int extrusion_cali_hold_count = 0; std::chrono::system_clock::time_point last_extrusion_cali_start_time; int extrusion_cali_set_tray_id = -1; std::chrono::system_clock::time_point extrusion_cali_set_hold_start; @@ -272,6 +274,9 @@ public: bool is_in_extrusion_cali(); bool is_extrusion_cali_finished(); + /* Networking */ + NetworkAgent *get_agent() const { return m_agent; } + /* AMS */ DevAms* get_curr_Ams(); DevAmsTray* get_curr_tray(); @@ -286,7 +291,6 @@ public: bool is_target_slot_unload() const; bool can_unload_filament(); - bool is_support_amx_ext_mix_mapping() const { return true;} void get_ams_colors(std::vector& ams_colors); @@ -295,10 +299,26 @@ public: bool is_multi_extruders() const; int get_extruder_id_by_ams_id(const std::string& ams_id); + /* nozzle */ + DevNozzle get_nozzle_by_id_code(int id_code) const; + DevNozzle get_nozzle_by_sn(const std::string& sn) const; + + // auto nozzle mapping + DevNozzleMappingResult get_nozzle_mapping_result() const { return m_auto_nozzle_mapping; } + void set_manual_nozzle_mapping(int fila_id, int nozzle_pos_id) { m_auto_nozzle_mapping.SetManualNozzleMapping(this, fila_id, nozzle_pos_id); };// nozzle_pos_id is O\0x10\0x20\0x30... + void clear_auto_nozzle_mapping() { m_auto_nozzle_mapping.Clear(); } + int ctrl_get_auto_nozzle_mapping(Slic3r::GUI::Plater* plater, const std::vector& ams_mapping, int flow_cali_opt, int pa_value); + + /* ams settings*/ + std::optional IsDetectOnInsertEnabled() const; + //bool IsDetectOnPowerupEnabled() const { return m_enable_detect_on_powerup; } + //bool IsDetectRemainEnabled() const { return m_enable_detect_remain; } + //bool IsAutoRefillEnabled() const { return m_enable_auto_refill; } + /* E3D has extra nozzle flow type info */ bool has_extra_flow_type{false}; - [[nodiscard]] bool is_nozzle_flow_type_supported() const { return is_enable_np | has_extra_flow_type; }; + [[nodiscard]] bool is_nozzle_flow_type_supported() const { return is_enable_np | has_extra_flow_type; } [[nodiscard]] wxString get_nozzle_replace_url() const; /*online*/ @@ -308,8 +328,6 @@ public: int last_online_version = -1; /* temperature */ - float chamber_temp; - float chamber_temp_target; float frame_temp; /* signals */ @@ -317,16 +335,20 @@ public: std::string link_th; std::string link_ams; bool network_wired { false }; + std::shared_ptr GetInfo() const { return m_dev_info; } /* parts */ DevExtderSystem* GetExtderSystem() const { return m_extder_system; } std::weak_ptr GetExtensionTool() const { return m_extension_tool; } - DevNozzleSystem* GetNozzleSystem() const { return m_nozzle_system;} + DevNozzleSystem* GetNozzleSystem() const { return m_nozzle_system;} + std::shared_ptr GetNozzleRack() const;; DevFilaSystem* GetFilaSystem() const { return m_fila_system;} bool HasAms() const; + std::shared_ptr GetAxis() const { return m_axis; } + std::shared_ptr GetChamber() const { return m_chamber; } DevLamp* GetLamp() const { return m_lamp; } DevFan* GetFan() const { return m_fan; } DevBed * GetBed() const { return m_bed; }; @@ -339,20 +361,10 @@ public: DevPrintOptions* GetPrintOptions() const { return m_print_options; } /* print options */ DevPrintingSpeedLevel GetPrintingSpeedLevel() const; /* print speed */ + std::weak_ptr GetUpgrade() const { return m_upgrade;} + /* upgrade */ - bool upgrade_force_upgrade { false }; - bool upgrade_new_version { false }; - bool upgrade_consistency_request { false }; - DevFirmwareUpgradingState upgrade_display_state; - int upgrade_display_hold_count = 0; - PrinterFirmwareType firmware_type; // engineer|production - PrinterFirmwareType lifecycle { PrinterFirmwareType::FIRMWARE_TYPE_PRODUCTION }; - std::string upgrade_progress; - std::string upgrade_message; - std::string upgrade_status; - std::string upgrade_module; std::string ams_new_version_number; - std::string ota_new_version_number; std::string ahb_new_version_number; int get_version_retry = 0; @@ -361,19 +373,13 @@ public: DevFirmwareVersionInfo cutting_module_version_info; DevFirmwareVersionInfo extinguish_version_info; std::map module_vers; - std::map new_ver_list; - bool m_new_ver_list_exist = false; - int upgrade_err_code = 0; std::vector firmware_list; std::string get_firmware_type_str(); std::string get_lifecycle_type_str(); bool is_in_upgrading() const; - bool is_upgrading_avalable(); - int get_upgrade_percent() const; std::string get_ota_version(); bool check_version_valid(); - wxString get_upgrade_result_str(int upgrade_err_code); // key: ams_id start as 0,1,2,3 std::map get_ams_version(); @@ -449,13 +455,11 @@ public: std::vector stage_list_info; int stage_curr = 0; - int stage_remaining_seconds = 0; + int stage_remaining_seconds = -1; int m_push_count = 0; int m_full_msg_count = 0; /*the full message count, there are full or diff messages from network*/ bool calibration_done { false }; - bool is_axis_at_home(std::string axis); - bool is_filament_at_extruder(); wxString get_curr_stage(); @@ -528,13 +532,6 @@ public: DOOR_OPEN_CHECK_ENABLE_PAUSE_PRINT = 2,/*pause print*/ }; - enum DeviceMode : unsigned int - { - DEVICE_MODE_FDM = 0x00000001, - DEVICE_MODE_LASER = 0x00000010, - DEVICE_MODE_CUT = 0x00000100, - }; - bool file_model_download{false}; bool virtual_camera{false}; @@ -572,6 +569,13 @@ public: bool is_support_build_plate_marker_detect{false}; PlateMakerDectect m_plate_maker_detect_type{ POS_CHECK }; + /* plate build type & align detect*/ + DevDirtyHandler xcam_build_plate_type_detect{true, HOLD_TIME_3SEC, DirtyMode::TIMER}; + DevDirtyHandler xcam_build_plate_align_detect{true, HOLD_TIME_3SEC, DirtyMode::TIMER}; + + bool is_support_build_plate_type_detect{false}; + bool is_support_build_plate_align_detect{false}; + /*PA flow calibration is using in sending print*/ bool is_support_pa_calibration{false}; bool is_support_flow_calibration{false}; @@ -600,7 +604,6 @@ public: bool is_support_upgrade_kit{false}; bool is_support_filament_setting_inprinting{false}; bool is_support_internal_timelapse { false };// fun[28], support timelapse without SD card - bool m_support_mqtt_homing { false };// fun[32] bool is_support_brtc{false}; // fun[31], support tcp and upload protocol bool is_support_ext_change_assist{false}; bool is_support_partskip{false}; @@ -615,6 +618,7 @@ public: // fun2 bool is_support_print_with_emmc{false}; + bool is_support_pa_mode{false}; bool installed_upgrade_kit{false}; int bed_temperature_limit = -1; @@ -625,8 +629,6 @@ public: /*temp temp range*/ std::vector bed_temp_range; - - /* machine mqtt apis */ int connect(bool use_openssl = true); int disconnect(); @@ -651,10 +653,6 @@ public: bool is_makeworld_subtask(); - /* device type */ - DeviceMode m_device_mode{ DEVICE_MODE_FDM }; - inline bool is_fdm_type() const { return m_device_mode == DEVICE_MODE_FDM; } - int m_plate_index { -1 }; std::string m_gcode_file; int gcode_file_prepare_percent = 0; @@ -683,7 +681,6 @@ public: std::string parse_version(); void parse_version_func(); - bool is_studio_cmd(int seq); /* quick check*/ bool canEnableTimelapse(wxString& error_message) const; @@ -699,16 +696,7 @@ public: int command_get_access_code(); int command_ack_proceed(json& proceed); - /* command upgrade */ - int command_upgrade_confirm(); - int command_consistency_upgrade_confirm(); - int command_upgrade_firmware(FirmwareInfo info); - int command_upgrade_module(std::string url, std::string module_type, std::string version); - /* control apis */ - int command_xyz_abs(); - int command_auto_leveling(); - int command_go_home(); int command_task_abort(); /* cancelled the job_id */ @@ -730,7 +718,6 @@ public: int command_set_nozzle(int temp); int command_set_nozzle_new(int nozzle_id, int temp); int command_refresh_nozzle(); - int command_set_chamber(int temp); int check_resume_condition(); // ams controls //int command_ams_switch(int tray_index, int old_temp = 210, int new_temp = 210); @@ -764,10 +751,6 @@ public: int command_nozzle_blob_detect(bool nozzle_blob_detect); - // axis string is X, Y, Z, E - bool m_support_mqtt_axis_control = false; - int command_axis_control(std::string axis, double unit = 1.0f, double input_val = 1.0f, int speed = 3000); - int command_extruder_control(int nozzle_id, double val); // calibration printer bool is_support_command_calibration(); @@ -807,6 +790,8 @@ public: int command_xcam_control_auto_recovery_step_loss(bool on_off); int command_xcam_control_allow_prompt_sound(bool on_off); int command_xcam_control_filament_tangle_detect(bool on_off); + int command_xcam_control_build_plate_type_detector(bool on_off); + int command_xcam_control_build_plate_align_detector(bool on_off); /* common apis */ inline bool is_local() { return !get_dev_ip().empty(); } @@ -820,7 +805,6 @@ public: bool is_in_printing_pause() const; bool is_in_prepare(); bool is_printing_finished(); - bool is_core_xy(); void reset_update_time(); void reset(); static bool is_in_printing_status(std::string status); diff --git a/src/slic3r/GUI/DeviceTab/CMakeLists.txt b/src/slic3r/GUI/DeviceTab/CMakeLists.txt index 9db6bb5..e67f3bf 100644 --- a/src/slic3r/GUI/DeviceTab/CMakeLists.txt +++ b/src/slic3r/GUI/DeviceTab/CMakeLists.txt @@ -8,5 +8,11 @@ list(APPEND SLIC3R_GUI_SOURCES GUI/DeviceTab/uiAmsHumidityPopup.cpp GUI/DeviceTab/uiDeviceUpdateVersion.h GUI/DeviceTab/uiDeviceUpdateVersion.cpp + GUI/DeviceTab/wgtDeviceNozzleRack.h + GUI/DeviceTab/wgtDeviceNozzleRack.cpp + GUI/DeviceTab/wgtDeviceNozzleRackUpdate.h + GUI/DeviceTab/wgtDeviceNozzleRackUpdate.cpp + GUI/DeviceTab/wgtDeviceNozzleSelect.h + GUI/DeviceTab/wgtDeviceNozzleSelect.cpp ) set(SLIC3R_GUI_SOURCES ${SLIC3R_GUI_SOURCES} PARENT_SCOPE) \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceTab/uiDeviceUpdateVersion.cpp b/src/slic3r/GUI/DeviceTab/uiDeviceUpdateVersion.cpp index 85325e6..1406663 100644 --- a/src/slic3r/GUI/DeviceTab/uiDeviceUpdateVersion.cpp +++ b/src/slic3r/GUI/DeviceTab/uiDeviceUpdateVersion.cpp @@ -1,7 +1,7 @@ //**********************************************************/ /* File: uiDeviceUpdateVersion.cpp * Description: The panel with firmware info -* +* * \n class uiDeviceUpdateVersion //**********************************************************/ @@ -80,7 +80,6 @@ void uiDeviceUpdateVersion::CreateWidgets() serial_text->SetFont(font); version_text->SetFont(font); model_text->SetFont(font); - // The grid sizer wxFlexGridSizer* grid_sizer = new wxFlexGridSizer(0, 2, 0, 0); //grid_sizer->AddGrowableCol(1); @@ -105,7 +104,6 @@ void uiDeviceUpdateVersion::CreateWidgets() // Updating wxSizer* main_sizer = new wxBoxSizer(wxVERTICAL); - main_sizer->AddSpacer(FromDIP(40)); main_sizer->Add(grid_sizer, 0, wxALIGN_LEFT, FromDIP(5)); SetSizer(main_sizer); diff --git a/src/slic3r/GUI/DeviceTab/wgtDeviceNozzleRack.cpp b/src/slic3r/GUI/DeviceTab/wgtDeviceNozzleRack.cpp new file mode 100644 index 0000000..550a8d7 --- /dev/null +++ b/src/slic3r/GUI/DeviceTab/wgtDeviceNozzleRack.cpp @@ -0,0 +1,1020 @@ +//**********************************************************/ +/* File: wgtDeviceNozzleRack.cpp +* Description: The panel with rack info +* +* \n class wgtDeviceNozzleRackArea; +* \n class wgtDeviceNozzleRackNozzleItem; +* \n class wgtDeviceNozzleRackToolHead; +* \n class wgtDeviceNozzleRackPos; +//**********************************************************/ + +#include "wgtDeviceNozzleRack.h" +#include "wgtDeviceNozzleRackUpdate.h" + +#include "slic3r/GUI/DeviceCore/DevNozzleSystem.h" + +#include "slic3r/GUI/I18N.hpp" +#include "slic3r/GUI/MainFrame.hpp" +#include "slic3r/GUI/MsgDialog.hpp" +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/wxExtensions.hpp" + +#include "slic3r/GUI/Widgets/Button.hpp" +#include "slic3r/GUI/Widgets/Label.hpp" + +#include + +#define WX_DIP_SIZE_18 wxSize(FromDIP(18), FromDIP(18)) +#define WX_DIP_SIZE_46 wxSize(FromDIP(46), FromDIP(46)) +#define WX_DIP_SIZE(x, y) wxSize(FromDIP(x), FromDIP(y)) + +#define WGT_RACK_NOZZLE_SIZE WX_DIP_SIZE(88, 100) + +#define L_RAW_A_STR _L("Row A") +#define L_RAW_B_STR _L("Row B") + +static wxColour s_gray_clr("#B0B0B0"); +static wxColour s_hgreen_clr("#4479fb"); +static wxColour s_red_clr("#D01B1B"); + +static std::vector a_nozzle_seq = { 0, 2, 4, 1, 3, 5 }; +static std::vector b_nozzle_seq = { 1, 3, 5, 0, 2, 4 }; + +wxDEFINE_EVENT(EVT_NOZZLE_RACK_NOZZLE_ITEM_SELECTED, wxCommandEvent); + +namespace Slic3r::GUI +{ + +static wxBitmap SetNozzleBmpColor(const wxBitmap& bmp, const std::string& color_str) { + if(color_str.empty()) return bmp; + + wxImage img = bmp.ConvertToImage(); + wxColour color("#" + color_str); + + for (int y = 0; y < img.GetHeight(); ++y) { + for (int x = 0; x < img.GetWidth(); ++x) { + unsigned char r = img.GetRed(x, y); + unsigned char g = img.GetGreen(x, y); + unsigned char b = img.GetBlue(x, y); + + /*replace yellow with color*/ + if ( r >= 180 && g >= 180 && b <= 150) { + img.SetRGB(x, y, color.Red(), color.Green(), color.Blue()); + } + } + } + + return wxBitmap(img, -1, bmp.GetScaleFactor()); +} + +wgtDeviceNozzleRack::wgtDeviceNozzleRack(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style) + : wxPanel(parent, id, pos, size, style) +{ + CreateGui(); +} + +void wgtDeviceNozzleRack::CreateGui() +{ + m_toolhead_panel = new wgtDeviceNozzleRackToolHead(this); + m_rack_area = new wgtDeviceNozzleRackArea(this); + + wxPanel* separator = new wxPanel(this); + separator->SetMaxSize(wxSize(FromDIP(1), -1)); + separator->SetMinSize(wxSize(FromDIP(1), -1)); + separator->SetBackgroundColour(WXCOLOUR_GREY300); + + wxSizer* main_sizer = new wxBoxSizer(wxHORIZONTAL); + main_sizer->AddStretchSpacer(); + main_sizer->Add(m_toolhead_panel, 0, wxEXPAND); + main_sizer->Add(separator, 0, wxEXPAND); + main_sizer->Add(m_rack_area, 0, wxEXPAND); + main_sizer->AddStretchSpacer(); + + SetSizer(main_sizer); + SetMaxSize(WX_DIP_SIZE(586, -1)); + SetMinSize(WX_DIP_SIZE(586, -1)); + SetSize(WX_DIP_SIZE(586, -1)); + Layout(); + + wxGetApp().UpdateDarkUIWin(this); +} + +void wgtDeviceNozzleRack::UpdateRackInfo(std::shared_ptr rack) +{ + if (!rack->IsSupported()) { return; } + + m_nozzle_rack = rack; + if (m_nozzle_rack.expired()) { return; } + + DevNozzleSystem* nozzle_system = m_nozzle_rack.lock()->GetNozzleSystem(); + if (nozzle_system) + { + m_toolhead_panel->UpdateToolHeadInfo(nozzle_system->GetExtNozzle(MAIN_EXTRUDER_ID)); + m_rack_area->UpdateRackInfo(m_nozzle_rack); + } +} + +void wgtDeviceNozzleRack::Rescale() +{ + m_toolhead_panel->Rescale(); + m_rack_area->Rescale(); + Layout(); +} + +class wgtDeviceNozzleRackTitle : public StaticBox +{ +public: + wgtDeviceNozzleRackTitle(wxWindow* parent, const wxString& title) : StaticBox(parent) + { + SetBackgroundColour(WXCOLOUR_GREY200); + SetBorderColor(*wxWHITE); + SetCornerRadius(0); + + m_title_label = new Label(this, title); + m_title_label->SetFont(Label::Body_14); + m_title_label->SetBackgroundColour(WXCOLOUR_GREY200); + + wxSizer* title_sizer = new wxBoxSizer(wxHORIZONTAL); + title_sizer->AddStretchSpacer(); + title_sizer->Add(m_title_label, 0, wxEXPAND | wxALIGN_CENTER | wxTOP | wxBOTTOM, FromDIP(5)); + title_sizer->AddStretchSpacer(); + SetSizer(title_sizer); + }; + +public: + void SetLabel(const wxString& new_label) { m_title_label->SetLabel(new_label); } + +private: + Label* m_title_label; +}; + + +void wgtDeviceNozzleRackToolHead::CreateGui() +{ + wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL); + + // Create Header + wgtDeviceNozzleRackTitle* title_box = new wgtDeviceNozzleRackTitle(this, _L("Toolhead")); + mainSizer->Add(title_box, 0, wxEXPAND | wxTOP); + mainSizer->AddStretchSpacer(); + + // Image + m_extruder_nozzle_empty = new ScalableBitmap(this, "dev_rack_toolhead_empty", 98); + m_extruder_nozzle_normal = new ScalableBitmap(this, "dev_rack_toolhead_normal", 98); + m_toolhead_icon = new wxStaticBitmap(this, wxID_ANY, m_extruder_nozzle_empty->bmp(), wxDefaultPosition, WX_DIP_SIZE(98, 98)); + mainSizer->Add(m_toolhead_icon, 0, wxALIGN_CENTRE_HORIZONTAL | wxTOP, FromDIP(20)); + + // Nozzle info + m_nozzle_diamenter_label = new Label(this); + m_nozzle_diamenter_label->SetFont(Label::Body_13); + m_nozzle_diamenter_label->SetBackgroundColour(*wxWHITE); + mainSizer->Add(m_nozzle_diamenter_label, 0, wxALIGN_CENTRE_HORIZONTAL | wxBOTTOM | wxTOP, FromDIP(5)); + + m_nozzle_flowtype_label = new Label(this); + m_nozzle_flowtype_label->SetFont(Label::Body_13); + m_nozzle_flowtype_label->SetBackgroundColour(*wxWHITE); + mainSizer->Add(m_nozzle_flowtype_label, 0, wxALIGN_CENTRE_HORIZONTAL); + mainSizer->AddStretchSpacer(); + + // Set sizer + SetSizer(mainSizer); + SetMaxSize(WX_DIP_SIZE(132, -1)); + SetMinSize(WX_DIP_SIZE(132, -1)); + SetSize(WX_DIP_SIZE(132, -1)); +} + +void wgtDeviceNozzleRackToolHead::UpdateToolHeadInfo(const DevNozzle& extruder_nozzle) +{ + /* Labels */ + if (extruder_nozzle.IsEmpty()) + { + m_nozzle_diamenter_label->Show(false); + m_nozzle_flowtype_label->SetLabel(_L("Empty")); + } + else if (extruder_nozzle.IsUnknown()) + { + m_nozzle_diamenter_label->Show(false); + m_nozzle_flowtype_label->SetLabel(_L("Unknown")); + } + else if (extruder_nozzle.IsAbnormal()) + { + m_nozzle_diamenter_label->Show(false); + m_nozzle_flowtype_label->SetLabel(_L("Error")); + } + else /*extruder_nozzle.IsNormal()*/ + { + m_nozzle_diamenter_label->Show(true); + m_nozzle_diamenter_label->SetLabel(extruder_nozzle.GetNozzleDiameterStr()); + m_nozzle_flowtype_label->SetLabel(extruder_nozzle.GetNozzleFlowTypeStr()); + } + + /* Icon*/ + bool extruder_exist = !extruder_nozzle.IsEmpty(); + if (m_extruder_nozzle_exist != extruder_exist) + { + m_extruder_nozzle_exist = extruder_exist; + m_filament_color = extruder_nozzle.GetFilamentColor(); + m_toolhead_icon->SetBitmap(m_extruder_nozzle_exist ? SetNozzleBmpColor(m_extruder_nozzle_normal->bmp(), m_filament_color) : m_extruder_nozzle_empty->bmp()); + m_toolhead_icon->Refresh(); + } +} + +void wgtDeviceNozzleRackToolHead::Rescale() +{ + m_extruder_nozzle_normal->msw_rescale(); + m_extruder_nozzle_empty->msw_rescale(); + m_toolhead_icon->SetBitmap(m_extruder_nozzle_exist ? SetNozzleBmpColor(m_extruder_nozzle_normal->bmp(), m_filament_color) : m_extruder_nozzle_empty->bmp()); + + Layout(); + Refresh(); +} + +void wgtDeviceNozzleRackArea::CreateGui() +{ + wxSizer* main_sizer = new wxBoxSizer(wxVERTICAL); + + // Create Header + m_title_nozzle_rack = new wgtDeviceNozzleRackTitle(this, _L("Induction Hotend Rack")); + main_sizer->Add(m_title_nozzle_rack, 0, wxEXPAND | wxTOP); + + // Create Simple Book + m_simple_book = new wxSimplebook(this, wxID_ANY); + + wxSizer* content_sizer = new wxBoxSizer(wxVERTICAL); + + m_panel_content = new wxPanel(m_simple_book, wxID_ANY); + m_panel_refresh = new wxPanel(m_simple_book, wxID_ANY); + + // Create Hotends ans Rack Position Panel + wxSizer* hotends_rack_sizer = new wxBoxSizer(wxHORIZONTAL); + + // Hotends + m_hotends_sizer = new wxBoxSizer(wxVERTICAL); + m_arow_nozzles_box = CreateNozzleBox( { 0, 2, 4}); + m_brow_nozzles_box = CreateNozzleBox( { 1, 3, 5}); + m_hotends_sizer->Add(m_arow_nozzles_box); + m_hotends_sizer->Add(m_brow_nozzles_box); + hotends_rack_sizer->Add(m_hotends_sizer, 0, wxLEFT, FromDIP(8)); + + // Rack + m_rack_pos_panel = new wgtDeviceNozzleRackPos(m_panel_content); + hotends_rack_sizer->Add(m_rack_pos_panel, 0, wxEXPAND); + content_sizer->Add(hotends_rack_sizer, 0); + + wxSizer* btn_sizer = new wxBoxSizer(wxHORIZONTAL); + m_btn_hotends_infos = new Button(m_panel_content, _L("Hotends Info")); + m_btn_hotends_infos->SetFont(Label::Body_12); + m_btn_hotends_infos->SetBackgroundColor(StateColor::createButtonStyleGray()); + m_btn_hotends_infos->SetBackgroundColour(*wxWHITE); + m_btn_hotends_infos->Bind(wxEVT_BUTTON, &wgtDeviceNozzleRackArea::OnBtnHotendsInfos, this); + + m_btn_read_all = new Button(m_panel_content, _L("Read All")); + m_btn_read_all->SetFont(Label::Body_12); + m_btn_read_all->SetBackgroundColor(StateColor::createButtonStyleGray()); + m_btn_read_all->SetBackgroundColour(*wxWHITE); + m_btn_read_all->Bind(wxEVT_BUTTON, &wgtDeviceNozzleRackArea::OnBtnReadAll, this); + + btn_sizer->Add(m_btn_hotends_infos, 0, wxLEFT); + btn_sizer->Add(m_btn_read_all, 0, wxLEFT, FromDIP(5)); + content_sizer->Add(btn_sizer, 0, wxLEFT, FromDIP(10)); + + /* refresh panel */ + wxSizer* refresh_sizer = CreateRefreshBook(m_panel_refresh); + + m_panel_content->SetSizer(content_sizer); + m_panel_refresh->SetSizer(refresh_sizer); + m_simple_book->AddPage(m_panel_content, "Content"); + m_simple_book->AddPage(m_panel_refresh, "Refresh"); + main_sizer->Add(m_simple_book, 1, wxEXPAND); + + m_simple_book->SetSelection(0); + + SetSizer(main_sizer); + Layout(); + Fit(); +} + +wxSizer* wgtDeviceNozzleRackArea::CreateRefreshBook(wxPanel* parent) +{ + wxSizer* refresh_sizer = new wxBoxSizer(wxVERTICAL); + + std::vector list{"ams_rfid_1", "ams_rfid_2", "ams_rfid_3", "ams_rfid_4"}; + m_refresh_icon = new AnimaIcon(parent, wxID_ANY, list, "refresh_printer", 100); + m_refresh_icon->SetMinSize(wxSize(FromDIP(25), FromDIP(25))); + + wxSizer* progress_sizer = new wxBoxSizer(wxHORIZONTAL); + + Label* progress_prefix = new Label(parent, _L("Reading ")); + progress_prefix->SetBackgroundColour(*wxWHITE); + m_progress_refresh = new Label(parent, "(1/6)"); + m_progress_refresh->SetFont(Label::Body_14); + m_progress_refresh->SetBackgroundColour(*wxWHITE); + m_progress_refresh->SetForegroundColour(*wxGREEN); + Label* progress_suffix = new Label(parent, " ..."); + progress_suffix->SetBackgroundColour(*wxWHITE); + + progress_sizer->Add(progress_prefix, 0, wxLEFT); + progress_sizer->Add(m_progress_refresh, 0, wxLEFT); + progress_sizer->Add(progress_suffix, 0, wxLEFT); + + Label* refresh_tip = new Label(parent, _L("Please wait")); + refresh_tip->SetBackgroundColour(*wxWHITE); + + refresh_sizer->Add(0, 0, 1, wxEXPAND, 0); + refresh_sizer->Add(m_refresh_icon, 0, wxALIGN_CENTER_HORIZONTAL, 0); + refresh_sizer->Add(progress_sizer, 0, wxALIGN_CENTER_HORIZONTAL, FromDIP(0)); + refresh_sizer->Add(refresh_tip, 0, wxALIGN_CENTER_HORIZONTAL, FromDIP(0)); + refresh_sizer->Add(0, 0, 1, wxEXPAND, 0); + + return refresh_sizer; +} + +StaticBox* wgtDeviceNozzleRackArea::CreateNozzleBox(const std::vector nozzle_idxes) +{ + StaticBox* nozzle_box = new StaticBox(m_panel_content); + nozzle_box->SetBackgroundColor(*wxWHITE); + nozzle_box->SetBorderColor(*wxWHITE); + nozzle_box->SetCornerRadius(0); + + wxSizer* h_sizer = new wxBoxSizer(wxHORIZONTAL); + for (auto start_idx : nozzle_idxes) + { + wgtDeviceNozzleRackNozzleItem* nozzle_item = new wgtDeviceNozzleRackNozzleItem(nozzle_box, start_idx); + m_nozzle_items[start_idx] = nozzle_item; + h_sizer->Add(nozzle_item, 0, wxALL, FromDIP(8)); + } + + nozzle_box->SetSizer(h_sizer); + return nozzle_box; +} + +static void s_update_title(const std::shared_ptr rack, wgtDeviceNozzleRackTitle* title_label) +{ + wxString title = _L("Induction Hotend Rack"); + if (rack && (rack->GetReadingCount() > 0)) + { + wxString pending = ": " + _L("Reading") + wxString::Format(" %d/%d", rack->GetReadingIdx(), rack->GetReadingCount()); + title += pending; + } + + title_label->SetLabel(title); +} + +#if 0 +static void s_update_readall_btn(const std::shared_ptr rack, Button* btn) +{ + wxString label = _L("Read All"); + if (rack && rack->HasUnreliableNozzles()) + { + label += "*"; + } + + if (btn->GetLabel() != label) + { + btn->SetLabel(label); + } + + btn->Enable(rack->CtrlCanReadAll()); +}; +#endif + +void wgtDeviceNozzleRackArea::UpdateNozzleItems(const std::unordered_map& nozzle_items, + std::shared_ptr nozzle_rack) +{ + for (auto iter : nozzle_items) + { + iter.second->Update(nozzle_rack); + } + + /*update nozzle possition and background*/ + if (nozzle_rack->GetReadingCount() != 0) + { + m_progress_refresh->SetLabel(wxString::Format("(%d/%d)", nozzle_rack->GetReadingIdx(), nozzle_rack->GetReadingCount())); + if(!m_refresh_icon->IsPlaying()) { + m_simple_book->SetSelection(1); + m_refresh_icon->Play(); + } + return; + } else{ + m_refresh_icon->Stop(); + m_simple_book->SetSelection(0); + } + + const DevNozzleRack::RackPos new_pos = nozzle_rack->GetPosition(); + const DevNozzleRack::RackStatus new_status = nozzle_rack->GetStatus(); + if (m_rack_pos != new_pos || m_rack_status != new_status) + { + m_rack_pos = new_pos; + m_rack_status = new_status; + if (m_rack_status == DevNozzleRack::RACK_STATUS_IDLE) + { + m_hotends_sizer->Clear(); + if (m_rack_pos == DevNozzleRack::RACK_POS_B_TOP) + { + m_hotends_sizer->Add(m_brow_nozzles_box); + m_hotends_sizer->Add(m_arow_nozzles_box); + } + else if (m_rack_pos == DevNozzleRack::RACK_POS_A_TOP) + { + m_hotends_sizer->Add(m_arow_nozzles_box); + m_hotends_sizer->Add(m_brow_nozzles_box); + } + else + { + m_hotends_sizer->Add(m_arow_nozzles_box); + m_hotends_sizer->Add(m_brow_nozzles_box); + } + } + } +} + +void wgtDeviceNozzleRackArea::UpdateRackInfo(std::weak_ptr rack) +{ + m_nozzle_rack = rack; + const auto& nozzle_rack = rack.lock(); + if (nozzle_rack) + { + // s_update_title(nozzle_rack, m_title_nozzle_rack); + UpdateNozzleItems(m_nozzle_items, nozzle_rack); + m_rack_pos_panel->UpdateRackPos(nozzle_rack); + m_btn_read_all->Enable(nozzle_rack->CtrlCanReadAll()); + } + + if (m_rack_upgrade_dlg && m_rack_upgrade_dlg->IsShown()) + { + m_rack_upgrade_dlg->UpdateRackInfo(nozzle_rack); + } +}; + +void wgtDeviceNozzleRackArea::OnBtnHotendsInfos(wxCommandEvent& evt) +{ + const auto& nozzle_rack = m_nozzle_rack.lock(); + if (nozzle_rack) + { + m_rack_upgrade_dlg = new wgtDeviceNozzleRackUpgradeDlg((wxWindow*)wxGetApp().mainframe, nozzle_rack); + m_rack_upgrade_dlg->ShowModal(); + + delete m_rack_upgrade_dlg; + m_rack_upgrade_dlg = nullptr; + } + + evt.Skip(); +} + +void wgtDeviceNozzleRackArea::OnBtnReadAll(wxCommandEvent& evt) +{ + if (const auto nozzle_rack = m_nozzle_rack.lock()) + { + nozzle_rack->CtrlRackReadAll(true); + } + + evt.Skip(); +} + +void wgtDeviceNozzleRackArea::Rescale() +{ + for (auto item : m_nozzle_items) + { + item.second->Rescale(); + } + + m_rack_pos_panel->Rescale(); + m_btn_hotends_infos->Rescale(); + m_btn_read_all->Rescale(); +} + +static void s_set_bg_style(StaticBox* box, + ScalableButton* btn, + Label* label_row, + Label* label_row_status, + const wxColour& clr) +{ + box->SetBorderColor(clr); + box->SetBackgroundColor(clr); + btn->SetBackgroundColour(clr); + label_row->SetBackgroundColour(clr); + label_row_status->SetBackgroundColour(clr); +} + +void wgtDeviceNozzleRackPos::CreateGui() +{ + // RowA + m_rowup_panel = new StaticBox(this, wxID_ANY); + m_rowup_panel->SetCornerRadius(0); + + wxBoxSizer* rowa_sizer = new wxBoxSizer(wxVERTICAL); + rowa_sizer->AddStretchSpacer(); + m_btn_rowup = new ScalableButton(m_rowup_panel, wxID_ANY, "dev_rack_row_up", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, false, 25); + m_btn_rowup->Bind(wxEVT_ENTER_WINDOW, [this](auto&) { SetCursor(wxCURSOR_HAND); }); + m_btn_rowup->Bind(wxEVT_LEAVE_WINDOW, [this](auto&) { SetCursor(wxCURSOR_ARROW); }); + m_btn_rowup->Bind(wxEVT_BUTTON, &wgtDeviceNozzleRackPos::OnMoveRackUp, this); + rowa_sizer->Add(m_btn_rowup, 0, wxALIGN_CENTER | wxEXPAND | wxLEFT | wxRIGHT, FromDIP(10)); + + m_label_rowup_status = new Label(m_rowup_panel); + m_label_rowup_status->SetFont(Label::Body_12); + m_label_rowup_status->Show(false); + rowa_sizer->Add(m_label_rowup_status, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, FromDIP(10)); + + m_label_rowup = new Label(m_rowup_panel); + m_label_rowup->SetFont(Label::Body_14); + rowa_sizer->Add(m_label_rowup, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, FromDIP(10)); + rowa_sizer->AddStretchSpacer(); + + m_rowup_panel->SetSizer(rowa_sizer); + + // homing + m_btn_homing = new ScalableButton(this, wxID_ANY, "dev_rack_home", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, false, 25); + m_btn_homing->SetBackgroundColour(WXCOLOUR_GREY200); + m_btn_homing->Bind(wxEVT_ENTER_WINDOW, [this](auto&) { SetCursor(wxCURSOR_HAND); }); + m_btn_homing->Bind(wxEVT_LEAVE_WINDOW, [this](auto&) { SetCursor(wxCURSOR_ARROW); }); + m_btn_homing->Bind(wxEVT_BUTTON, &wgtDeviceNozzleRackPos::OnBtnHomingRack, this); + + // Row B + m_rowbottom_panel = new StaticBox(this, wxID_ANY); + m_rowbottom_panel->SetCornerRadius(0); + + wxBoxSizer* rowb_sizer = new wxBoxSizer(wxVERTICAL); + rowb_sizer->AddStretchSpacer(); + + m_btn_rowbottom_up = new ScalableButton(m_rowbottom_panel, wxID_ANY, "dev_rack_row_up", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, false, 25); + m_btn_rowbottom_up->Bind(wxEVT_BUTTON, &wgtDeviceNozzleRackPos::OnMoveRackDown, this); + m_btn_rowbottom_up->Bind(wxEVT_ENTER_WINDOW, [this](auto&) { SetCursor(wxCURSOR_HAND); }); + m_btn_rowbottom_up->Bind(wxEVT_LEAVE_WINDOW, [this](auto&) { SetCursor(wxCURSOR_ARROW); }); + rowb_sizer->Add(m_btn_rowbottom_up, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, FromDIP(10)); + + m_label_rowbottom_status = new Label(m_rowbottom_panel); + m_label_rowbottom_status->SetFont(Label::Body_12); + m_label_rowbottom_status->Show(false); + rowb_sizer->Add(m_label_rowbottom_status, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, FromDIP(10)); + + m_label_rowbottom = new Label(m_rowbottom_panel); + m_label_rowbottom->SetFont(Label::Body_14); + rowb_sizer->Add(m_label_rowbottom, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, FromDIP(10)); + rowb_sizer->AddStretchSpacer(); + + m_rowbottom_panel->SetSizer(rowb_sizer); + + // bg style + SetBackgroundColour(*wxWHITE); + s_set_bg_style(m_rowup_panel, m_btn_rowup, m_label_rowup, m_label_rowup_status, *wxWHITE); + s_set_bg_style(m_rowbottom_panel, m_btn_rowbottom_up, m_label_rowbottom, m_label_rowbottom_status, *wxWHITE); + + // main sizer + wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL); + main_sizer->Add(m_rowup_panel, 1, wxALIGN_TOP | wxEXPAND | wxALIGN_CENTER); + main_sizer->Add(m_btn_homing, 0, wxALIGN_CENTER | wxTOP | wxBOTTOM, FromDIP(10)); + main_sizer->Add(m_rowbottom_panel, 1, wxALIGN_BOTTOM | wxEXPAND | wxALIGN_CENTER); + SetSizer(main_sizer); + + SetMinSize(WX_DIP_SIZE(85, -1)); + + Layout(); + Fit(); +} + +void wgtDeviceNozzleRackPos::UpdateRackPos(const std::shared_ptr& rack) +{ + m_rack = rack; + if (rack) + { + UpdateRackPos(rack->GetPosition(), rack->GetStatus(), rack->GetReadingCount() > 0); + } +} + +static void s_show_label(Label* label, const wxString& text) +{ + label->SetLabel(text); + label->Show(); +} + +static void s_show_label(Label* label, const wxString& text, const wxColour& text_color) +{ + label->SetLabel(text); + label->SetForegroundColour(StateColor::darkModeColorFor(text_color)); + label->Show(); +} + +void wgtDeviceNozzleRackPos::UpdateRackPos(DevNozzleRack::RackPos new_pos, + DevNozzleRack::RackStatus new_status, bool is_reading) +{ + /* di*/ + if (is_reading) + { + s_show_label(m_label_rowup, L_RAW_A_STR, *wxBLACK); + s_show_label(m_label_rowup_status, _L("Running...")); + + s_show_label(m_label_rowbottom, L_RAW_B_STR, *wxBLACK); + s_show_label(m_label_rowbottom_status, _L("Running...")); + + m_btn_rowup->Show(false); + m_btn_rowbottom_up->Show(false); + + m_rack_pos = DevNozzleRack::RACK_POS_UNKNOWN; + m_rack_status = DevNozzleRack::RACK_STATUS_UNKNOWN; + return; + } + + if (new_pos != m_rack_pos || m_rack_status != new_status) + { + m_rack_pos = new_pos; + m_rack_status = new_status; + + if (m_rack_status != DevNozzleRack::RACK_STATUS_IDLE) + { + s_show_label(m_label_rowup, L_RAW_A_STR, *wxBLACK); + s_show_label(m_label_rowup_status, _L("Running...")); + + s_show_label(m_label_rowbottom, L_RAW_B_STR, *wxBLACK); + s_show_label(m_label_rowbottom_status, _L("Running...")); + + m_btn_rowup->Show(false); + m_btn_rowbottom_up->Show(false); + } + else + { + if (new_pos == DevNozzleRack::RACK_POS_A_TOP) + { + s_show_label(m_label_rowup, L_RAW_A_STR, s_hgreen_clr); + s_show_label(m_label_rowup_status, _L("Raised")); + + m_rowbottom_panel->SetBorderColor(*wxWHITE); + m_rowbottom_panel->SetBackgroundColor(*wxWHITE); + s_show_label(m_label_rowbottom, L_RAW_B_STR, *wxBLACK); + m_label_rowbottom_status->Show(false); + + m_btn_rowup->Show(false); + m_btn_rowbottom_up->Show(true); + } + else if (new_pos == DevNozzleRack::RACK_POS_B_TOP) + { + s_show_label(m_label_rowup, L_RAW_B_STR, s_hgreen_clr); + s_show_label(m_label_rowup_status, _L("Raised")); + s_show_label(m_label_rowbottom, L_RAW_A_STR, *wxBLACK); + m_label_rowbottom_status->Show(false); + + m_btn_rowup->Show(false); + m_btn_rowbottom_up->Show(true); + } + else + { + s_show_label(m_label_rowup, L_RAW_A_STR, *wxBLACK); + m_label_rowup_status->Show(false); + + s_show_label(m_label_rowbottom, L_RAW_B_STR, *wxBLACK); + m_label_rowbottom_status->Show(false); + + m_btn_rowup->Show(true); + m_btn_rowbottom_up->Show(true); + } + } + + Layout(); + Refresh(); + } +}; + +void wgtDeviceNozzleRackPos::OnMoveRackUp(wxCommandEvent& evt) +{ + auto rack = m_rack.lock(); + if (rack) + { + if (m_label_rowup->GetLabel() == L_RAW_A_STR) + { + rack->CtrlRackPosMove(DevNozzleRack::RACK_POS_A_TOP); + } + else if (m_label_rowup->GetLabel() == L_RAW_B_STR) + { + rack->CtrlRackPosMove(DevNozzleRack::RACK_POS_B_TOP); + } + } + evt.Skip(); +} + +void wgtDeviceNozzleRackPos::OnMoveRackDown(wxCommandEvent& evt) +{ + auto rack = m_rack.lock(); + if (rack) + { + if (m_label_rowbottom->GetLabel() == L_RAW_A_STR) + { + rack->CtrlRackPosMove(DevNozzleRack::RACK_POS_A_TOP); + } + else if (m_label_rowbottom->GetLabel() == L_RAW_B_STR) + { + rack->CtrlRackPosMove(DevNozzleRack::RACK_POS_B_TOP); + } + } + evt.Skip(); +} + +void wgtDeviceNozzleRackPos::OnBtnHomingRack(wxCommandEvent& evt) +{ + if (auto rack = m_rack.lock()) + { + rack->CtrlRackPosGoHome(); + } + evt.Skip(); +} + +void wgtDeviceNozzleRackPos::Rescale() +{ + m_btn_rowup->msw_rescale(); + m_btn_rowbottom_up->msw_rescale(); + m_btn_homing->msw_rescale(); +} + +wgtDeviceNozzleRackNozzleItem::wgtDeviceNozzleRackNozzleItem(wxWindow* parent, int nozzle_id) + : StaticBox(parent, wxID_ANY), m_nozzle_id(nozzle_id) +{ + CreateGui(); +} + +void wgtDeviceNozzleRackNozzleItem::CreateGui() +{ + // Background + SetCornerRadius(FromDIP(5)); + SetBackgroundColor(*wxWHITE); + + // Top H + wxSizer *top_h_sizer = new wxBoxSizer(wxHORIZONTAL); + + m_nozzle_label_id = new Label(this); + m_nozzle_label_id->SetFont(Label::Body_12); + m_nozzle_label_id->SetBackgroundColour(*wxWHITE); + m_nozzle_label_id->SetLabel(wxString::Format("%d", m_nozzle_id + 1)); + + m_status = NOZZLE_STATUS::NOZZLE_EMPTY; + m_nozzle_empty_image = new ScalableBitmap(this, "dev_rack_nozzle_empty", 46); + m_nozzle_icon = new wxStaticBitmap(this, wxID_ANY, m_nozzle_empty_image->bmp(), wxDefaultPosition, WX_DIP_SIZE_46); + m_nozzle_icon->SetBackgroundColour(*wxWHITE); + + m_nozzle_selected_bitmap = new wxStaticBitmap(this, wxID_ANY, wxNullBitmap, wxDefaultPosition, WX_DIP_SIZE(20, 20)); + m_nozzle_selected_bitmap->SetBackgroundColour(*wxWHITE); + + top_h_sizer->Add(m_nozzle_label_id, 0, wxTOP | wxLEFT, FromDIP(6)); + top_h_sizer->AddSpacer(FromDIP(10)); + top_h_sizer->Add(m_nozzle_icon, 0, wxTOP, FromDIP(10)); + top_h_sizer->AddStretchSpacer(); + top_h_sizer->Add(m_nozzle_selected_bitmap, 0, wxTOP | wxRIGHT, FromDIP(2)); + + // Bottom V + wxBoxSizer* bottom_v = new wxBoxSizer(wxVERTICAL); + + wxSizer* label_h_sizer = new wxBoxSizer(wxHORIZONTAL); + m_nozzle_label_1 = new Label(this); + m_nozzle_label_1->SetFont(Label::Body_12); + m_nozzle_label_1->SetBackgroundColour(*wxWHITE); + m_nozzle_label_1->SetLabel(_L("Empty")); + + label_h_sizer->Add(m_nozzle_label_1, 0, wxALIGN_LEFT); + + auto status_icon = create_scaled_bitmap("dev_rack_nozzle_error_icon", this, 14); + m_nozzle_status_icon = new wxStaticBitmap(this, wxID_ANY, status_icon, wxDefaultPosition, WX_DIP_SIZE(14, 14)); + m_nozzle_status_icon->Bind(wxEVT_LEFT_DOWN, &wgtDeviceNozzleRackNozzleItem::OnBtnNozzleStatus, this); + m_nozzle_status_icon->Bind(wxEVT_ENTER_WINDOW, [this](auto&) { SetCursor(wxCURSOR_HAND); }); + m_nozzle_status_icon->Bind(wxEVT_LEAVE_WINDOW, [this](auto&) { SetCursor(wxCURSOR_ARROW); }); + m_nozzle_status_icon->SetBackgroundColour(*wxWHITE); + m_nozzle_status_icon->Show(false); + + label_h_sizer->Add(m_nozzle_status_icon, 0, wxALIGN_CENTER | wxLEFT, FromDIP(2)); + bottom_v->Add(label_h_sizer, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP, FromDIP(2)); + + m_nozzle_label_2 = new Label(this); + m_nozzle_label_2->SetFont(Label::Body_12); + m_nozzle_label_2->SetBackgroundColour(*wxWHITE); + bottom_v->Add(m_nozzle_label_2, 0, wxALIGN_CENTER_HORIZONTAL); + + // Main sizer + wxSizer* main_sizer = new wxBoxSizer(wxVERTICAL); + main_sizer->Add(top_h_sizer); + main_sizer->Add(bottom_v, 0, wxALIGN_CENTER_HORIZONTAL); + SetSizer(main_sizer); + + SetMinSize(WGT_RACK_NOZZLE_SIZE); + SetMaxSize(WGT_RACK_NOZZLE_SIZE); + SetSize(WGT_RACK_NOZZLE_SIZE); + Layout(); +}; + +void wgtDeviceNozzleRackNozzleItem::SetSelected(bool selected) +{ + if (!m_enable_select){ + assert(false && "not support select"); + return; + } + + if (m_is_selected != selected) { + m_is_selected = selected; + if (selected) { + if (!m_nozzle_selected_image) { + m_nozzle_selected_image = new ScalableBitmap(this, "dev_rack_nozzle_selected", 20); + } + + m_nozzle_selected_bitmap->SetBitmap(m_nozzle_selected_image->bmp()); + SetBorderColor(StateColor::darkModeColorFor(s_hgreen_clr)); + } else { + m_nozzle_selected_bitmap->SetBitmap(wxNullBitmap); + SetBorderColor(StateColor::darkModeColorFor(s_gray_clr)); + } + + Refresh(); + } +} + +void wgtDeviceNozzleRackNozzleItem::Update(const std::shared_ptr rack, bool on_rack /*= true*/) +{ + m_rack = rack; + + if (rack) { + const auto &nozzle_info = on_rack ? rack->GetNozzle(m_nozzle_id) : rack->GetNozzleSystem()->GetExtNozzle(m_nozzle_id); + const wxString &diameter_str = nozzle_info.GetNozzleDiameterStr(); + const wxString &flowtype_str = nozzle_info.GetNozzleFlowTypeStr(); + const std::string &color = nozzle_info.GetFilamentColor(); + + /*check empty first*/ + if (nozzle_info.IsEmpty()) { + SetNozzleStatus(NOZZLE_STATUS::NOZZLE_EMPTY, _L("Empty"), wxEmptyString, color); + } else if (nozzle_info.IsNormal()) { + SetNozzleStatus(NOZZLE_STATUS::NOZZLE_NORMAL, diameter_str, flowtype_str, color); + } else if (nozzle_info.IsAbnormal()) { + SetNozzleStatus(NOZZLE_STATUS::NOZZLE_ERROR, _L("Error"), wxEmptyString, color); + } else if (nozzle_info.IsUnknown()) { + SetNozzleStatus(NOZZLE_STATUS::NOZZLE_UNKNOWN, _L("Unknown"), wxEmptyString, color); + } + } +} + +void wgtDeviceNozzleRackNozzleItem::SetNozzleStatus(NOZZLE_STATUS status, const wxString& str1, const wxString& str2, const std::string& color) +{ + if (m_status != status || m_filament_color != color) + { + m_status = status; + m_filament_color = color; + switch (status) + { + case Slic3r::GUI::wgtDeviceNozzleRackNozzleItem::NOZZLE_EMPTY: + { + if (!m_nozzle_empty_image) { m_nozzle_empty_image = new ScalableBitmap(this, "dev_rack_nozzle_empty", 46);} + m_nozzle_icon->SetBitmap(m_nozzle_empty_image->bmp()); + break; + } + case Slic3r::GUI::wgtDeviceNozzleRackNozzleItem::NOZZLE_NORMAL: + { + if (!m_nozzle_normal_image) { m_nozzle_normal_image = new ScalableBitmap(this, "dev_rack_nozzle_normal", 46);} + m_nozzle_icon->SetBitmap(SetNozzleBmpColor(m_nozzle_normal_image->bmp(), m_filament_color)); + break; + } + case Slic3r::GUI::wgtDeviceNozzleRackNozzleItem::NOZZLE_UNKNOWN: + { + if (!m_nozzle_unknown_image) { m_nozzle_unknown_image = new ScalableBitmap(this, "dev_rack_nozzle_unknown", 46);} + m_nozzle_icon->SetBitmap(m_nozzle_unknown_image->bmp()); + break; + } + case Slic3r::GUI::wgtDeviceNozzleRackNozzleItem::NOZZLE_ERROR: + { + if (!m_nozzle_error_image) { m_nozzle_error_image = new ScalableBitmap(this, "dev_rack_nozzle_error", 46);} + m_nozzle_icon->SetBitmap(m_nozzle_error_image->bmp()); + break; + } + default: + { + break; + } + } + + if (status == wgtDeviceNozzleRackNozzleItem::NOZZLE_ERROR) + { + m_nozzle_label_1->SetForegroundColour(StateColor::darkModeColorFor(s_red_clr)); + m_nozzle_status_icon->Show(true); + } + else + { + m_nozzle_label_1->SetForegroundColour(StateColor::darkModeColorFor(*wxBLACK)); + m_nozzle_status_icon->Show(false); + } + } + + bool update_layout = (m_nozzle_label_1->GetLabel() != str1 || m_nozzle_label_2->GetLabel() != str2); + m_nozzle_label_1->SetLabel(str1); + m_nozzle_label_2->SetLabel(str2); + + if (update_layout) { + Layout(); + } +} + +void wgtDeviceNozzleRackNozzleItem::OnBtnNozzleStatus(wxMouseEvent& evt) +{ + if (m_is_disabled) { + return; + } + + auto rack = m_rack.lock(); + if (rack && m_status == wgtDeviceNozzleRackNozzleItem::NOZZLE_ERROR) + { + MessageDialog dlg(nullptr, _L("The hotend is in an abnormal state and currently unavailable. " + "Please go to 'Device -> Upgrade' to upgrade firmware."), _L("Abnormal Hotend"), wxICON_WARNING); + dlg.AddButton(wxID_CANCEL, _L("Cancel"), false); + dlg.AddButton(wxID_OK,_L("Jump to the upgrade page"), true); + + if (dlg.ShowModal() == wxID_OK) { + wxGetApp().mainframe->m_monitor->jump_to_Upgrade(); + }; + } +} + +void wgtDeviceNozzleRackNozzleItem::Rescale() +{ + if (m_nozzle_normal_image) { m_nozzle_normal_image->msw_rescale(); } + if (m_nozzle_empty_image) { m_nozzle_empty_image->msw_rescale(); } + if (m_nozzle_unknown_image) { m_nozzle_unknown_image->msw_rescale(); } + if (m_nozzle_error_image) { m_nozzle_error_image->msw_rescale(); } + + auto status_icon = create_scaled_bitmap("dev_rack_nozzle_error_icon", this, 14); + m_nozzle_status_icon->SetBitmap(status_icon); + m_nozzle_status_icon->Refresh(); + + if (m_nozzle_selected_image) { + m_nozzle_selected_image->msw_rescale(); + if (m_is_selected) { + m_nozzle_selected_bitmap->SetBitmap(m_nozzle_selected_image->bmp()); + } + }; + + switch (m_status) + { + case Slic3r::GUI::wgtDeviceNozzleRackNozzleItem::NOZZLE_EMPTY: + { + m_nozzle_icon->SetBitmap(m_nozzle_empty_image->bmp()); + break; + } + case Slic3r::GUI::wgtDeviceNozzleRackNozzleItem::NOZZLE_NORMAL: + { + m_nozzle_icon->SetBitmap(SetNozzleBmpColor(m_nozzle_normal_image->bmp(), m_filament_color)); + break; + } + case Slic3r::GUI::wgtDeviceNozzleRackNozzleItem::NOZZLE_UNKNOWN: + { + m_nozzle_icon->SetBitmap(m_nozzle_unknown_image->bmp()); + break; + } + case Slic3r::GUI::wgtDeviceNozzleRackNozzleItem::NOZZLE_ERROR: + { + m_nozzle_icon->SetBitmap(m_nozzle_error_image->bmp()); + break; + } + default: + { + break; + } + }; +}; + +void wgtDeviceNozzleRackNozzleItem::EnableSelect() +{ + if (m_enable_select == true) { + return; + }; + + m_enable_select = true; + m_nozzle_icon->Bind(wxEVT_LEFT_DOWN, [this](auto& evt) { OnItemSelected(evt); }); + m_nozzle_label_id->Bind(wxEVT_LEFT_DOWN, [this](auto& evt) { OnItemSelected(evt); }); + m_nozzle_label_1->Bind(wxEVT_LEFT_DOWN, [this](auto& evt) { OnItemSelected(evt); }); + m_nozzle_label_2->Bind(wxEVT_LEFT_DOWN, [this](auto& evt) { OnItemSelected(evt); }); + Bind(wxEVT_LEFT_DOWN, [this](auto& evt) { OnItemSelected(evt); }); +} + +void wgtDeviceNozzleRackNozzleItem::OnItemSelected(wxMouseEvent& evt) +{ + if (m_enable_select && !m_is_disabled){ + SetSelected(true); + wxCommandEvent command_evt(EVT_NOZZLE_RACK_NOZZLE_ITEM_SELECTED, GetId()); + command_evt.SetEventObject(this); + ProcessEvent(command_evt); + } + + evt.Skip(); +} + + +void wgtDeviceNozzleRackNozzleItem::SetDisable(bool disabled) +{ + if (m_is_disabled == disabled) { + return; + } + + m_is_disabled = disabled; + + auto bg_clr = disabled ? StateColor::darkModeColorFor("#E5E7EB") : StateColor::darkModeColorFor(*wxWHITE); + m_nozzle_icon->SetBackgroundColour(bg_clr); + m_nozzle_label_id->SetBackgroundColour(bg_clr); + m_nozzle_label_1->SetBackgroundColour(bg_clr); + m_nozzle_status_icon->SetBackgroundColour(bg_clr); + m_nozzle_label_2->SetBackgroundColour(bg_clr); + m_nozzle_selected_bitmap->SetBackgroundColour(bg_clr); + + SetBackgroundColor(bg_clr); + Refresh(); +}; + +}; diff --git a/src/slic3r/GUI/DeviceTab/wgtDeviceNozzleRack.h b/src/slic3r/GUI/DeviceTab/wgtDeviceNozzleRack.h new file mode 100644 index 0000000..fe12b8b --- /dev/null +++ b/src/slic3r/GUI/DeviceTab/wgtDeviceNozzleRack.h @@ -0,0 +1,255 @@ +//**********************************************************/ +/* File: wgtDeviceNozzleRack.h +* Description: The panel with rack and nozzles +* +* \n class wgtDeviceNozzleRackArea; +* \n class wgtDeviceNozzleRackNozzleItem; +* \n class wgtDeviceNozzleRackToolHead; +* \n class wgtDeviceNozzleRackPos; +//**********************************************************/ + +#pragma once +#include "slic3r/GUI/DeviceCore/DevNozzleRack.h" + +#include "slic3r/GUI/Widgets/StaticBox.hpp" +#include "slic3r/GUI/Widgets/AnimaController.hpp" + +#include +#include +#include + +// Previous definitions +class Button; +class Label; +class ScalableBitmap; +class ScalableButton; +namespace Slic3r +{ + struct DevNozzle; + class DevNozzleRack; +namespace GUI +{ + class wgtDeviceNozzleRackArea; + class wgtDeviceNozzleRackNozzleItem; + class wgtDeviceNozzleRackToolHead; + class wgtDeviceNozzleRackPos; + class wgtDeviceNozzleRackTitle; + class wgtDeviceNozzleRackUpgradeDlg; +} +}; + +// Events +wxDECLARE_EVENT(EVT_NOZZLE_RACK_NOZZLE_ITEM_SELECTED, wxCommandEvent); + +namespace Slic3r::GUI +{ +class wgtDeviceNozzleRack : public wxPanel +{ +public: + wgtDeviceNozzleRack(wxWindow* parent, + wxWindowID id = wxID_ANY, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = wxTAB_TRAVERSAL); + ~wgtDeviceNozzleRack() = default; + +public: + void UpdateRackInfo(std::shared_ptr rack); + void Rescale(); + +private: + void CreateGui(); + +private: + std::weak_ptr m_nozzle_rack; + + // GUI + wgtDeviceNozzleRackToolHead* m_toolhead_panel{ nullptr }; + wgtDeviceNozzleRackArea* m_rack_area{ nullptr }; +}; + + +class wgtDeviceNozzleRackToolHead : public wxPanel +{ +public: + wgtDeviceNozzleRackToolHead(wxWindow* parent) : wxPanel(parent) { CreateGui();} + +public: + void UpdateToolHeadInfo(const DevNozzle& extruder_nozzle); + void Rescale(); + +private: + void CreateGui(); + +private: + bool m_extruder_nozzle_exist = false; + std::string m_filament_color; + + // GUI + ScalableBitmap* m_extruder_nozzle_normal = nullptr; + ScalableBitmap* m_extruder_nozzle_empty = nullptr; + wxStaticBitmap* m_toolhead_icon; + + Label* m_nozzle_diamenter_label; + Label* m_nozzle_flowtype_label; +}; + + +class wgtDeviceNozzleRackArea : public wxPanel +{ +public: + wgtDeviceNozzleRackArea(wxWindow* parent) : wxPanel(parent) { CreateGui();} + +public: + void UpdateRackInfo(std::weak_ptr rack); + void Rescale(); + +private: + void CreateGui(); + StaticBox* CreateNozzleBox(const std::vector nozzle_idxes); + wxSizer* CreateRefreshBook(wxPanel* parent); + + // updates + void UpdateNozzleItems(const std::unordered_map& nozzle_items, + std::shared_ptr nozzle_rack); + + // events + void OnBtnHotendsInfos(wxCommandEvent& evt); + void OnBtnReadAll(wxCommandEvent& evt); + +private: + std::weak_ptr m_nozzle_rack; + DevNozzleRack::RackPos m_rack_pos = DevNozzleRack::RACK_POS_UNKNOWN; + DevNozzleRack::RackStatus m_rack_status = DevNozzleRack::RACK_STATUS_UNKNOWN; + + // GUI + wxSimplebook* m_simple_book{ nullptr }; + wxPanel* m_panel_content{ nullptr }; + wxPanel* m_panel_refresh{ nullptr }; + + wgtDeviceNozzleRackTitle* m_title_nozzle_rack; + wxBoxSizer* m_hotends_sizer; + StaticBox* m_arow_nozzles_box; + StaticBox* m_brow_nozzles_box; + std::unordered_map m_nozzle_items; + + wgtDeviceNozzleRackPos* m_rack_pos_panel; + + Button* m_btn_hotends_infos; + Button* m_btn_read_all; + + /* refresh book */ + Label* m_progress_refresh{ nullptr }; + AnimaIcon* m_refresh_icon{ nullptr }; + + wgtDeviceNozzleRackUpgradeDlg* m_rack_upgrade_dlg = nullptr; +}; + +class wgtDeviceNozzleRackPos : public wxPanel +{ +public: + explicit wgtDeviceNozzleRackPos(wxWindow* parent) : wxPanel(parent) { CreateGui();} + +public: + void UpdateRackPos(const std::shared_ptr& rack); + void Rescale(); + +private: + void CreateGui(); + + void UpdateRackPos(DevNozzleRack::RackPos new_pos, + DevNozzleRack::RackStatus new_status, + bool is_reading); + + // events + void OnMoveRackUp(wxCommandEvent& evt); + void OnMoveRackDown(wxCommandEvent& evt); + void OnBtnHomingRack(wxCommandEvent& evt); + +private: + std::weak_ptr m_rack; + DevNozzleRack::RackPos m_rack_pos = DevNozzleRack::RACK_POS_UNKNOWN; + DevNozzleRack::RackStatus m_rack_status = DevNozzleRack::RACK_STATUS_UNKNOWN; + + // GUI + StaticBox* m_rowup_panel; + ScalableButton* m_btn_rowup; + Label* m_label_rowup_status{ nullptr }; + Label* m_label_rowup{ nullptr }; + + StaticBox* m_rowbottom_panel; + ScalableButton* m_btn_rowbottom_up; + Label* m_label_rowbottom_status{ nullptr }; + Label* m_label_rowbottom{ nullptr }; + + ScalableButton* m_btn_homing{ nullptr }; +}; + +class wgtDeviceNozzleRackNozzleItem : public StaticBox +{ +public: + enum NOZZLE_STATUS + { + NOZZLE_EMPTY, + NOZZLE_NORMAL, + NOZZLE_UNKNOWN, + NOZZLE_ERROR + }; + +public: + wgtDeviceNozzleRackNozzleItem(wxWindow* parent, int nozzle_id); + +public: + void Update(const std::shared_ptr rack, bool on_rack = true); // on_rack is false means extruder nozzle + + int GetNozzleId() const { return m_nozzle_id; } + void SetDisplayIdText(const wxString& text) { m_nozzle_label_id->SetLabel(text);}; + + void EnableSelect();; + void SetSelected(bool selected); + bool IsSelected() const { return m_is_selected; } + + bool IsDisabled() const { return m_is_disabled; } + void SetDisable(bool disabled); + + void Rescale(); + +private: + void CreateGui(); + + void SetNozzleStatus(NOZZLE_STATUS status, const wxString& str1, const wxString& str2, const std::string& color); + + void OnBtnNozzleStatus(wxMouseEvent& evt); + void OnItemSelected(wxMouseEvent& evt); + +private: + std::weak_ptr m_rack; + + int m_nozzle_id; // internal id, from 0 to 5 + std::string m_filament_color; + NOZZLE_STATUS m_status = NOZZLE_STATUS::NOZZLE_EMPTY; + + // select + bool m_is_selected = false; + bool m_enable_select = false; + ScalableBitmap* m_nozzle_selected_image{ nullptr }; + wxStaticBitmap* m_nozzle_selected_bitmap{ nullptr }; + + // enable or disable + bool m_is_disabled = false; + + // Images + ScalableBitmap* m_nozzle_normal_image{ nullptr }; + ScalableBitmap* m_nozzle_empty_image{ nullptr }; + ScalableBitmap* m_nozzle_unknown_image{ nullptr }; + ScalableBitmap* m_nozzle_error_image{ nullptr }; + + // GUI + wxStaticBitmap* m_nozzle_icon{ nullptr }; + Label* m_nozzle_label_id { nullptr }; + Label* m_nozzle_label_1{ nullptr }; + wxStaticBitmap* m_nozzle_status_icon = nullptr; + Label* m_nozzle_label_2{ nullptr }; +}; + +};// end of namespace Slic3r::GUI \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceTab/wgtDeviceNozzleRackUpdate.cpp b/src/slic3r/GUI/DeviceTab/wgtDeviceNozzleRackUpdate.cpp new file mode 100644 index 0000000..f65ed2c --- /dev/null +++ b/src/slic3r/GUI/DeviceTab/wgtDeviceNozzleRackUpdate.cpp @@ -0,0 +1,393 @@ +//**********************************************************/ +/* File: wgtDeviceNozzleRackUpdate.cpp +* Description: The panel with rack updating +* +* \n class wgtDeviceNozzleRackUpdate +//**********************************************************/ + +#include "wgtDeviceNozzleRackUpdate.h" + +#include "slic3r/GUI/DeviceCore/DevNozzleSystem.h" + +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/I18N.hpp" +#include "slic3r/GUI/MsgDialog.hpp" +#include "slic3r/GUI/wxExtensions.hpp" + +#include "slic3r/GUI/Widgets/Button.hpp" +#include "slic3r/GUI/Widgets/Label.hpp" + +#define WX_DIP_SIZE(x, y) wxSize(FromDIP(x), FromDIP(y)) + +static wxColour s_red_clr("#D01B1B"); + +namespace Slic3r::GUI +{ + +wgtDeviceNozzleRackUpgradeDlg::wgtDeviceNozzleRackUpgradeDlg(wxWindow* parent, const std::shared_ptr rack) + : DPIDialog(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX) +{ + m_rack_upgrade_panel = new wgtDeviceNozzleRackUprade(this); + m_rack_upgrade_panel->UpdateRackInfo(rack); + + auto main_sizer = new wxBoxSizer(wxVERTICAL); + main_sizer->Add(m_rack_upgrade_panel, 0, wxEXPAND); + SetSizer(main_sizer); + Layout(); + Fit(); + + wxGetApp().UpdateDlgDarkUI(this); +} + +void wgtDeviceNozzleRackUpgradeDlg::UpdateRackInfo(const std::shared_ptr rack) +{ + m_rack_upgrade_panel->UpdateRackInfo(rack); +} + +void wgtDeviceNozzleRackUpgradeDlg::on_dpi_changed(const wxRect& suggested_rect) +{ + m_rack_upgrade_panel->Rescale(); +} + +wgtDeviceNozzleRackUprade::wgtDeviceNozzleRackUprade(wxWindow* parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style) + : wxPanel(parent, id, pos, size, style) +{ + CreateGui(); +} + +void wgtDeviceNozzleRackUprade::CreateGui() +{ + SetBackgroundColour(*wxWHITE); + + // Main vertical sizer + auto* main_sizer = new wxBoxSizer(wxVERTICAL); + + // Header: title + buttons + auto* header_sizer = new wxBoxSizer(wxHORIZONTAL); + + // Title label + auto* title_label = new Label(this, _L("Hotends Info")); + title_label->SetFont(Label::Head_14); + header_sizer->Add(title_label, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(20)); + + // Spacer + header_sizer->AddStretchSpacer(); + main_sizer->Add(header_sizer, 0, wxEXPAND | wxTOP | wxRIGHT, FromDIP(10)); + + // "Nozzles" + m_extruder_nozzle_item = new wgtDeviceNozzleRackHotendUpdate(this, "R"); + m_extruder_nozzle_item->UpdateColourStyle(wxColour("#F8F8F8")); + m_extruder_nozzle_item->SetExtruderNozzleId(MAIN_EXTRUDER_ID); + + main_sizer->Add(m_extruder_nozzle_item, 0, wxEXPAND | wxALL, FromDIP(12)); + for (int id = 0; id < 6; id ++) + { + auto item = new wgtDeviceNozzleRackHotendUpdate(this, wxString::Format("%d", id + 1)); + item->SetRackNozzleId(id); + m_nozzle_items[id] = item; + + main_sizer->Add(item, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(12)); + if (id < 5) + { + wxPanel* separator = new wxPanel(this); + separator->SetMaxSize(wxSize(-1, FromDIP(1))); + separator->SetMinSize(wxSize(-1, FromDIP(1))); + separator->SetBackgroundColour(WXCOLOUR_GREY300); + main_sizer->Add(separator, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(12)); + } + } + + main_sizer->AddSpacer(FromDIP(20)); + + // Set sizer + this->SetSizer(main_sizer); + this->Layout(); +} + +void wgtDeviceNozzleRackUprade::UpdateRackInfo(const std::shared_ptr rack) +{ + m_nozzle_rack = rack; + if (!rack) { return;} + + // update the nozzles + m_extruder_nozzle_item->UpdateExtruderNozzleInfo(rack); + for (auto iter : m_nozzle_items) + { + iter.second->UpdateRackNozzleInfo(rack); + } + + // update layout + Layout(); +} + +void wgtDeviceNozzleRackUprade::OnBtnReadAll(wxCommandEvent& e) +{ + if (auto rack = m_nozzle_rack.lock()) + { + rack->CtrlRackReadAll(true); + } +} + +void wgtDeviceNozzleRackUprade::Rescale() +{ + m_extruder_nozzle_item->Rescale(); + for (auto& iter : m_nozzle_items) + { + iter.second->Rescale(); + } +} + +#define WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG *wxWHITE +wgtDeviceNozzleRackHotendUpdate::wgtDeviceNozzleRackHotendUpdate(wxWindow* parent, const wxString& idx_text) + : StaticBox(parent, wxID_ANY) +{ + CreateGui(); + + m_idx_label->SetLabel(idx_text); +} + +void wgtDeviceNozzleRackHotendUpdate::CreateGui() +{ + SetBackgroundColour(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG); + SetBorderColor(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG); + SetCornerRadius(0); + + auto* content_sizer = new wxBoxSizer(wxHORIZONTAL); + + // Index + m_idx_label = new Label(this); + m_idx_label->SetFont(Label::Head_14); + m_idx_label->SetBackgroundColour(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG); + content_sizer->Add(m_idx_label, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(25)); + + // Icon + m_nozzle_empty_image = new ScalableBitmap(this, "dev_rack_nozzle_empty", 46); + m_icon_bitmap = new wxStaticBitmap(this, wxID_ANY, m_nozzle_empty_image->bmp()); + content_sizer->Add(m_icon_bitmap, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(25)); + + // Diameter/type (vertical) + wxPanel* type_panel = new wxPanel(this); + auto* type_sizer = new wxBoxSizer(wxVERTICAL); + m_diameter_label = new Label(type_panel); + m_diameter_label->SetFont(Label::Body_12); + m_diameter_label->SetBackgroundColour(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG); + + m_flowtype_label = new Label(type_panel); + m_flowtype_label->SetFont(Label::Body_12); + m_flowtype_label->SetBackgroundColour(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG); + + m_type_label = new Label(type_panel); + m_type_label->SetFont(Label::Body_12); + m_type_label->SetBackgroundColour(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG); + + type_sizer->AddStretchSpacer(); + type_sizer->Add(m_diameter_label, 0, wxALIGN_LEFT); + type_sizer->Add(m_flowtype_label, 0, wxALIGN_LEFT); + type_sizer->Add(m_type_label, 0, wxALIGN_LEFT); + type_sizer->AddStretchSpacer(); + type_panel->SetSizer(type_sizer); + type_panel->SetMaxSize(WX_DIP_SIZE(100, -1)); + type_panel->SetMinSize(WX_DIP_SIZE(100, -1)); + type_panel->SetSize(WX_DIP_SIZE(100, -1)); + content_sizer->Add(type_panel, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, FromDIP(12)); + + // SN and version (vertical) + auto* info_sizer = new wxBoxSizer(wxVERTICAL); + m_sn_label = new Label(this); + m_sn_label->SetFont(Label::Body_12); + m_sn_label->SetBackgroundColour(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG); + + auto* version_h_sizer = new wxBoxSizer(wxHORIZONTAL); + m_version_label = new Label(this); + m_version_label->SetFont(Label::Body_12); + m_version_label->SetBackgroundColour(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG); + + m_version_new_label = new Label(this); + m_version_new_label->SetFont(Label::Body_12); + m_version_new_label->SetBackgroundColour(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG); + m_version_new_label->SetForegroundColour(wxColour(0, 168, 84)); // Green + + version_h_sizer->Add(m_version_label, 0, wxALIGN_CENTER_VERTICAL); + version_h_sizer->Add(m_version_new_label, 0, wxALIGN_CENTER_VERTICAL); + info_sizer->Add(m_sn_label, 0, wxALIGN_LEFT); + info_sizer->Add(version_h_sizer, 0, wxALIGN_LEFT); + content_sizer->Add(info_sizer, 1, wxALIGN_CENTER_VERTICAL | wxLEFT); + content_sizer->AddSpacer(FromDIP(25)); + + auto* main_sizer = new wxBoxSizer(wxVERTICAL); + main_sizer->Add(content_sizer, 0, wxEXPAND | wxTOP | wxBOTTOM, FromDIP(10)); + + SetSizer(main_sizer); + Layout(); +} + +void wgtDeviceNozzleRackHotendUpdate::UpdateColourStyle(const wxColour& clr) +{ + SetBackgroundColour(clr); + SetBorderColor(clr); + + // BFS: Update all children background color + auto children = GetChildren(); + while (!children.IsEmpty()) + { + auto win = children.front(); + children.pop_front(); + win->SetBackgroundColour(clr); + + for (auto child : win->GetChildren()) + { + children.push_back(child); + } + } +} + +void wgtDeviceNozzleRackHotendUpdate::UpdateExtruderNozzleInfo(const std::shared_ptr rack) +{ + m_nozzle_rack = rack; + if (rack) + { + DevNozzleSystem* nozzle_system = rack->GetNozzleSystem(); + if (nozzle_system) + { + UpdateInfo(nozzle_system->GetExtNozzle(m_ext_nozzle_id)); + } + } +} + +void wgtDeviceNozzleRackHotendUpdate::UpdateRackNozzleInfo(const std::shared_ptr rack) +{ + m_nozzle_rack = rack; + if (rack) + { + UpdateInfo(rack->GetNozzle(m_rack_nozzle_id)); + } +} + +void wgtDeviceNozzleRackHotendUpdate::UpdateInfo(const DevNozzle& nozzle) +{ + if (nozzle.IsEmpty() && m_nozzle_status != NOZZLE_STATUS_EMPTY) + { + m_nozzle_status = NOZZLE_STATUS_EMPTY; + m_diameter_label->Show(false); + m_flowtype_label->Show(false); + m_type_label->SetLabel(_L("Empty")); + m_type_label->SetForegroundColour(StateColor::darkModeColorFor(*wxBLACK)); + + if (!m_nozzle_empty_image) {m_nozzle_empty_image = new ScalableBitmap(this, "dev_rack_nozzle_empty", 46);} + m_icon_bitmap->SetBitmap(m_nozzle_empty_image->bmp()); + m_icon_bitmap->Refresh(); + } + else if (nozzle.IsNormal() && m_nozzle_status != NOZZLE_STATUS_NORMAL) + { + m_nozzle_status = NOZZLE_STATUS_NORMAL; + m_diameter_label->Show(true); + m_flowtype_label->Show(true); + m_diameter_label->SetLabel(nozzle.GetNozzleDiameterStr()); + m_flowtype_label->SetLabel(nozzle.GetNozzleFlowTypeStr()); + m_type_label->SetLabel(nozzle.GetNozzleTypeStr()); + m_type_label->SetForegroundColour(StateColor::darkModeColorFor(*wxBLACK)); + + if (!m_nozzle_image) {m_nozzle_image = new ScalableBitmap(this, "dev_rack_nozzle_normal", 46);} + m_icon_bitmap->SetBitmap(m_nozzle_image->bmp()); + m_icon_bitmap->Refresh(); + } + else if (nozzle.IsAbnormal() && m_nozzle_status != NOZZLE_STATUS_ABNORMAL) + { + m_nozzle_status = NOZZLE_STATUS_ABNORMAL; + m_diameter_label->Show(false); + m_flowtype_label->Show(false); + m_type_label->SetLabel(_L("Error")); + m_type_label->SetForegroundColour(StateColor::darkModeColorFor(s_red_clr)); + + if (!m_nozzle_image) { m_nozzle_image = new ScalableBitmap(this, "dev_rack_nozzle_normal", 46); } + m_icon_bitmap->SetBitmap(m_nozzle_image->bmp()); + m_icon_bitmap->Refresh(); + } + else if (nozzle.IsUnknown() && m_nozzle_status != NOZZLE_STATUS_UNKNOWN) + { + m_nozzle_status = NOZZLE_STATUS_UNKNOWN; + m_diameter_label->Show(false); + m_flowtype_label->Show(false); + m_type_label->SetLabel(_L("Unknown")); + m_type_label->SetForegroundColour(StateColor::darkModeColorFor(*wxBLACK)); + + if (!m_nozzle_image) { m_nozzle_image = new ScalableBitmap(this, "dev_rack_nozzle_normal", 46); } + m_icon_bitmap->SetBitmap(m_nozzle_image->bmp()); + m_icon_bitmap->Refresh(); + } + + // Update firmware info + const DevFirmwareVersionInfo& firmware = nozzle.GetFirmwareInfo(); + if (!nozzle.IsAbnormal()) + { + if (firmware.isValid()) + { + m_sn_label->SetLabel(wxString::Format("%s: %s", _L("SN"), firmware.sn)); + + if (!firmware.sw_new_ver.empty() && firmware.sw_new_ver != firmware.sw_ver) + { + m_version_label->SetLabel(wxString::Format("%s: %s > ", _L("Version"), firmware.sw_ver)); + m_version_new_label->SetLabel(wxString::Format("%s", firmware.sw_new_ver)); + m_version_new_label->Show(true); + } + else + { + m_version_label->SetLabel(wxString::Format("%s:%s", _L("Version"), firmware.sw_ver)); + m_version_new_label->Show(false); + } + } + else + { + m_sn_label->SetLabel(wxString::Format("%s: N/A", _L("SN"))); + m_version_label->SetLabel(wxString::Format("%s: N/A", _L("Version"))); + m_version_new_label->Show(false); + } + } + else /*default to show update icon if IsAbnormal*/ + { + if (firmware.isValid()) + { + m_sn_label->SetLabel(wxString::Format("%s: %s", _L("SN"), firmware.sn)); + + if (!firmware.sw_new_ver.empty() && firmware.sw_new_ver != firmware.sw_ver) + { + m_version_label->SetLabel(wxString::Format("%s: %s > ", _L("Version"), firmware.sw_ver)); + m_version_new_label->SetLabel(wxString::Format("%s", firmware.sw_new_ver)); + m_version_new_label->Show(true); + } + else + { + m_version_label->SetLabel(wxString::Format("%s:%s", _L("Version"), firmware.sw_ver)); + m_version_new_label->Show(false); + } + } + else + { + m_sn_label->SetLabel(wxString::Format("%s: N/A", _L("SN"))); + m_version_label->SetLabel(wxString::Format("%s: N/A", _L("Version"))); + m_version_new_label->Show(false); + } + } +} + +void wgtDeviceNozzleRackHotendUpdate::Rescale() +{ + // update images + if (m_nozzle_image) { m_nozzle_image->msw_rescale(); } + if (m_nozzle_empty_image) { m_nozzle_empty_image->msw_rescale(); } + if (m_nozzle_status == NOZZLE_STATUS_EMPTY && m_nozzle_empty_image) + { + m_icon_bitmap->SetBitmap(m_nozzle_empty_image->bmp()); + } + else if (m_nozzle_image != nullptr) + { + m_icon_bitmap->SetBitmap(m_nozzle_image->bmp()); + } + m_icon_bitmap->Refresh(); +} + +};// namespace Slic3r::GUI diff --git a/src/slic3r/GUI/DeviceTab/wgtDeviceNozzleRackUpdate.h b/src/slic3r/GUI/DeviceTab/wgtDeviceNozzleRackUpdate.h new file mode 100644 index 0000000..48035ed --- /dev/null +++ b/src/slic3r/GUI/DeviceTab/wgtDeviceNozzleRackUpdate.h @@ -0,0 +1,142 @@ +//**********************************************************/ +/* File: wgtDeviceNozzleRackUpdate.h +* Description: The panel for updating hotends +* +* \n class wgtDeviceNozzleRackUpdate +//**********************************************************/ + +#pragma once +#include "slic3r/GUI/DeviceCore/DevNozzleRack.h" + +#include "slic3r/GUI/GUI_Utils.hpp" +#include "slic3r/GUI/Widgets/StaticBox.hpp" + +#include +#include + +// Previous definitions +class Button; +class Label; +class ScalableBitmap; +class ScalableButton; +namespace Slic3r +{ + struct DevNozzle; + class DevNozzleRack; +namespace GUI +{ + class wgtDeviceNozzleRackUprade; + class wgtDeviceNozzleRackHotendUpdate; +} +}; + +namespace Slic3r::GUI +{ +class wgtDeviceNozzleRackUpgradeDlg : public DPIDialog +{ +public: + wgtDeviceNozzleRackUpgradeDlg(wxWindow* parent, const std::shared_ptr rack); + +public: + void UpdateRackInfo(const std::shared_ptr rack);; + +public: + void on_dpi_changed(const wxRect& suggested_rect) override; + +private: + wgtDeviceNozzleRackUprade* m_rack_upgrade_panel; +}; + + +class wgtDeviceNozzleRackUprade : public wxPanel +{ +public: + wgtDeviceNozzleRackUprade(wxWindow* parent, + wxWindowID id = wxID_ANY, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = wxTAB_TRAVERSAL); + ~wgtDeviceNozzleRackUprade() = default; + +public: + void UpdateRackInfo(const std::shared_ptr rack); + void Rescale(); + +private: + void CreateGui(); + + void OnBtnReadAll(wxCommandEvent& e); + +private: + std::weak_ptr m_nozzle_rack; + + // GUI + wgtDeviceNozzleRackHotendUpdate* m_extruder_nozzle_item; + std::unordered_map m_nozzle_items; +}; + +// Using for rack nozzle id or extruder nozzle id +class wgtDeviceNozzleRackHotendUpdate : public StaticBox +{ +public: + wgtDeviceNozzleRackHotendUpdate(wxWindow* parent, const wxString& idx_text); + +public: + // Color + void UpdateColourStyle(const wxColour& clr); + + // extruder nozzle + int GetExtruderNozzleId() const { return m_ext_nozzle_id; } + void SetExtruderNozzleId(int ext_nozzle_id) { m_ext_nozzle_id = ext_nozzle_id; } + void UpdateExtruderNozzleInfo(const std::shared_ptr rack); + + // rack nozzle + void SetRackNozzleId(int rack_nozzle_id) { m_rack_nozzle_id = rack_nozzle_id; } + int GetRackNozzleId() const { return m_rack_nozzle_id; } + void UpdateRackNozzleInfo(const std::shared_ptr rack); + + // gui + void Rescale(); + +private: + void CreateGui(); + + void UpdateInfo(const DevNozzle& nozzle); + +private: + enum NozzleStatus : int + { + NOZZLE_STATUS_DC = -1, + NOZZLE_STATUS_EMPTY, + NOZZLE_STATUS_NORMAL, + NOZZLE_STATUS_ABNORMAL, + NOZZLE_STATUS_UNKNOWN, + }; + +private: + int m_ext_nozzle_id = -1; + int m_rack_nozzle_id = -1; + + NozzleStatus m_nozzle_status = NOZZLE_STATUS_DC; + std::weak_ptr m_nozzle_rack; + + // GUI + ScalableBitmap* m_nozzle_image = nullptr; + ScalableBitmap* m_nozzle_empty_image = nullptr; + + Label* m_idx_label; + wxStaticBitmap* m_icon_bitmap{ nullptr }; + + Label* m_diameter_label; + Label* m_flowtype_label; + Label* m_type_label; + ScalableButton* m_error_button{ nullptr }; + + Label* m_sn_label; + Label* m_version_label; + Label* m_version_new_label; +}; + + + +};// end of namespace Slic3r::GUI \ No newline at end of file diff --git a/src/slic3r/GUI/DeviceTab/wgtDeviceNozzleSelect.cpp b/src/slic3r/GUI/DeviceTab/wgtDeviceNozzleSelect.cpp new file mode 100644 index 0000000..c806981 --- /dev/null +++ b/src/slic3r/GUI/DeviceTab/wgtDeviceNozzleSelect.cpp @@ -0,0 +1,170 @@ +//**********************************************************/ +/* File: wgtDeviceNozzleSelect.cpp +* Description: The panel to select nozzle +* +* \n class wgtDeviceNozzleSelect; +//**********************************************************/ + +#include "wgtDeviceNozzleSelect.h" +#include "wgtDeviceNozzleRack.h" + +#include "slic3r/GUI/I18N.hpp" + +static wxColour s_gray_clr("#B0B0B0"); +static wxColour s_hgreen_clr("#4479fb"); +static wxColour s_red_clr("#D01B1B"); + +static std::vector a_nozzle_seq = {0, 2, 4, 1, 3, 5}; +static std::vector b_nozzle_seq = {1, 3, 5, 0, 2, 4}; + +wxDEFINE_EVENT(EVT_NOZZLE_RACK_ITEM_CLICKED, wxCommandEvent); + +namespace Slic3r::GUI { + +wgtDeviceNozzleRackSelect::wgtDeviceNozzleRackSelect(wxWindow *parent) : wxPanel(parent, wxID_ANY) { CreateGui(); } + +static wxPanel* s_create_title(wxWindow *parent, const wxString& text) +{ + wxPanel *panel = new wxPanel(parent, wxID_ANY); + + auto title = new Label(panel, text); + title->SetFont(::Label::Body_13); + title->SetBackgroundColour(*wxWHITE); + title->SetForegroundColour(0x909090); + + auto split_line = new wxPanel(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + split_line->SetBackgroundColour(0xeeeeee); + split_line->SetMinSize(wxSize(-1, 1)); + split_line->SetMaxSize(wxSize(-1, 1)); + + wxBoxSizer *sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(0, 0, 0, wxEXPAND, 0); + sizer->Add(title, 0, wxALIGN_CENTER, 0); + sizer->Add(split_line, 1, wxALIGN_CENTER_VERTICAL | wxEXPAND, 0); + panel->SetSizer(sizer); + panel->Layout(); + return panel; +} + +void wgtDeviceNozzleRackSelect::CreateGui() +{ + wxSizer *main_sizer = new wxBoxSizer(wxVERTICAL); + + // nozzles + wxGridSizer *nozzle_sizer = new wxGridSizer(2, 3, FromDIP(10), FromDIP(10)); + for (auto idx : a_nozzle_seq) { + wgtDeviceNozzleRackNozzleItem *nozzle_item = new wgtDeviceNozzleRackNozzleItem(this, idx); + nozzle_item->EnableSelect(); + nozzle_item->Bind(EVT_NOZZLE_RACK_NOZZLE_ITEM_SELECTED, &wgtDeviceNozzleRackSelect::OnNozzleItemSelected, this); + m_nozzle_items[idx] = nozzle_item; + nozzle_sizer->Add(nozzle_item, 0); + } + + // toolhead area + m_toolhead_nozzle = new wgtDeviceNozzleRackNozzleItem(this, 0); + m_toolhead_nozzle->EnableSelect(); + m_toolhead_nozzle->SetDisplayIdText("R"); + m_toolhead_nozzle->Bind(EVT_NOZZLE_RACK_NOZZLE_ITEM_SELECTED, &wgtDeviceNozzleRackSelect::OnNozzleItemSelected, this); + + main_sizer->Add(s_create_title(this, _L("Hotend Rack")), 0, wxEXPAND); + main_sizer->Add(nozzle_sizer, 0, wxTOP | wxBOTTOM | wxALIGN_LEFT, FromDIP(12)); + main_sizer->Add(s_create_title(this, _L("ToolHead")), 0, wxEXPAND); + main_sizer->AddSpacer(FromDIP(12)); + main_sizer->Add(m_toolhead_nozzle, 0, wxALIGN_LEFT); + + SetBackgroundColour(*wxWHITE); + SetSizer(main_sizer); + Layout(); + Fit(); +} + +static void s_update_item(wgtDeviceNozzleRackNozzleItem* item, std::shared_ptr rack, const DevNozzle& nozzle_info, const DevNozzle& selected_nozzle) +{ + if (item) { + item->Update(rack, nozzle_info.IsOnRack()); + if (!nozzle_info.IsEmpty() && !nozzle_info.IsAbnormal() && !nozzle_info.IsUnknown() && + nozzle_info.GetNozzleType() == selected_nozzle.GetNozzleType() && + nozzle_info.GetNozzleDiameter() == selected_nozzle.GetNozzleDiameter() && + nozzle_info.GetNozzleFlowType() == selected_nozzle.GetNozzleFlowType()){ + item->SetDisable(false); + } else{ + item->SetDisable(true); + } + + if (nozzle_info.IsUnknown()) { + if(item->GetToolTipText() != _L("Nozzle information needs to be read")) { + item->SetToolTip(_L("Nozzle information needs to be read")); + } + } else { + item->SetToolTip(wxEmptyString); + } + } +} + +void wgtDeviceNozzleRackSelect::UpdateRackSelect(std::shared_ptr rack, int selected_nozzle_pos_id) +{ + m_nozzle_rack = rack; + if (rack) { + if (selected_nozzle_pos_id < 0) { + ClearSelection(); + } else if (selected_nozzle_pos_id < 0x10) { + SetSelectedNozzle(rack->GetNozzleSystem()->GetExtNozzle(MAIN_EXTRUDER_ID)); + } else { + SetSelectedNozzle(rack->GetNozzle(selected_nozzle_pos_id - 0x10)); + } + + s_update_item(m_toolhead_nozzle, rack, rack->GetNozzleSystem()->GetExtNozzle(MAIN_EXTRUDER_ID), m_selected_nozzle); + for (auto& item : m_nozzle_items) { + s_update_item(item.second, rack, rack->GetNozzleSystem()->GetRackNozzle(item.first), m_selected_nozzle); + } + } +} + +void wgtDeviceNozzleRackSelect::ClearSelection() +{ + m_selected_nozzle = DevNozzle(); + m_toolhead_nozzle->SetSelected(false); + for (auto &item : m_nozzle_items) { item.second->SetSelected(false); } +} + +void wgtDeviceNozzleRackSelect::SetSelectedNozzle(const DevNozzle &nozzle) +{ + if (m_selected_nozzle.GetNozzlePosId() != nozzle.GetNozzlePosId()) { + ClearSelection(); + + m_selected_nozzle = nozzle; + if (m_selected_nozzle.IsNormal()) { + if (!m_selected_nozzle.IsOnRack()) { + m_toolhead_nozzle->SetSelected(true); + } else { + auto it = m_nozzle_items.find(m_selected_nozzle.GetNozzleId()); + if (it != m_nozzle_items.end()) { it->second->SetSelected(true); } + } + } + } +} + +void wgtDeviceNozzleRackSelect::OnNozzleItemSelected(wxCommandEvent &evt) +{ + auto *item = dynamic_cast(evt.GetEventObject()); + if (item; auto ptr = m_nozzle_rack.lock()) { + if (item == m_toolhead_nozzle) { + SetSelectedNozzle(ptr->GetNozzleSystem()->GetExtNozzle(MAIN_EXTRUDER_ID)); + } else { + SetSelectedNozzle(ptr->GetNozzle(item->GetNozzleId())); + } + } + + wxCommandEvent change_evt(EVT_NOZZLE_RACK_ITEM_CLICKED, GetId()); + change_evt.SetEventObject(this); + ProcessEvent(change_evt); + evt.Skip(); +} + +void wgtDeviceNozzleRackSelect::Rescale() +{ + m_toolhead_nozzle->Rescale(); + for (auto &item : m_nozzle_items) { item.second->Rescale(); } +} + +}; // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/DeviceTab/wgtDeviceNozzleSelect.h b/src/slic3r/GUI/DeviceTab/wgtDeviceNozzleSelect.h new file mode 100644 index 0000000..3b7d1ed --- /dev/null +++ b/src/slic3r/GUI/DeviceTab/wgtDeviceNozzleSelect.h @@ -0,0 +1,58 @@ +//**********************************************************/ +/* File: wgtDeviceNozzleSelect.h +* Description: The panel to select nozzle +* +* \n class wgtDeviceNozzleSelect; +//**********************************************************/ + +#pragma once + +#include "slic3r/GUI/DeviceCore/DevNozzleSystem.h" + +#include + +#include + +// Previous definitions +namespace Slic3r +{ + struct DevNozzle; + class DevNozzleRack; +namespace GUI +{ + class wgtDeviceNozzleRackNozzleItem; +} +}; + +wxDECLARE_EVENT(EVT_NOZZLE_RACK_ITEM_CLICKED, wxCommandEvent); +namespace Slic3r::GUI +{ +class wgtDeviceNozzleRackSelect : public wxPanel +{ +public: + wgtDeviceNozzleRackSelect(wxWindow* parent); + ~wgtDeviceNozzleRackSelect() = default; + +public: + int GetSelectedNozzlePosID() const { return m_selected_nozzle.GetNozzlePosId();} + void UpdateRackSelect(std::shared_ptr rack, int selected_nozzle_pos_id = -1); + void Rescale(); + +private: + void CreateGui(); + + void ClearSelection(); + void SetSelectedNozzle(const DevNozzle &nozzle); + + void OnNozzleItemSelected(wxCommandEvent& evt); + +private: + DevNozzle m_selected_nozzle; + std::weak_ptr m_nozzle_rack; + + // GUI + wgtDeviceNozzleRackNozzleItem * m_toolhead_nozzle{nullptr}; + std::unordered_map m_nozzle_items; // from 0 to 5 +}; + +};// end of namespace Slic3r::GUI \ No newline at end of file diff --git a/src/slic3r/GUI/DragDropPanel.cpp b/src/slic3r/GUI/DragDropPanel.cpp index 352880c..68d42a1 100644 --- a/src/slic3r/GUI/DragDropPanel.cpp +++ b/src/slic3r/GUI/DragDropPanel.cpp @@ -1,9 +1,11 @@ #include "DragDropPanel.hpp" -#include "Widgets/Label.hpp" +#include "GUI_App.hpp" #include namespace Slic3r { namespace GUI { +wxDEFINE_EVENT(wxEVT_DRAG_DROP_COMPLETED, wxCommandEvent); + struct CustomData { int filament_id; @@ -201,7 +203,7 @@ wxDragResult ColorDropTarget::OnData(wxCoord x, wxCoord y, wxDragResult def) /////////////// ColorDropTarget end //////////////////////// -DragDropPanel::DragDropPanel(wxWindow *parent, const wxString &label, bool is_auto) +DragDropPanel::DragDropPanel(wxWindow *parent, const wxString &label, bool is_auto, bool has_title, bool is_sub) : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE) , m_is_auto(is_auto) { @@ -209,22 +211,30 @@ DragDropPanel::DragDropPanel(wxWindow *parent, const wxString &label, bool is_au m_sizer = new wxBoxSizer(wxVERTICAL); - auto title_panel = new wxPanel(this); - title_panel->SetBackgroundColour(0xEEEEEE); - auto title_sizer = new wxBoxSizer(wxHORIZONTAL); - title_panel->SetSizer(title_sizer); + if (has_title) { + auto title_panel = new wxPanel(this); + title_panel->SetBackgroundColour(is_sub ? 0xF8F8F8 : 0xEEEEEE); + auto title_sizer = new wxBoxSizer(wxHORIZONTAL); + title_panel->SetSizer(title_sizer); - Label* static_text = new Label(this, label); - static_text->SetFont(Label::Head_13); - static_text->SetBackgroundColour(0xEEEEEE); + m_title_label = new Label(this, label); + m_title_label->SetFont(is_sub ? Label::Body_12 : Label::Head_13); + m_title_label->SetForegroundColour(is_sub ? 0x6B6B6B : 0x000000); + m_title_label->SetBackgroundColour(is_sub ? 0xF8F8F8 : 0xEEEEEE); - title_sizer->Add(static_text, 0, wxALIGN_CENTER | wxALL, FromDIP(5)); + title_sizer->Add(m_title_label, 0, wxALIGN_CENTER | wxALL, FromDIP(5)); - m_sizer->Add(title_panel, 0, wxEXPAND); - m_sizer->AddSpacer(10); + m_sizer->Add(title_panel, 0, wxEXPAND); + m_sizer->AddSpacer(10); + } - m_grid_item_sizer = new wxGridSizer(0, 6, FromDIP(8), FromDIP(8)); // row = 0, col = 3, 10 10 is space - m_sizer->Add(m_grid_item_sizer, 1, wxEXPAND | wxALL, FromDIP(8)); + if (is_sub) { + m_grid_item_sizer = new wxGridSizer(0, 3, FromDIP(6), FromDIP(6)); + m_sizer->Add(m_grid_item_sizer, 0, wxEXPAND); + } else { + m_grid_item_sizer = new wxGridSizer(0, 6, FromDIP(8), FromDIP(8)); // row = 0, col = 3, 10 10 is space + m_sizer->Add(m_grid_item_sizer, 0, wxEXPAND | wxALL, FromDIP(8)); + } // set droptarget auto drop_target = new ColorDropTarget(this); @@ -242,20 +252,35 @@ void DragDropPanel::AddColorBlock(const wxColour &color, const std::string &type m_grid_item_sizer->Add(panel, 0); m_filament_blocks.push_back(panel); if (update_ui) { - m_filament_blocks.front()->Refresh(); // FIX BUG: STUDIO-8467 + Freeze(); + + m_grid_item_sizer->Layout(); + Layout(); + Fit(); GetParent()->GetParent()->Layout(); GetParent()->GetParent()->Fit(); + m_filament_blocks.front()->Refresh(); // FIX BUG: STUDIO-8467 + + Thaw(); + NotifyDragDropCompleted(); } } void DragDropPanel::RemoveColorBlock(ColorPanel *panel, bool update_ui) { - m_sizer->Detach(panel); + m_grid_item_sizer->Detach(panel); panel->Destroy(); m_filament_blocks.erase(std::remove(m_filament_blocks.begin(), m_filament_blocks.end(), panel), m_filament_blocks.end()); if (update_ui) { + Freeze(); + + Layout(); + Fit(); GetParent()->GetParent()->Layout(); GetParent()->GetParent()->Fit(); + + Thaw(); + NotifyDragDropCompleted(); } } @@ -270,6 +295,15 @@ void DragDropPanel::DoDragDrop(ColorPanel *panel, const wxColour &color, const s } } +void DragDropPanel::UpdateLabel(const wxString &label) +{ + if (m_title_label) { + m_title_label->SetLabel(label); + m_title_label->Refresh(); + Layout(); + } +} + std::vector DragDropPanel::GetAllFilaments() const { std::vector filaments; @@ -287,4 +321,304 @@ std::vector DragDropPanel::GetAllFilaments() const return filaments; } +void DragDropPanel::NotifyDragDropCompleted() +{ + wxCommandEvent event(wxEVT_DRAG_DROP_COMPLETED); + event.SetEventObject(this); + + wxWindow *parent = GetParent(); + while (parent) { + auto name = parent->GetName(); + if (name == wxT("FilamentMapManualPanel")) { + parent->GetEventHandler()->ProcessEvent(event); + break; + } + parent = parent->GetParent(); + } +} + +class SeparatedColorDropTarget : public wxDropTarget +{ +public: + SeparatedColorDropTarget(SeparatedDragDropPanel *panel) : wxDropTarget(), m_panel(panel) + { + m_data = new ColorDataObject(); + SetDataObject(m_data); + } + + virtual wxDragResult OnData(wxCoord x, wxCoord y, wxDragResult def) override; + virtual bool OnDrop(wxCoord x, wxCoord y) override { return true; } + +private: + SeparatedDragDropPanel *m_panel; + ColorDataObject *m_data; +}; + +wxDragResult SeparatedColorDropTarget::OnData(wxCoord x, wxCoord y, wxDragResult def) +{ + if (!GetData()) return wxDragNone; + + m_panel->AddColorBlock(m_data->GetColor(), m_data->GetType(), m_data->GetFilament(), false); + return wxDragCopy; +} + +SeparatedDragDropPanel::SeparatedDragDropPanel(wxWindow *parent, const wxString &label, bool use_separation) : wxPanel(parent), m_use_separation(use_separation) +{ + SetBackgroundColour(0xF8F8F8); + + m_main_sizer = new wxBoxSizer(wxVERTICAL); + + auto title_panel = new wxPanel(this); + title_panel->SetBackgroundColour(0xEEEEEE); + auto title_sizer = new wxBoxSizer(wxHORIZONTAL); + title_panel->SetSizer(title_sizer); + + m_label = new Label(title_panel, label); + m_label->SetFont(Label::Head_13); + m_label->SetBackgroundColour(0xEEEEEE); + + title_sizer->Add(m_label, 0, wxALIGN_CENTER | wxALL, FromDIP(5)); + + m_main_sizer->Add(title_panel, 0, wxEXPAND); + m_main_sizer->AddSpacer(10); + + m_content_panel = new wxPanel(this); + m_content_panel->SetBackgroundColour(0xF8F8F8); + m_content_sizer = new wxBoxSizer(wxHORIZONTAL); + m_content_panel->SetSizer(m_content_sizer); + + m_high_flow_panel = new DragDropPanel(m_content_panel, _L("High Flow"), false, true, true); + m_standard_panel = new DragDropPanel(m_content_panel, _L("Standard"), false, true, true); + m_unified_panel = new DragDropPanel(m_content_panel, wxEmptyString, false, false); + + m_high_flow_panel->SetBackgroundColour(0xF8F8F8); + m_standard_panel->SetBackgroundColour(0xF8F8F8); + m_unified_panel->SetBackgroundColour(0xF8F8F8); + m_unified_panel->SetMinSize({FromDIP(260), -1}); + + m_main_sizer->Add(m_content_panel, 1, wxEXPAND); + + UpdateLayout(); + + auto drop_target = new SeparatedColorDropTarget(this); + SetDropTarget(drop_target); + + SetSizer(m_main_sizer); + Layout(); + wxGetApp().UpdateDarkUIWin(this); +} + +void SeparatedDragDropPanel::UpdateLayout() +{ + m_content_sizer->Clear(false); + + if (m_use_separation) { + m_unified_panel->Hide(); + m_high_flow_panel->Show(); + m_standard_panel->Show(); + + wxSize content_size = m_content_panel->GetSize(); + int panel_width = (content_size.GetWidth() - FromDIP(1) - FromDIP(8)) / 2; + if (panel_width > 0) { + m_high_flow_panel->SetMinSize(wxSize(panel_width, -1)); + m_standard_panel->SetMinSize(wxSize(panel_width, -1)); + } + + m_content_sizer->Add(m_high_flow_panel, 1, wxEXPAND | wxLEFT, FromDIP(8)); + + auto separator = new wxPanel(m_content_panel, wxID_ANY); + separator->SetBackgroundColour(0xCCCCCC); + separator->SetMinSize(wxSize(FromDIP(1), -1)); + m_content_sizer->Add(separator, 0, wxEXPAND | wxALL, FromDIP(4)); + + m_content_sizer->Add(m_standard_panel, 1, wxEXPAND | wxRIGHT, FromDIP(8)); + } else { + m_high_flow_panel->Hide(); + m_standard_panel->Hide(); + m_unified_panel->Show(); + + m_content_sizer->Add(m_unified_panel, 1, wxEXPAND); + } + m_content_sizer->Layout(); + Layout(); + Fit(); + if (GetParent()) { + GetParent()->Layout(); + if (GetParent()->GetParent()) { + GetParent()->GetParent()->Layout(); + } + } +} + +void SeparatedDragDropPanel::UpdateLabel(const wxString &label) +{ + if (m_label) { + m_label->SetLabel(label); + m_label->Refresh(); + Layout(); + } +} + +void SeparatedDragDropPanel::SetUseSeparation(bool use_separation) +{ + if (m_use_separation != use_separation) { + m_use_separation = use_separation; + + if (use_separation) { + auto blocks = m_unified_panel->get_filament_blocks(); + for (auto &block : blocks) { + m_standard_panel->AddColorBlock(block->GetColor(), block->GetType(), block->GetFilamentId(), false); + m_unified_panel->RemoveColorBlock(block, false); + } + } else { + auto high_flow_blocks = m_high_flow_panel->get_filament_blocks(); + auto standard_blocks = m_standard_panel->get_filament_blocks(); + + for (auto &block : high_flow_blocks) { + m_unified_panel->AddColorBlock(block->GetColor(), block->GetType(), block->GetFilamentId(), false); + m_high_flow_panel->RemoveColorBlock(block, false); + } + + for (auto &block : standard_blocks) { + m_unified_panel->AddColorBlock(block->GetColor(), block->GetType(), block->GetFilamentId(), false); + m_standard_panel->RemoveColorBlock(block, false); + } + } + + UpdateLayout(); + } +} + +void SeparatedDragDropPanel::AddColorBlock(const wxColour &color, const std::string &type, int filament_id, bool is_high_flow, bool update_ui) +{ + if (m_use_separation) { + if (is_high_flow) { + m_high_flow_panel->AddColorBlock(color, type, filament_id, update_ui); + } else { + m_standard_panel->AddColorBlock(color, type, filament_id, update_ui); + } + + if (update_ui) { + CallAfter([this]() { + Layout(); + GetParent()->Layout(); + }); + } + } else { + m_unified_panel->AddColorBlock(color, type, filament_id, update_ui); + } +} + +void SeparatedDragDropPanel::RemoveColorBlock(ColorPanel *panel, bool update_ui) +{ + auto high_flow_blocks = m_high_flow_panel->get_filament_blocks(); + auto standard_blocks = m_standard_panel->get_filament_blocks(); + auto unified_blocks = m_unified_panel->get_filament_blocks(); + + if (std::find(high_flow_blocks.begin(), high_flow_blocks.end(), panel) != high_flow_blocks.end()) { + m_high_flow_panel->RemoveColorBlock(panel, update_ui); + } else if (std::find(standard_blocks.begin(), standard_blocks.end(), panel) != standard_blocks.end()) { + m_standard_panel->RemoveColorBlock(panel, update_ui); + } else if (std::find(unified_blocks.begin(), unified_blocks.end(), panel) != unified_blocks.end()) { + m_unified_panel->RemoveColorBlock(panel, update_ui); + } + + if (update_ui && m_use_separation) { + CallAfter([this]() { + Layout(); + GetParent()->Layout(); + }); + } +} + +std::vector SeparatedDragDropPanel::GetAllFilaments() const +{ + if (m_use_separation) { + auto high_flow = m_high_flow_panel->GetAllFilaments(); + auto standard = m_standard_panel->GetAllFilaments(); + + std::vector result; + result.insert(result.end(), high_flow.begin(), high_flow.end()); + result.insert(result.end(), standard.begin(), standard.end()); + return result; + } else { + return m_unified_panel->GetAllFilaments(); + } +} + +std::vector SeparatedDragDropPanel::GetHighFlowFilaments() const +{ + if (m_use_separation) { + return m_high_flow_panel->GetAllFilaments(); + } + auto nozzle_volumes = wxGetApp().preset_bundle->project_config.option("nozzle_volume_type"); + const int right_eid = 1; + if (nozzle_volumes->values.size() > right_eid) { + int volume_type = nozzle_volumes->values[right_eid]; + if (volume_type == static_cast(NozzleVolumeType::nvtHighFlow)) { + return m_unified_panel->GetAllFilaments(); + } + } + return {}; +} + +std::vector SeparatedDragDropPanel::GetStandardFilaments() const +{ + if (m_use_separation) { + return m_standard_panel->GetAllFilaments(); + } + auto nozzle_volumes = wxGetApp().preset_bundle->project_config.option("nozzle_volume_type"); + const int right_eid = 1; + if (nozzle_volumes->values.size() > right_eid) { + int volume_type = nozzle_volumes->values[right_eid]; + if (volume_type == static_cast(NozzleVolumeType::nvtStandard)) { + return m_unified_panel->GetAllFilaments(); + } + } + return {}; +} + +std::vector SeparatedDragDropPanel::get_filament_blocks() const +{ + if (m_use_separation) { + auto high_flow = m_high_flow_panel->get_filament_blocks(); + auto standard = m_standard_panel->get_filament_blocks(); + + std::vector result; + result.insert(result.end(), high_flow.begin(), high_flow.end()); + result.insert(result.end(), standard.begin(), standard.end()); + return result; + } else { + return m_unified_panel->get_filament_blocks(); + } +} + +std::vector SeparatedDragDropPanel::get_high_flow_blocks() const +{ + return m_high_flow_panel->get_filament_blocks(); +} + +std::vector SeparatedDragDropPanel::get_standard_blocks() const +{ + return m_standard_panel->get_filament_blocks(); +} + +void SeparatedDragDropPanel::ClearAllBlocks() +{ + auto high_flow_blocks = m_high_flow_panel->get_filament_blocks(); + for (auto &block : high_flow_blocks) { + m_high_flow_panel->RemoveColorBlock(block, false); + } + + auto standard_blocks = m_standard_panel->get_filament_blocks(); + for (auto &block : standard_blocks) { + m_standard_panel->RemoveColorBlock(block, false); + } + + auto unified_blocks = m_unified_panel->get_filament_blocks(); + for (auto &block : unified_blocks) { + m_unified_panel->RemoveColorBlock(block, false); + } +} + }} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/DragDropPanel.hpp b/src/slic3r/GUI/DragDropPanel.hpp index d988164..739b450 100644 --- a/src/slic3r/GUI/DragDropPanel.hpp +++ b/src/slic3r/GUI/DragDropPanel.hpp @@ -3,6 +3,7 @@ #include "GUI.hpp" #include "GUI_Utils.hpp" +#include "Widgets/Label.hpp" #include #include @@ -13,17 +14,22 @@ namespace Slic3r { namespace GUI { +wxDECLARE_EVENT(wxEVT_DRAG_DROP_COMPLETED, wxCommandEvent); + +class FilamentMapManualPanel; + wxColor Hex2Color(const std::string& str); class ColorPanel; class DragDropPanel : public wxPanel { public: - DragDropPanel(wxWindow *parent, const wxString &label, bool is_auto); + DragDropPanel(wxWindow *parent, const wxString &label, bool is_auto, bool has_title = true, bool is_sub = false); void AddColorBlock(const wxColour &color, const std::string &type, int filament_id, bool update_ui = true); void RemoveColorBlock(ColorPanel *panel, bool update_ui = true); void DoDragDrop(ColorPanel *panel, const wxColour &color, const std::string &type, int filament_id); + void UpdateLabel(const wxString &label); std::vector GetAllFilaments() const; @@ -35,9 +41,12 @@ public: private: wxBoxSizer *m_sizer; wxGridSizer *m_grid_item_sizer; + Label *m_title_label = nullptr; bool m_is_auto; std::vector m_filament_blocks; + + void NotifyDragDropCompleted(); private: bool m_is_draging = false; }; @@ -64,6 +73,44 @@ private: int m_filament_id; }; + +class SeparatedDragDropPanel : public wxPanel +{ +public: + SeparatedDragDropPanel(wxWindow *parent, const wxString &label, bool use_separation = false); + + void AddColorBlock(const wxColour &color, const std::string &type, int filament_id, bool is_high_flow = false, bool update_ui = true); + void RemoveColorBlock(ColorPanel *panel, bool update_ui = true); + + std::vector GetAllFilaments() const; + std::vector GetHighFlowFilaments() const; + std::vector GetStandardFilaments() const; + + std::vector get_filament_blocks() const; + std::vector get_high_flow_blocks() const; + std::vector get_standard_blocks() const; + + void SetUseSeparation(bool use_separation); + bool IsUseSeparation() const { return m_use_separation; } + void ClearAllBlocks(); + void UpdateLabel(const wxString &label); + +private: + void UpdateLayout(); + + wxBoxSizer *m_main_sizer; + wxPanel *m_content_panel; + wxBoxSizer *m_content_sizer; + wxStaticText *m_label; + + DragDropPanel *m_high_flow_panel; + DragDropPanel *m_standard_panel; + + DragDropPanel *m_unified_panel; + + bool m_use_separation; +}; + }} // namespace Slic3r::GUI #endif /* slic3r_DragDropPanel_hpp_ */ diff --git a/src/slic3r/GUI/FilamentMapDialog.cpp b/src/slic3r/GUI/FilamentMapDialog.cpp index edcbe8d..9e1583d 100644 --- a/src/slic3r/GUI/FilamentMapDialog.cpp +++ b/src/slic3r/GUI/FilamentMapDialog.cpp @@ -33,6 +33,13 @@ static std::vector get_applied_map(DynamicConfig& proj_config, const Plater return plater_ref->get_global_filament_map(); } +static std::vector get_applied_volume_map(DynamicConfig& proj_config, const Plater* plater_ref, const PartPlate* partplate_ref, const bool sync_plate) +{ + if (sync_plate) + return partplate_ref->get_real_filament_volume_maps(proj_config); + return plater_ref->get_global_filament_volume_map(); +} + extern std::string& get_left_extruder_unprintable_text(); extern std::string& get_right_extruder_unprintable_text(); @@ -50,7 +57,9 @@ bool try_pop_up_before_slice(bool is_slice_all, Plater* plater_ref, PartPlate* p std::vector filament_types = full_config.option("filament_type")->values; FilamentMapMode applied_mode = get_applied_map_mode(full_config, plater_ref,partplate_ref, sync_plate); std::vector applied_maps = get_applied_map(full_config, plater_ref, partplate_ref, sync_plate); + std::vector applied_volume_maps = get_applied_volume_map(full_config, plater_ref, partplate_ref, sync_plate); applied_maps.resize(filament_colors.size(), 1); + applied_volume_maps.resize(filament_colors.size(), 0); if (!force_pop_up && applied_mode != fmmManual) return true; @@ -68,6 +77,7 @@ bool try_pop_up_before_slice(bool is_slice_all, Plater* plater_ref, PartPlate* p filament_colors, filament_types, applied_maps, + applied_volume_maps, filament_lists, applied_mode, plater_ref->get_machine_sync_status(), @@ -79,25 +89,32 @@ bool try_pop_up_before_slice(bool is_slice_all, Plater* plater_ref, PartPlate* p if (ret == wxID_OK) { FilamentMapMode new_mode = map_dlg.get_mode(); std::vector new_maps = map_dlg.get_filament_maps(); + std::vector new_volume_maps = map_dlg.get_filament_volume_maps(); if (sync_plate) { if (is_slice_all) { auto plate_list = plater_ref->get_partplate_list().get_plate_list(); for (int i = 0; i < plate_list.size(); ++i) { plate_list[i]->set_filament_map_mode(new_mode); - if(new_mode == fmmManual) + if (new_mode == fmmManual) { plate_list[i]->set_filament_maps(new_maps); + plate_list[i]->set_filament_volume_maps(new_volume_maps); + } } } else { partplate_ref->set_filament_map_mode(new_mode); - if (new_mode == fmmManual) + if (new_mode == fmmManual) { partplate_ref->set_filament_maps(new_maps); + partplate_ref->set_filament_volume_maps(new_volume_maps); + } } } else { plater_ref->set_global_filament_map_mode(new_mode); - if (new_mode == fmmManual) + if (new_mode == fmmManual) { plater_ref->set_global_filament_map(new_maps); + plater_ref->set_global_filament_volume_map(new_volume_maps); + } } plater_ref->update(); // check whether able to slice, if not, return false @@ -130,12 +147,17 @@ FilamentMapDialog::FilamentMapDialog(wxWindow *parent, const std::vector &filament_color, const std::vector &filament_type, const std::vector &filament_map, + const std::vector &filament_volume_map, const std::vector &filaments, const FilamentMapMode mode, bool machine_synced, bool show_default, bool with_checkbox) - : wxDialog(parent, wxID_ANY, _L("Filament grouping"), wxDefaultPosition, wxDefaultSize,wxDEFAULT_DIALOG_STYLE), m_filament_color(filament_color), m_filament_type(filament_type), m_filament_map(filament_map) + : wxDialog(parent, wxID_ANY, _L("Filament grouping"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE) + , m_filament_color(filament_color) + , m_filament_type(filament_type) + , m_filament_map(filament_map) + , m_filament_volume_map(filament_volume_map) { SetBackgroundColour(*wxWHITE); @@ -177,7 +199,7 @@ FilamentMapDialog::FilamentMapDialog(wxWindow *parent, mode == fmmAutoForMatch && !machine_synced ? fmmAutoForFlush : mode; - m_manual_map_panel = new FilamentMapManualPanel(this, m_filament_color, m_filament_type, filaments, filament_map); + m_manual_map_panel = new FilamentMapManualPanel(this, m_filament_color, m_filament_type, filaments, filament_map, filament_volume_map); m_auto_map_panel = new FilamentMapAutoPanel(this, default_auto_mode, machine_synced); if (show_default) m_default_map_panel = new FilamentMapDefaultPanel(this); @@ -241,6 +263,25 @@ FilamentMapDialog::FilamentMapDialog(wxWindow *parent, m_manual_btn->Bind(wxEVT_BUTTON, &FilamentMapDialog::on_switch_mode, this); if (show_default) m_default_btn->Bind(wxEVT_BUTTON, &FilamentMapDialog::on_switch_mode, this); + m_manual_map_panel->Bind(wxEVT_INVALID_MANUAL_MAP, [this](wxCommandEvent& event) { + if (m_page_type != PageType::ptManual) { + if (!m_ok_btn->IsEnabled()) { + m_ok_btn->Enable(); + } + return; + } + if (event.GetInt()) { + if (!m_ok_btn->IsEnabled()) { + m_ok_btn->Enable(); + } + } + else { + if (m_ok_btn->IsEnabled()) { + m_ok_btn->Disable(); + } + } + }); + SetSizer(main_sizer); Layout(); Fit(); @@ -280,16 +321,8 @@ void FilamentMapDialog::on_checkbox(wxCommandEvent &event) void FilamentMapDialog::on_ok(wxCommandEvent &event) { if (m_page_type == PageType::ptManual) { - std::vector left_filaments = m_manual_map_panel->GetLeftFilaments(); - std::vector right_filaments = m_manual_map_panel->GetRightFilaments(); - - for (int i = 0; i < m_filament_map.size(); ++i) { - if (std::find(left_filaments.begin(), left_filaments.end(), i + 1) != left_filaments.end()) { - m_filament_map[i] = 1; - } else if (std::find(right_filaments.begin(), right_filaments.end(), i + 1) != right_filaments.end()) { - m_filament_map[i] = 2; - } - } + m_filament_map = m_manual_map_panel->GetFilamentMaps(); + m_filament_volume_map = m_manual_map_panel->GetFilamentVolumeMaps(); } EndModal(wxID_OK); @@ -325,6 +358,9 @@ void FilamentMapDialog::update_panel_status(PageType page) if (page == PageType::ptAuto) { m_auto_btn->Select(true); m_auto_map_panel->Show(); + if (!m_ok_btn->IsEnabled()) { + m_ok_btn->Enable(); + } } Layout(); diff --git a/src/slic3r/GUI/FilamentMapDialog.hpp b/src/slic3r/GUI/FilamentMapDialog.hpp index bafc6ab..3ed0366 100644 --- a/src/slic3r/GUI/FilamentMapDialog.hpp +++ b/src/slic3r/GUI/FilamentMapDialog.hpp @@ -42,6 +42,7 @@ public: const std::vector& filament_color, const std::vector& filament_type, const std::vector &filament_map, + const std::vector &filament_volume_map, const std::vector &filaments, const FilamentMapMode mode, bool machine_synced, @@ -56,6 +57,12 @@ public: return {}; } + std::vector get_filament_volume_maps() const { + if (m_page_type == PageType::ptManual) + return m_filament_volume_map; + return {}; + } + int ShowModal(); void set_modal_btn_labels(const wxString& left_label, const wxString& right_label); private: @@ -83,6 +90,7 @@ private: private: std::vector m_filament_map; + std::vector m_filament_volume_map; std::vector m_filament_color; std::vector m_filament_type; }; diff --git a/src/slic3r/GUI/FilamentMapPanel.cpp b/src/slic3r/GUI/FilamentMapPanel.cpp index 44f9be9..bb6e3e6 100644 --- a/src/slic3r/GUI/FilamentMapPanel.cpp +++ b/src/slic3r/GUI/FilamentMapPanel.cpp @@ -1,4 +1,5 @@ #include "FilamentMapPanel.hpp" +#include "Widgets/MultiNozzleSync.hpp" #include "GUI_App.hpp" #include #include "wx/graphics.h" @@ -16,14 +17,232 @@ static const wxColour BorderDisableColor = wxColour("#EEEEEE"); static const wxColour TextNormalBlackColor = wxColour("#262E30"); static const wxColour TextNormalGreyColor = wxColour("#6B6B6B"); static const wxColour TextDisableColor = wxColour("#CECECE"); +static const wxColour TextErrorColor = wxColour("#E14747"); + +wxDEFINE_EVENT(wxEVT_INVALID_MANUAL_MAP, wxCommandEvent); + +void FilamentMapManualPanel::OnTimer(wxTimerEvent &) +{ + bool valid = true; + int invalid_eid = -1; + NozzleVolumeType invalid_nozzle = NozzleVolumeType::nvtStandard; + auto preset_bundle = wxGetApp().preset_bundle; + auto proj_config = preset_bundle->project_config; + auto nozzle_volume_values = proj_config.option("nozzle_volume_type")->values; + std::vector filament_map = GetFilamentMaps(); + std::vector filament_volume_map = GetFilamentVolumeMaps(); + std::vectornozzle_count(nozzle_volume_values.size()); + for (size_t eid = 0; eid < nozzle_volume_values.size(); ++eid) { + NozzleVolumeType extruder_volume_type = NozzleVolumeType(nozzle_volume_values[eid]); + bool extruder_used = std::find_if(m_filament_list.begin(), m_filament_list.end(), + [this, eid, filament_map](int fid) { + return (filament_map[fid - 1] - 1) == eid; + }) != m_filament_list.end(); + + if (!extruder_used) { + continue; + } + + if (extruder_volume_type == nvtHybrid) { + int standard_count = preset_bundle->extruder_nozzle_stat.get_extruder_nozzle_count(eid, NozzleVolumeType::nvtStandard); + int highflow_count = preset_bundle->extruder_nozzle_stat.get_extruder_nozzle_count(eid, NozzleVolumeType::nvtHighFlow); + + auto has_material_of_type = [this, eid, &filament_map, &filament_volume_map](NozzleVolumeType volume_type) { + return std::find_if(m_filament_list.begin(), m_filament_list.end(), + [this, eid, &filament_map, &filament_volume_map, volume_type](int fid) { + return (filament_map[fid - 1] - 1) == eid && + fid - 1 < filament_volume_map.size() && + filament_volume_map[fid - 1] == static_cast(volume_type); + }) != m_filament_list.end(); + }; + + bool has_standard = has_material_of_type(NozzleVolumeType::nvtStandard); + bool has_highflow = has_material_of_type(NozzleVolumeType::nvtHighFlow); + + if ((has_standard && standard_count == 0) || + (has_highflow && highflow_count == 0)) { + valid = false; + invalid_eid = eid; + invalid_nozzle = (has_standard && standard_count == 0) ? NozzleVolumeType::nvtStandard : NozzleVolumeType::nvtHighFlow; + break; + } + } else { + int count = preset_bundle->extruder_nozzle_stat.get_extruder_nozzle_count(eid, extruder_volume_type); + if (count == 0) { + valid = false; + invalid_eid = eid; + invalid_nozzle = extruder_volume_type; + break; + } + } + } + + bool update_ui = m_invalid_id != invalid_eid; + bool send_event = update_ui || m_force_validation; + + m_invalid_id = invalid_eid; + + if (update_ui) { + if (valid) { + m_errors->Hide(); + m_suggestion_panel->Hide(); + } else { + m_errors->SetLabel(wxString::Format(_L("Error: %s extruder has no available %s nozzle, current group result is invalid."), + invalid_eid == 0 ? _L("Left") : _L("Right"), invalid_nozzle == NozzleVolumeType::nvtStandard ? _L("Standard") : _L("High Flow"))); + m_errors->Show(); + m_suggestion_panel->Show(); + } + m_left_panel->Freeze(); + m_right_panel->Freeze(); + m_tips->Freeze(); + m_description->Freeze(); + Layout(); + Fit(); + this->GetParent()->Layout(); + this->GetParent()->Fit(); + m_left_panel->Thaw(); + m_right_panel->Thaw(); + m_tips->Thaw(); + m_description->Thaw(); + } + + if (send_event) { + wxCommandEvent event(wxEVT_INVALID_MANUAL_MAP); + event.SetInt(valid); + ProcessEvent(event); + m_force_validation = false; + } +} + +void FilamentMapManualPanel::OnSuggestionClicked(wxCommandEvent &event) +{ + wxWindow *current = this; + while (current && !wxDynamicCast(current, wxDialog)) { + current = current->GetParent(); + } + + if (current) { + wxDialog *dlg = wxDynamicCast(current, wxDialog); + if (dlg) { + int invalid_eid = m_invalid_id; + dlg->EndModal(wxID_CANCEL); + + if (invalid_eid == 0) { + manuallySetNozzleCount(0); + } else if (invalid_eid == 1) { + manuallySetNozzleCount(1); + } + wxGetApp().plater()->update(); + } + } +} + +std::vector FilamentMapManualPanel::GetFilamentMaps() const +{ + std::vector new_filament_map = m_filament_map; + std::vector left_filaments = this->GetLeftFilaments(); + std::vector right_filaments = this->GetRightFilaments(); + + for (int i = 0; i < new_filament_map.size(); ++i) { + if (std::find(left_filaments.begin(), left_filaments.end(), i + 1) != left_filaments.end()) { + new_filament_map[i] = 1; + } else if (std::find(right_filaments.begin(), right_filaments.end(), i + 1) != right_filaments.end()) { + new_filament_map[i] = 2; + } + } + return new_filament_map; +} + +std::vector FilamentMapManualPanel::GetFilamentVolumeMaps() const +{ + std::vector volume_map(m_filament_map.size(), 0); + + std::vector left_filaments = this->GetLeftFilaments(); + std::vector right_high_flow_filaments = this->GetRightHighFlowFilaments(); + std::vector right_standard_filaments = this->GetRightStandardFilaments(); + + auto preset_bundle = wxGetApp().preset_bundle; + auto proj_config = preset_bundle->project_config; + auto nozzle_volume_values = proj_config.option("nozzle_volume_type")->values; + + for (int i = 0; i < volume_map.size(); ++i) { + int filament_id = i + 1; + + if (std::find(left_filaments.begin(), left_filaments.end(), filament_id) != left_filaments.end()) { + if (nozzle_volume_values.size() > 0) { + volume_map[i] = nozzle_volume_values[0]; + } + } + else if (std::find(right_high_flow_filaments.begin(), right_high_flow_filaments.end(), filament_id) != right_high_flow_filaments.end()) { + volume_map[i] = 1; + } + else if (std::find(right_standard_filaments.begin(), right_standard_filaments.end(), filament_id) != right_standard_filaments.end()) { + volume_map[i] = 0; + } + } + + return volume_map; +} + +void FilamentMapManualPanel::SyncPanelHeights() +{ + if (!m_left_panel || !m_right_panel) return; + + auto curr_left = m_left_panel->GetMinSize(); + auto curr_right = m_right_panel->GetMinSize(); + + m_left_panel->SetMinSize(wxSize(FromDIP(260), -1)); + m_right_panel->SetMinSize(wxSize(FromDIP(260), -1)); + + m_left_panel->Layout(); + m_left_panel->Fit(); + m_right_panel->Layout(); + m_right_panel->Fit(); + + wxSize left_best_size = m_left_panel->GetBestSize(); + wxSize right_best_size = m_right_panel->GetBestSize(); + + int max_height = std::max(left_best_size.GetHeight(), right_best_size.GetHeight()); + bool height_changed = curr_left.GetHeight() != max_height || curr_right.GetHeight() != max_height; + if (!height_changed) { + if (curr_left.GetHeight() > 0) + m_left_panel->SetMinSize(curr_left); + if (curr_right.GetHeight() > 0) + m_right_panel->SetMinSize(curr_right); + if (GetParent()) { + GetParent()->Layout(); + GetParent()->Fit(); + } + return; + } + + m_left_panel->SetMinSize(wxSize(FromDIP(260), max_height)); + m_right_panel->SetMinSize(wxSize(FromDIP(260), max_height)); + + Layout(); + Fit(); + + if (GetParent()) { + GetParent()->Layout(); + GetParent()->Fit(); + } +} + +void FilamentMapManualPanel::OnDragDropCompleted(wxCommandEvent& event) +{ + SyncPanelHeights(); + event.Skip(); +} FilamentMapManualPanel::FilamentMapManualPanel(wxWindow *parent, const std::vector &color, const std::vector &type, const std::vector &filament_list, - const std::vector &filament_map) - : wxPanel(parent), m_filament_map(filament_map), m_filament_color(color), m_filament_type(type), m_filament_list(filament_list) + const std::vector &filament_map, + const std::vector &filament_volume_map) + : wxPanel(parent), m_filament_map(filament_map), m_filament_color(color), m_filament_type(type), m_filament_list(filament_list), m_filament_volume_map(filament_volume_map) { + SetName(wxT("FilamentMapManualPanel")); SetBackgroundColour(BgNormalColor); auto top_sizer = new wxBoxSizer(wxVERTICAL); @@ -34,10 +253,12 @@ FilamentMapManualPanel::FilamentMapManualPanel(wxWindow *p auto drag_sizer = new wxBoxSizer(wxHORIZONTAL); - m_left_panel = new DragDropPanel(this, _L("Left Nozzle"), false); - m_right_panel = new DragDropPanel(this, _L("Right Nozzle"), false); + m_left_panel = new DragDropPanel(this, _L("Left Extruder"), false); + m_right_panel = new SeparatedDragDropPanel(this, _L("Right Extruder"), false); m_switch_btn = new ScalableButton(this, wxID_ANY, "switch_filament_maps"); + UpdateNozzleVolumeType(); + for (size_t idx = 0; idx < m_filament_map.size(); ++idx) { auto iter = std::find(m_filament_list.begin(), m_filament_list.end(), idx + 1); if (iter == m_filament_list.end()) continue; @@ -47,7 +268,8 @@ FilamentMapManualPanel::FilamentMapManualPanel(wxWindow *p m_left_panel->AddColorBlock(color, type, idx + 1); } else { assert(m_filament_map[idx] == 2); - m_right_panel->AddColorBlock(color, type, idx + 1); + bool is_high_flow = (idx < m_filament_volume_map.size()) && (m_filament_volume_map[idx] == 1); + m_right_panel->AddColorBlock(color, type, idx + 1, is_high_flow); } } m_left_panel->SetMinSize({ FromDIP(260),-1 }); @@ -64,26 +286,111 @@ FilamentMapManualPanel::FilamentMapManualPanel(wxWindow *p top_sizer->Add(drag_sizer, 0, wxALIGN_CENTER | wxEXPAND); m_tips = new Label(this, _L("Tips: You can drag the filaments to reassign them to different nozzles.")); - m_tips->SetFont(Label::Body_14); + m_tips->SetFont(Label::Body_13); m_tips->SetForegroundColour(TextNormalGreyColor); top_sizer->AddSpacer(FromDIP(20)); top_sizer->Add(m_tips, 0, wxALIGN_LEFT | wxLEFT, FromDIP(15)); + m_errors = new Label(this, ""); + m_errors->SetFont(Label::Body_13); + m_errors->SetForegroundColour(TextErrorColor); + top_sizer->AddSpacer(FromDIP(10)); + top_sizer->Add(m_errors, 0, wxALIGN_LEFT | wxLEFT, FromDIP(15)); + + m_errors->Hide(); + + m_suggestion_panel = new wxPanel(this, wxID_ANY); + m_suggestion_panel->SetBackgroundColour(*wxWHITE); + auto suggestion_sizer = new wxBoxSizer(wxHORIZONTAL); + auto suggestion_text = new Label(m_suggestion_panel, _L("Please adjust your grouping or click ")); + suggestion_text->SetFont(Label::Body_13); + suggestion_text->SetForegroundColour(TextErrorColor); + suggestion_text->SetBackgroundColour(*wxWHITE); + auto suggestion_btn = new ScalableButton(m_suggestion_panel, wxID_ANY, "edit", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, true, 14); + suggestion_btn->SetBackgroundColour(*wxWHITE); + auto suggestion_text2 = new Label(m_suggestion_panel, _L(" to set nozzle count")); + suggestion_text2->SetFont(Label::Body_13); + suggestion_text2->SetForegroundColour(TextErrorColor); + suggestion_text2->SetBackgroundColour(*wxWHITE); + suggestion_sizer->Add(suggestion_text, 0, wxALIGN_CENTER_VERTICAL); + suggestion_sizer->Add(suggestion_btn, 0, wxALIGN_CENTER_VERTICAL); + suggestion_sizer->Add(suggestion_text2, 0, wxALIGN_CENTER_VERTICAL); + m_suggestion_panel->SetSizer(suggestion_sizer); + top_sizer->Add(m_suggestion_panel, 0, wxALIGN_LEFT | wxLEFT, FromDIP(15)); + m_suggestion_panel->Hide(); + suggestion_btn->Bind(wxEVT_BUTTON, &FilamentMapManualPanel::OnSuggestionClicked, this); + + m_timer = new wxTimer(this); + Bind(wxEVT_TIMER, &FilamentMapManualPanel::OnTimer, this); + Bind(wxEVT_DRAG_DROP_COMPLETED, &FilamentMapManualPanel::OnDragDropCompleted, this); + m_switch_btn->Bind(wxEVT_BUTTON, &FilamentMapManualPanel::OnSwitchFilament, this); SetSizer(top_sizer); Layout(); Fit(); + if (GetParent()) { + GetParent()->Layout(); + GetParent()->Fit(); + } GUI::wxGetApp().UpdateDarkUIWin(this); } +void FilamentMapManualPanel::UpdateNozzleVolumeType() +{ + auto check_separation = [this]() { + auto preset_bundle = wxGetApp().preset_bundle; + auto nozzle_volume_values = preset_bundle->project_config.option("nozzle_volume_type")->values; + if (nozzle_volume_values.size() <= 1) + return false; + + return nozzle_volume_values[1] == static_cast(NozzleVolumeType::nvtHybrid); + }; + bool should_separate = check_separation(); + m_right_panel->SetUseSeparation(should_separate); + + UpdateNozzleCountDisplay(); + + Layout(); + Fit(); + if (GetParent()) { + GetParent()->Layout(); + GetParent()->Fit(); + } +} + +void FilamentMapManualPanel::UpdateNozzleCountDisplay() +{ + auto preset_bundle = wxGetApp().preset_bundle; + + int left_count = preset_bundle->extruder_nozzle_stat.get_extruder_nozzle_count(0); + wxString left_title = wxString::Format(_L("Left Extruder(%d)"), left_count); + m_left_panel->UpdateLabel(left_title); + + if (m_right_panel->IsUseSeparation()) { + int standard_count = preset_bundle->extruder_nozzle_stat.get_extruder_nozzle_count(1, NozzleVolumeType::nvtStandard); + int highflow_count = preset_bundle->extruder_nozzle_stat.get_extruder_nozzle_count(1, NozzleVolumeType::nvtHighFlow); + wxString right_title = wxString::Format(_L("Right Extruder(Std: %d, HF: %d)"), standard_count, highflow_count); + m_right_panel->UpdateLabel(right_title); + } else { + int right_count = preset_bundle->extruder_nozzle_stat.get_extruder_nozzle_count(1); + wxString right_title = wxString::Format(_L("Right Extruder(%d)"), right_count); + m_right_panel->UpdateLabel(right_title); + } +} + +FilamentMapManualPanel::~FilamentMapManualPanel() +{ + m_timer->Stop(); +} + void FilamentMapManualPanel::OnSwitchFilament(wxCommandEvent &) { auto left_blocks = m_left_panel->get_filament_blocks(); auto right_blocks = m_right_panel->get_filament_blocks(); for (auto &block : left_blocks) { - m_right_panel->AddColorBlock(block->GetColor(), block->GetType(), block->GetFilamentId(), false); + m_right_panel->AddColorBlock(block->GetColor(), block->GetType(), block->GetFilamentId(), false, false); m_left_panel->RemoveColorBlock(block, false); } @@ -93,6 +400,14 @@ void FilamentMapManualPanel::OnSwitchFilament(wxCommandEvent &) } this->GetParent()->Layout(); this->GetParent()->Fit(); + + if (m_right_panel->IsUseSeparation()) { + m_left_panel->Layout(); + m_left_panel->Fit(); + m_right_panel->Layout(); + m_right_panel->Fit(); + SyncPanelHeights(); + } } void FilamentMapManualPanel::Hide() @@ -101,6 +416,7 @@ void FilamentMapManualPanel::Hide() m_right_panel->Hide(); m_switch_btn->Hide(); wxPanel::Hide(); + m_timer->Stop(); } void FilamentMapManualPanel::Show() @@ -109,6 +425,8 @@ void FilamentMapManualPanel::Show() m_right_panel->Show(); m_switch_btn->Show(); wxPanel::Show(); + m_force_validation = true; + m_timer->Start(500); } GUI::FilamentMapBtnPanel::FilamentMapBtnPanel(wxWindow *parent, const wxString &label, const wxString &detail, const std::string &icon) : wxPanel(parent) diff --git a/src/slic3r/GUI/FilamentMapPanel.hpp b/src/slic3r/GUI/FilamentMapPanel.hpp index d2b6ea2..eccb1e6 100644 --- a/src/slic3r/GUI/FilamentMapPanel.hpp +++ b/src/slic3r/GUI/FilamentMapPanel.hpp @@ -8,32 +8,55 @@ namespace Slic3r { namespace GUI { +wxDECLARE_EVENT(wxEVT_INVALID_MANUAL_MAP, wxCommandEvent); + class FilamentMapManualPanel : public wxPanel { public: - FilamentMapManualPanel(wxWindow *parent, const std::vector &color, const std::vector &type, const std::vector &filament_list, const std::vector &filament_map); - - std::vector GetFilamentMaps() const { return m_filament_map; } + FilamentMapManualPanel(wxWindow *parent, + const std::vector &color, + const std::vector &type, + const std::vector &filament_list, + const std::vector &filament_map, + const std::vector &filament_volume_map); + ~FilamentMapManualPanel(); + std::vector GetFilamentMaps() const; + std::vector GetFilamentVolumeMaps() const; std::vector GetLeftFilaments() const { return m_left_panel->GetAllFilaments(); } std::vector GetRightFilaments() const { return m_right_panel->GetAllFilaments(); } + std::vector GetRightHighFlowFilaments() const { return m_right_panel->GetHighFlowFilaments(); } + std::vector GetRightStandardFilaments() const { return m_right_panel->GetStandardFilaments(); } + void UpdateNozzleVolumeType(); + void UpdateNozzleCountDisplay(); + void Hide(); void Show(); private: - void OnSwitchFilament(wxCommandEvent &); + void OnTimer(wxTimerEvent &evt); + void OnSwitchFilament(wxCommandEvent &); + void SyncPanelHeights(); + void OnDragDropCompleted(wxCommandEvent &evt); + void OnSuggestionClicked(wxCommandEvent& event); DragDropPanel *m_left_panel; - DragDropPanel *m_right_panel; + SeparatedDragDropPanel *m_right_panel; Label *m_description; Label *m_tips; + Label *m_errors; + wxPanel *m_suggestion_panel; ScalableButton *m_switch_btn; std::vector m_filament_map; + std::vector m_filament_volume_map; std::vector m_filament_list; std::vector m_filament_color; std::vector m_filament_type; + wxTimer* m_timer; + int m_invalid_id{ -1 }; + bool m_force_validation{ false }; }; class FilamentMapBtnPanel : public wxPanel diff --git a/src/slic3r/GUI/GCodeRenderer/AdvancedRenderer.cpp b/src/slic3r/GUI/GCodeRenderer/AdvancedRenderer.cpp index d4d7bd9..3e0c415 100644 --- a/src/slic3r/GUI/GCodeRenderer/AdvancedRenderer.cpp +++ b/src/slic3r/GUI/GCodeRenderer/AdvancedRenderer.cpp @@ -6,8 +6,11 @@ #include "slic3r/GUI/OpenGLManager.hpp" #include "slic3r/GUI/IMSlider.hpp" #include "slic3r/GUI/MainFrame.hpp" +#include "libslic3r/ClipperUtils.hpp" #include "libslic3r/Geometry/ConvexHull.hpp" #include +#include +#include namespace { Slic3r::Vec2f get_view_data_index_from_view_type(const Slic3r::GUI::gcode::EViewType type) diff --git a/src/slic3r/GUI/GCodeRenderer/BaseRenderer.cpp b/src/slic3r/GUI/GCodeRenderer/BaseRenderer.cpp index 66d1d2b..2802029 100644 --- a/src/slic3r/GUI/GCodeRenderer/BaseRenderer.cpp +++ b/src/slic3r/GUI/GCodeRenderer/BaseRenderer.cpp @@ -1600,8 +1600,8 @@ namespace Slic3r else btn_name = ImGui::FoldButtonIcon + boost::nowide::widen(std::string("")); ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.0f, 0.68f, 0.26f, 1.00f)); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.0f, 0.68f, 0.26f, 0.78f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.27f, 0.47f, 0.98f, 1.00f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.27f, 0.47f, 0.98f, 0.78f)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f)); float button_width = ImGui::CalcTextSize(into_u8(btn_name).c_str()).x; ImGui::SetCursorPosY(8.f); @@ -2117,7 +2117,7 @@ namespace Slic3r ImGui::SameLine(); imgui.text(_u8L("Filament change times") + ":"); ImGui::SameLine(); - ::sprintf(buf, "%d", m_print_statistics.total_filament_changes); + ::sprintf(buf, "%d", m_print_statistics.total_filament_changes + m_print_statistics.total_extruder_changes + m_print_statistics.total_nozzle_changes); imgui.text(buf); //QDS display cost ImGui::Dummy({ window_padding, window_padding }); @@ -3533,12 +3533,7 @@ namespace Slic3r ImGui::PushItemWidth(item_size); imgui.text(buf); // helio - if (view_type != EViewType::ThermalIndexMin && view_type != EViewType::ThermalIndexMax && view_type != EViewType::ThermalIndexMean) { - sprintf(buf, "%s%.0f", speed.c_str(), m_curr_move.feedrate); - ImGui::PushItemWidth(item_size); - imgui.text(buf); - } - else { + if (view_type == EViewType::ThermalIndexMin || view_type == EViewType::ThermalIndexMax || view_type == EViewType::ThermalIndexMean) { sprintf(buf, "%s", thermal_index.c_str()); ImGui::PushItemWidth(item_size); imgui.text(buf); @@ -3703,7 +3698,7 @@ namespace Slic3r break; } case EType::Logarithmic: { - global_t = (value > _min && _min > 0.0f && step != 0.0f) ? std::max(0.0f, value - _min) / step : 0.0f; + global_t = (value > _min && step != 0.0f) ? std::max(0.0f, value - _min) / step : 0.0f; break; } } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 43ece33..d54edea 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -258,7 +258,6 @@ void GLCanvas3D::LayersEditing::set_enabled(bool enabled) } float GLCanvas3D::LayersEditing::s_overlay_window_width; - void GLCanvas3D::LayersEditing::show_tooltip_information(const GLCanvas3D& canvas, std::map captions_texts, float x, float y) { ImTextureID normal_id = canvas.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP); @@ -1204,6 +1203,7 @@ void GLCanvas3D::SequentialPrintClearance::reset() wxDEFINE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_PLATE_NAME_CHANGE, SimpleEvent); +wxDEFINE_EVENT(EVT_GLCANVAS_MOVE_PLATE, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_PLATE_SELECT, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, RBtnEvent); wxDEFINE_EVENT(EVT_GLCANVAS_PLATE_RIGHT_CLICK, RBtnPlateEvent); @@ -1571,8 +1571,30 @@ void GLCanvas3D::update_gcode_sequential_view_current(unsigned int first, unsign t_gcode_viewer.update_sequential_view_current(first, last); } -unsigned int GLCanvas3D::get_volumes_count() const -{ +bool GLCanvas3D::is_in_same_model_object(const std::vector volume_ids) { + if (volume_ids.size() >= 2) { + auto gl_volume = m_selection.get_volume(volume_ids[0]); + if (gl_volume) { + std::set object_sets; + object_sets.insert(gl_volume->object_idx()); + for (int i = 1; i < volume_ids.size(); i++) { + auto temp_volume = m_selection.get_volume(volume_ids[i]); + if (temp_volume) { + auto temp_object_idx = temp_volume->object_idx(); + if (object_sets.find(temp_object_idx) == object_sets.end()) { return false; } + } else { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "check code"; + } + } + if (object_sets.size() == 1) { + return true; + } + } + } + return false; +} + +unsigned int GLCanvas3D::get_volumes_count() const { return (unsigned int)m_volumes.volumes.size(); } @@ -3305,6 +3327,10 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re float a = dynamic_cast(proj_cfg.option("wipe_tower_rotation_angle"))->value; // QDS std::vector v = dynamic_cast(m_config->option("filament_prime_volume"))->values; + if (proj_cfg.option>("prime_volume_mode")->value == pvmSaving) { + for (auto& val : v) + val = 15.f; + } Vec3d plate_origin = ppl.get_plate(plate_id)->get_origin(); const Print* print = m_process->fff_print(); @@ -3988,14 +4014,9 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) // break; //} - // QDS: use keypad to change extruder - case '1': { - if (!m_timer_set_color.IsRunning()) { - m_timer_set_color.StartOnce(500); - break; - } - } - case '0': //Color logic for material 10 + // BBS: use keypad to change extruder + case '0'://Color logic for material 10 + case '1': case '2': case '3': case '4': @@ -4004,12 +4025,14 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) case '7': case '8': case '9': { - if (m_timer_set_color.IsRunning()) { - if (keyCode < '7') keyCode += 10; - m_timer_set_color.Stop(); + int digit = keyCode - '0'; + if (m_color_input_value < 0 || !m_timer_set_color.IsRunning()) { + m_color_input_value = digit; + } else { + m_color_input_value = m_color_input_value * 10 + digit; } - if (m_gizmos.get_current_type() != GLGizmosManager::MmuSegmentation) - obj_list->set_extruder_for_selected_items(keyCode - '0'); + if (m_timer_set_color.IsRunning()) m_timer_set_color.Stop(); + m_timer_set_color.StartOnce(500); break; } @@ -4605,8 +4628,8 @@ void GLCanvas3D::on_render_timer(wxTimerEvent& evt) void GLCanvas3D::on_set_color_timer(wxTimerEvent& evt) { auto obj_list = wxGetApp().obj_list(); - if (m_gizmos.get_current_type() != GLGizmosManager::MmuSegmentation) - obj_list->set_extruder_for_selected_items(1); + if (m_gizmos.get_current_type() != GLGizmosManager::MmuSegmentation && m_color_input_value > 0) { obj_list->set_extruder_for_selected_items(m_color_input_value); } + m_color_input_value = -1; m_timer_set_color.Stop(); } @@ -4975,34 +4998,64 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // Don't deselect a volume if layer editing is enabled or any gizmo is active. We want the object to stay selected // during the scene manipulation. - if (m_picking_enabled && (!any_gizmo_active || !evt.CmdDown()) && (!m_hover_volume_idxs.empty())) { + if (m_picking_enabled && (m_gizmos.is_allow_multi_select_parts_or_objects() || !evt.CmdDown()) && (!m_hover_volume_idxs.empty())) { if (evt.LeftDown() && !m_hover_volume_idxs.empty()) { int volume_idx = get_first_hover_volume_idx(); bool already_selected = m_selection.contains_volume(volume_idx); bool ctrl_down = evt.CmdDown(); bool alt_down = evt.AltDown(); Selection::IndicesList curr_idxs = m_selection.get_volume_idxs(); - if (already_selected && ctrl_down) m_selection.remove(volume_idx); - else if (alt_down) { - Selection::EMode mode = Selection::Volume; - if (already_selected) { - std::vector volume_idxs; - for (auto idx : curr_idxs) { volume_idxs.emplace_back(idx); } - m_selection.remove_volumes(mode, volume_idxs); + else if (alt_down) {//support select multi parts + if (m_selection.is_single_full_object()) { + m_selection.clear(); + curr_idxs.clear(); + } + Selection::EMode mode = Selection::Volume; + std::vector volume_idxs; + for (auto idx : curr_idxs) { + volume_idxs.emplace_back(idx); + } + if (already_selected) { + if (!is_in_same_model_object(volume_idxs)) { + m_selection.clear(); + m_selection.add_volumes(mode, {(unsigned int) volume_idx}, false); + } else { + m_selection.remove_volumes(mode, {(unsigned int) volume_idx}); + } + } else { + volume_idxs.emplace_back(volume_idx); + if (!is_in_same_model_object(volume_idxs)) { + m_selection.clear(); + } + m_selection.add_volumes(mode, {(unsigned int) volume_idx}, false); } - std::vector add_volume_idxs; - add_volume_idxs.emplace_back(volume_idx); - m_selection.add_volumes(mode, add_volume_idxs, true); } else { - m_selection.add(volume_idx, !ctrl_down, true); - m_mouse.drag.move_requires_threshold = !already_selected; - if (already_selected) - m_mouse.set_move_start_threshold_position_2D_as_invalid(); - else - m_mouse.drag.move_start_threshold_position_2D = pos; + bool change_another_part = false; + auto cur_selection_mode = m_selection.get_mode(); + if (cur_selection_mode == Selection::Volume && !alt_down && !already_selected && curr_idxs.size() > 0) { + std::vector volume_idxs; + for (auto idx : curr_idxs) { volume_idxs.emplace_back(idx); } + auto temp_gl_volume = m_selection.get_volume(volume_idx); + auto new_select_object_idx = temp_gl_volume->object_idx(); + auto first_gl_volume = m_selection.get_volume(volume_idxs[0]); + if (first_gl_volume->object_idx() == new_select_object_idx) { + Selection::EMode mode = Selection::Volume; + m_selection.clear(); + m_selection.add_volumes(mode, {(unsigned int) volume_idx}, false); + change_another_part = true; + } + } + if (!change_another_part) { + m_selection.add(volume_idx, !ctrl_down, true); + m_mouse.drag.move_requires_threshold = !already_selected; + if (already_selected) + m_mouse.set_move_start_threshold_position_2D_as_invalid(); + else + m_mouse.drag.move_start_threshold_position_2D = pos; + } } // propagate event through callback @@ -5450,7 +5503,7 @@ void GLCanvas3D::set_tooltip(const std::string& tooltip) m_tooltip.set_text(tooltip); } -void GLCanvas3D::do_move(const std::string &snapshot_type) +void GLCanvas3D::do_move(const std::string &snapshot_type,bool force_volume_move) { if (m_model == nullptr) return; @@ -5480,7 +5533,15 @@ void GLCanvas3D::do_move(const std::string &snapshot_type) // Move instances/volumes ModelObject* model_object = m_model->objects[object_idx]; if (model_object != nullptr) { - if (selection_mode == Selection::Instance) { + if (force_volume_move || selection_mode == Selection::Volume) { + auto cur_mv = model_object->volumes[volume_idx]; + if (cur_mv->get_offset() != v->get_volume_offset()) { + cur_mv->set_transformation(v->get_volume_transformation()); + // QDS: backup + Slic3r::save_object_mesh(*model_object); + } + } + else if (selection_mode == Selection::Instance) { if (m_canvas_type == GLCanvas3D::ECanvasType::CanvasAssembleView) { if ((model_object->instances[instance_idx]->get_assemble_offset() - v->get_instance_offset()).norm() > 1e-2) { model_object->instances[instance_idx]->set_assemble_offset(v->get_instance_offset()); @@ -5489,14 +5550,6 @@ void GLCanvas3D::do_move(const std::string &snapshot_type) model_object->instances[instance_idx]->set_offset(v->get_instance_offset()); } } - else if (selection_mode == Selection::Volume) { - auto cur_mv = model_object->volumes[volume_idx]; - if (cur_mv->get_offset() != v->get_volume_offset()) { - cur_mv->set_transformation(v->get_volume_transformation()); - // QDS: backup - Slic3r::save_object_mesh(*model_object); - } - } object_moved = true; model_object->invalidate_bounding_box(); @@ -7413,8 +7466,9 @@ void GLCanvas3D::_picking_pass() wxGetApp().plater()->get_partplate_list().reset_hover_id(); if (0 <= volume_id && volume_id < (int)m_volumes.volumes.size()) { // do not add the volume id if any gizmo is active and CTRL is pressed - if (m_gizmos.get_current_type() == GLGizmosManager::EType::Undefined || !wxGetKeyState(WXK_CONTROL)) + if (m_gizmos.is_allow_multi_select_parts_or_objects() || !wxGetKeyState(WXK_CONTROL)) { hover_volume_idxs->emplace_back(volume_id); + } const_cast(&m_gizmos)->set_hover_id(-1); } else @@ -7612,7 +7666,6 @@ void GLCanvas3D::_render_bed(bool bottom, bool show_axes) #if ENABLE_RETINA_GL scale_factor = m_retina_helper->get_scale_factor(); #endif // ENABLE_RETINA_GL - //bool show_texture = true; //QDS set axes mode const auto& p_main_toolbar = get_main_toolbar(); @@ -7955,6 +8008,7 @@ void GLCanvas3D::_check_and_update_toolbar_icon_scale() if (p_main_toolbar) { p_main_toolbar->set_scale(sc); } + collapse_toolbar.set_scale(sc); size *= m_retina_helper->get_scale_factor(); @@ -10649,9 +10703,9 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, const plate_build_volume = plate->get_build_volume(); for (GLVolume* vol : volumes.volumes) { - if (!vol->is_modifier - && !vol->is_wipe_tower - && (!thumbnail_params.parts_only || vol->composite_id.volume_id >= 0) + if (!vol->is_modifier + && !vol->is_wipe_tower + && (!thumbnail_params.parts_only || vol->composite_id.volume_id >= 0) && (vol->partly_inside || is_volume_in_plate_boundingbox(*vol, plate_idx, plate_build_volume))) { visible_volumes.emplace_back(vol); } @@ -11215,7 +11269,7 @@ bool GLCanvas3D::is_flushing_matrix_error() { const std::vector &config_multiplier = (project_config.option("flush_multiplier"))->values; for (auto multiplier : config_multiplier) { - if (multiplier == 0) return true; + if (multiplier == 0 && config_matrix.size() >= config_multiplier.size() * 4) return true; } int matrix_len = config_matrix.size() / config_multiplier.size(); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 8832039..9cd7ce7 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -147,6 +147,7 @@ private: wxDECLARE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_PLATE_NAME_CHANGE, SimpleEvent); +wxDECLARE_EVENT(EVT_GLCANVAS_MOVE_PLATE, SimpleEvent); //QDS: declare EVT_GLCANVAS_PLATE_SELECT wxDECLARE_EVENT(EVT_GLCANVAS_PLATE_SELECT, SimpleEvent); @@ -587,6 +588,7 @@ private: bool m_in_render; wxTimer m_timer; wxTimer m_timer_set_color; + int m_color_input_value = -1; LayersEditing m_layers_editing; Mouse m_mouse; GLGizmosManager m_gizmos; @@ -810,6 +812,7 @@ public: void set_as_dirty(); void requires_check_outside_state() { m_requires_check_outside_state = true; } + bool is_in_same_model_object(const std::vector volume_ids); unsigned int get_volumes_count() const; const GLVolumeCollection& get_volumes() const { return m_volumes; } void reset_volumes(bool set_notice = true); @@ -864,6 +867,13 @@ public: void set_color_clip_plane(const Vec3d &cp_normal, double offset) { m_volumes.set_color_clip_plane(cp_normal, offset); } void set_color_clip_plane_colors(const std::array &colors) { m_volumes.set_color_clip_plane_colors(colors); } + // Volume color override methods (for mesh boolean gizmo) + void set_use_volume_color_override(bool use) { m_volumes.set_use_volume_color_override(use); } + void set_volume_color_override(unsigned int volume_idx, const std::array& color) { m_volumes.set_volume_color_override(volume_idx, color); } + void set_volumes_color_override(const std::vector& volume_indices, const std::array& color) { m_volumes.set_volumes_color_override(volume_indices, color); } + void clear_volume_color_override(unsigned int volume_idx) { m_volumes.clear_volume_color_override(volume_idx); } + void clear_all_volume_color_overrides() { m_volumes.clear_all_volume_color_overrides(); } + void set_color_by(const std::string& value); void set_show_world_axes(bool flag) { m_show_world_axes = flag; } @@ -1040,7 +1050,7 @@ public: void set_tooltip(const std::string& tooltip); // the following methods add a snapshot to the undo/redo stack, unless the given string is empty - void do_move(const std::string& snapshot_type); + void do_move(const std::string& snapshot_type,bool force_volume_move =false); void do_rotate(const std::string& snapshot_type); void do_scale(const std::string& snapshot_type); void do_flatten(const Vec3d& normal, const std::string& snapshot_type); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 925dfed..c62ac50 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -1087,7 +1087,7 @@ void GUI_App::post_init() } else { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " sync_user_preset: false"; } - + wxGetApp().report_consent_common(app_config->get("firstguide", "privacyuse") == "true"? true : false, "studio_improvement_policy_enable", "StudioImprovementPolicy"); /*request helio config*/ @@ -2059,6 +2059,14 @@ void GUI_App::init_networking_callbacks() }); return; } + if (return_code < 0) { //#define MQTTASYNC_SUCCESS 0 + GUI::wxGetApp().CallAfter([this] { + BOOST_LOG_TRIVIAL(trace) << "static: server connection failed"; + MessageDialog msg_dlg(nullptr, _L("Failed to connect to the cloud device server. Please check your network and firewall."), "", wxAPPLY | wxOK); + if (msg_dlg.ShowModal() == wxOK) { return; } + }); + return; + } GUI::wxGetApp().CallAfter([this] { if (is_closing()) return; @@ -2068,7 +2076,6 @@ void GUI_App::init_networking_callbacks() auto evt = new wxCommandEvent(EVT_UPDATE_MACHINE_LIST); wxQueueEvent(this, evt); } - m_agent->set_user_selected_machine(m_agent->get_user_selected_machine()); //subscribe device if (m_agent->is_user_login()) { @@ -2076,8 +2083,7 @@ void GUI_App::init_networking_callbacks() DeviceManager* dev = this->getDeviceManager(); if (!dev) return; - MachineObject *obj = dev->get_selected_machine(); - if (!obj) return; + m_load_last_machine.TryLoadFromMqttCB(m_agent, dev); /* resubscribe the cache dev list */ if (this->is_enable_multi_machine()) { @@ -2177,8 +2183,6 @@ void GUI_App::init_networking_callbacks() event.SetInt(-1); BOOST_LOG_TRIVIAL(info) << "set_on_local_connect_fn: state = " << state; } - - obj->set_lan_mode_connection_state(false); } else { if (state == ConnectStatus::ConnectStatusOk) { @@ -2936,7 +2940,7 @@ bool GUI_App::on_init_inner() const bool cancel_glmultidraw = app_config->get_bool("cancel_glmultidraw"); p_ogl_manager->set_cancle_glmultidraw(cancel_glmultidraw); - const bool b_advanced_gcode_viewer_enabled = app_config->get_bool("enable_advanced_gcode_viewer"); + const bool b_advanced_gcode_viewer_enabled = app_config->get_bool("enable_advanced_gcode_viewer_"); p_ogl_manager->set_advanced_gcode_viewer_enabled(b_advanced_gcode_viewer_enabled); } @@ -3104,6 +3108,8 @@ bool GUI_App::on_init_inner() on_init_network(); if (m_agent && m_agent->is_user_login()) { + m_load_last_machine.is_list_ok = false; + m_load_last_machine.is_mqtt_ok = false; enable_user_preset_folder(true); } else { enable_user_preset_folder(false); @@ -4273,9 +4279,13 @@ wxString GUI_App::transition_tridid(int trid_id) const return prefix; } else { - int id_index = std::clamp((int)ceil(trid_id / 4), 0, 25); - int id_suffix = trid_id % 4 + 1; - return wxString::Format("%s%d", maping_dict[id_index], id_suffix); + //y75 + // int id_index = std::clamp((int)ceil(trid_id / 4), 0, 25); + // int id_suffix = trid_id % 4 + 1; + // return wxString::Format("%s%d", maping_dict[id_index], id_suffix); + int group = trid_id / 4 + 1; + char suffix = 'A' + trid_id % 4; + return wxString::Format("%d%c", group, suffix); } } @@ -4637,6 +4647,15 @@ std::string GUI_App::handle_web_request(std::string cmd) } } } + else if (command_str.compare("get_academy_list") == 0){ + if (mainframe && root.get_child_optional("data") != boost::none) { + pt::ptree data_node = root.get_child("data"); + boost::optional region = data_node.get_optional("region"); + if (mainframe->m_webview) { + mainframe->m_webview->get_academy_list(region.value()=="oversea" ? true : false); + } + } + } else if (command_str.compare("userguide_wiki_open") == 0) { if (root.get_child_optional("data") != boost::none) { pt::ptree data_node = root.get_child("data"); @@ -5057,6 +5076,9 @@ void GUI_App::on_user_login_handle(wxCommandEvent &evt) boost::thread update_thread = boost::thread([this, dev] { dev->update_user_machine_list_info(); + CallAfter([this, dev]() { + m_load_last_machine.TryLoadFromHttpCB(m_agent, dev); + }); }); if (online_login) { @@ -8018,5 +8040,12 @@ bool is_support_filament(int extruder_id, bool strict_check) return support_option->get_at(0); }; +void TryLoadLastMachine::InnerLoad(NetworkAgent *agent, DeviceManager *dev) +{ + if (is_mqtt_ok && is_list_ok) { + if ((dev->get_selected_machine() == nullptr) && (dev->get_user_machinelist().size() > 0)) dev->set_selected_machine(agent->get_user_selected_machine()); + } +} + } // GUI } //Slic3r diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 1ec221d..4e4c5af 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -232,6 +232,25 @@ public: } }; +struct TryLoadLastMachine +{ + bool is_mqtt_ok = false; + bool is_list_ok = false; + + void InnerLoad(NetworkAgent *agent, DeviceManager *dev); + + inline void TryLoadFromMqttCB(NetworkAgent *agent, DeviceManager *dev) + { + is_mqtt_ok |= true; + InnerLoad(agent, dev); + } + inline void TryLoadFromHttpCB(NetworkAgent *agent, DeviceManager *dev) + { + is_list_ok |= true; + InnerLoad(agent, dev); + } +}; + class GUI_App : public wxApp { public: @@ -336,6 +355,8 @@ private: HttpServer m_http_server; boost::thread m_check_cert_thread; + TryLoadLastMachine m_load_last_machine; + public: //try again when subscription fails void on_start_subscribe_again(std::string dev_id); diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index a4accb4..5e67950 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -17,6 +17,7 @@ #include "PartPlate.hpp" #include "Gizmos/GLGizmoSVG.hpp" +#include "Gizmos/GLGizmoAlignment.hpp" #include #include "slic3r/Utils/FixModelByWin10.hpp" @@ -1102,6 +1103,24 @@ void MenuFactory::append_menu_item_merge_parts_to_single_part(wxMenu* menu) []() { return obj_list()->can_mesh_boolean(); }, m_parent); } +void MenuFactory::append_menu_item_merge_some_parts_to_single_part(wxMenu *menu) +{ + menu->AppendSeparator(); + append_menu_item(menu, wxID_ANY, _L("Merge into Single Part"), _L("Merge the selected parts into one part"), + [](wxCommandEvent&) { + obj_list()->merge_volumes(); + }, "", menu, + []() { + if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D) + return false; + else { + if (obj_list()->has_selected_cut_object()) return false; + return true; + } + }, + m_parent); +} + void MenuFactory::append_menu_items_mirror(wxMenu* menu) { wxMenu* mirror_menu = new wxMenu(); @@ -1217,6 +1236,8 @@ void MenuFactory::create_qdt_object_menu() append_menu_item_merge_parts_to_single_part(&m_object_menu); // Object Center append_menu_item_center(&m_object_menu); + // Object Align/Distribute + append_menu_item_align_distribute(&m_object_menu); // Object Split wxMenu* split_menu = new wxMenu(); if (!split_menu) @@ -1312,6 +1333,7 @@ void MenuFactory::create_text_part_menu() append_menu_item_fix_through_netfabb(menu); append_menu_item_simplify(menu); append_menu_item_center(menu); + append_menu_item_align_distribute(menu); append_menu_items_mirror(menu); menu->AppendSeparator(); append_menu_item_per_object_process(menu); @@ -1343,6 +1365,7 @@ void MenuFactory::create_qdt_part_menu() append_menu_item_fix_through_netfabb(menu); append_menu_item_simplify(menu); append_menu_item_center(menu); + append_menu_item_align_distribute(menu); append_menu_items_mirror(menu); wxMenu* split_menu = new wxMenu(); if (!split_menu) @@ -1489,6 +1512,20 @@ void MenuFactory::create_plate_menu() []() { return plater()->can_delete_plate(); }, m_parent); #endif + append_menu_item( + menu, wxID_ANY, _L("Move Plate"), "", + [](wxCommandEvent &e) { + int result = -1; + int hover_idx = plater()->canvas3D()->GetHoverId(); + if (hover_idx == -1) { + int plate_idx = plater()->GetPlateIndexByRightMenuInLeftUI(); + result = plater()->select_plate_by_hover_id(plate_idx * PartPlate::GRABBER_COUNT, false, false, true); + } else { + result = plater()->select_plate_by_hover_id(hover_idx, false, false, true); + } + if (result >= 0) { plater()->get_current_canvas3D()->post_event(SimpleEvent(EVT_GLCANVAS_MOVE_PLATE)); } + }, + "", nullptr, [this]() { return plater()->get_partplate_list().get_plate_count() >= 2; }, m_parent); // add shapes menu->AppendSeparator(); @@ -1653,6 +1690,7 @@ wxMenu* MenuFactory::multi_selection_menu() index++; } append_menu_item_center(menu); + append_menu_item_align_distribute(menu); append_menu_item_fix_through_netfabb(menu); //append_menu_item_simplify(menu); append_menu_item_delete(menu); @@ -1669,10 +1707,12 @@ wxMenu* MenuFactory::multi_selection_menu() } else { append_menu_item_center(menu); + append_menu_item_align_distribute(menu); auto mo = (*obj_list()->objects())[obj_idx]; if (count < mo->volumes.size()) { append_menu_item_sub_merge(menu); } + append_menu_item_merge_some_parts_to_single_part(menu); append_menu_item_fix_through_netfabb(menu); //append_menu_item_simplify(menu); append_menu_item_delete(menu); @@ -1828,6 +1868,267 @@ void MenuFactory::append_menu_item_center(wxMenu* menu) }, m_parent); } +void MenuFactory::append_menu_item_align_distribute(wxMenu *menu) +{ + wxMenu* align_distribute_menu = new wxMenu(); + if (!align_distribute_menu) + return; + append_menu_item( + align_distribute_menu, wxID_ANY, _L("Distribute left-right") + " (X)", "", + [this](wxCommandEvent &) { + auto canvas3d = plater()->get_view3D_canvas3D(); + canvas3d->get_gizmos_manager().check_object_located_outside_plate(true); + plater()->distribute_selection_x(); + }, + "", nullptr, + []() { + if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D) + return false; + else { + auto canvas3d = plater()->get_view3D_canvas3D(); + GLGizmoAlignment alignment_helper(*canvas3d); + return alignment_helper.can_distribute(GLGizmoAlignment::AlignType::DISTRIBUTE_X); + } + }, + m_parent); + + append_menu_item( + align_distribute_menu, wxID_ANY, _L("Distribute front-back") + " (Y)", "", + [this](wxCommandEvent &) { + auto canvas3d = plater()->get_view3D_canvas3D(); + canvas3d->get_gizmos_manager().check_object_located_outside_plate(true); + plater()->distribute_selection_y(); + }, "", nullptr, + []() { + if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D) + return false; + else { + auto canvas3d = plater()->get_view3D_canvas3D(); + GLGizmoAlignment alignment_helper(*canvas3d); + return alignment_helper.can_distribute(GLGizmoAlignment::AlignType::DISTRIBUTE_Y); + } + },m_parent); + + append_menu_item( + align_distribute_menu, wxID_ANY, _L("Distribute top-bottom") + " (Z)", "", + [this](wxCommandEvent &) { + auto canvas3d = plater()->get_view3D_canvas3D(); + canvas3d->get_gizmos_manager().check_object_located_outside_plate(true); + plater()->distribute_selection_z(); + }, + "", nullptr, + []() { + if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D) + return false; + else { + auto canvas3d = plater()->get_view3D_canvas3D(); + GLGizmoAlignment alignment_helper(*canvas3d); + return alignment_helper.can_distribute(GLGizmoAlignment::AlignType::DISTRIBUTE_Z); + } + }, + m_parent); + + align_distribute_menu->AppendSeparator(); + + append_menu_item( + align_distribute_menu, wxID_ANY, _L("Align left") + " (-X)", "", + [this](wxCommandEvent &) { + auto canvas3d = plater()->get_view3D_canvas3D(); + canvas3d->get_gizmos_manager().check_object_located_outside_plate(true); + plater()->align_selection_x_min(); + }, + "", nullptr, + []() { + if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D) + return false; + else { + auto canvas3d = plater()->get_view3D_canvas3D(); + GLGizmoAlignment alignment_helper(*canvas3d); + return alignment_helper.can_align(GLGizmoAlignment::AlignType::X_MIN); + } + }, + m_parent); + + append_menu_item( + align_distribute_menu, wxID_ANY, _L("Align left-right center") + " (X)", "", + [this](wxCommandEvent &) { + auto canvas3d = plater()->get_view3D_canvas3D(); + canvas3d->get_gizmos_manager().check_object_located_outside_plate(true); + plater()->align_selection_x_center(); + }, + "", nullptr, + []() { + if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D) + return false; + else { + auto canvas3d = plater()->get_view3D_canvas3D(); + GLGizmoAlignment alignment_helper(*canvas3d); + return alignment_helper.can_align(GLGizmoAlignment::AlignType::CENTER_X); + } + }, + m_parent); + + append_menu_item( + align_distribute_menu, wxID_ANY, _L("Align right") + " (+X)", "", + [this](wxCommandEvent &) { + auto canvas3d = plater()->get_view3D_canvas3D(); + canvas3d->get_gizmos_manager().check_object_located_outside_plate(true); + plater()->align_selection_x_max(); + }, + "", nullptr, + []() { + if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D) + return false; + else { + auto canvas3d = plater()->get_view3D_canvas3D(); + GLGizmoAlignment alignment_helper(*canvas3d); + return alignment_helper.can_align(GLGizmoAlignment::AlignType::X_MAX); + } + }, + m_parent); + + align_distribute_menu->AppendSeparator(); + + append_menu_item( + align_distribute_menu, wxID_ANY, _L("Align front") + " (-Y)", "", + [this](wxCommandEvent &) { + auto canvas3d = plater()->get_view3D_canvas3D(); + canvas3d->get_gizmos_manager().check_object_located_outside_plate(true); + plater()->align_selection_y_min(); + }, "", nullptr, + []() { + if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D) + return false; + else { + auto canvas3d = plater()->get_view3D_canvas3D(); + GLGizmoAlignment alignment_helper(*canvas3d); + return alignment_helper.can_align(GLGizmoAlignment::AlignType::Y_MIN); + } + }, + m_parent); + + append_menu_item( + align_distribute_menu, wxID_ANY, _L("Align front-back center") + " (Y)", "", + [this](wxCommandEvent &) { + auto canvas3d = plater()->get_view3D_canvas3D(); + canvas3d->get_gizmos_manager().check_object_located_outside_plate(true); + plater()->align_selection_y_center(); + }, "", nullptr, + []() { + if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D) + return false; + else { + auto canvas3d = plater()->get_view3D_canvas3D(); + GLGizmoAlignment alignment_helper(*canvas3d); + return alignment_helper.can_align(GLGizmoAlignment::AlignType::CENTER_Y); + } + }, + m_parent); + + append_menu_item( + align_distribute_menu, wxID_ANY, _L("Align back") + " (+Y)", "", + [this](wxCommandEvent &) { + auto canvas3d = plater()->get_view3D_canvas3D(); + canvas3d->get_gizmos_manager().check_object_located_outside_plate(true); + plater()->align_selection_y_max(); + }, + "", nullptr, + []() { + if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D) + return false; + else { + auto canvas3d = plater()->get_view3D_canvas3D(); + GLGizmoAlignment alignment_helper(*canvas3d); + return alignment_helper.can_align(GLGizmoAlignment::AlignType::Y_MAX); + } + }, + m_parent); + + align_distribute_menu->AppendSeparator(); + + append_menu_item( + align_distribute_menu, wxID_ANY, _L("Align bottom") + " (-Z)", "", + [this](wxCommandEvent &) { + auto canvas3d = plater()->get_view3D_canvas3D(); + canvas3d->get_gizmos_manager().check_object_located_outside_plate(true); + plater()->align_selection_z_min(); + }, + "", nullptr, + []() { + if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D) + return false; + else { + auto canvas3d = plater()->get_view3D_canvas3D(); + GLGizmoAlignment alignment_helper(*canvas3d); + return alignment_helper.can_align(GLGizmoAlignment::AlignType::Z_MIN); + } + }, + m_parent); + + append_menu_item( + align_distribute_menu, wxID_ANY, _L("Align top-bottom center") + " (Z)", "", + [this](wxCommandEvent &) { + auto canvas3d = plater()->get_view3D_canvas3D(); + canvas3d->get_gizmos_manager().check_object_located_outside_plate(true); + plater()->align_selection_z_center(); + }, + "", nullptr, + []() { + if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D) + return false; + else { + auto canvas3d = plater()->get_view3D_canvas3D(); + GLGizmoAlignment alignment_helper(*canvas3d); + return alignment_helper.can_align(GLGizmoAlignment::AlignType::CENTER_Z); + } + }, + m_parent); + + append_menu_item( + align_distribute_menu, wxID_ANY, _L("Align top") + " (+Z)", "", + [this](wxCommandEvent &) { + auto canvas3d = plater()->get_view3D_canvas3D(); + canvas3d->get_gizmos_manager().check_object_located_outside_plate(true); + plater()->align_selection_z_max(); + }, "", nullptr, + []() { + if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D) + return false; + else { + auto canvas3d = plater()->get_view3D_canvas3D(); + GLGizmoAlignment alignment_helper(*canvas3d); + return alignment_helper.can_align(GLGizmoAlignment::AlignType::Z_MAX); + } + }, + m_parent); + + append_submenu(menu, align_distribute_menu, wxID_ANY, _L("Align/Distribute"), _L("Align and distribute objects"), "", + []() { + if (plater()->canvas3D()->get_canvas_type() != GLCanvas3D::ECanvasType::CanvasView3D) + return false; + else { + auto canvas3d = plater()->get_view3D_canvas3D(); + GLGizmoAlignment alignment_helper(*canvas3d); + + bool has_any_align = alignment_helper.can_align(GLGizmoAlignment::AlignType::CENTER_X) || + alignment_helper.can_align(GLGizmoAlignment::AlignType::CENTER_Y) || + alignment_helper.can_align(GLGizmoAlignment::AlignType::CENTER_Z) || + alignment_helper.can_align(GLGizmoAlignment::AlignType::X_MAX) || + alignment_helper.can_align(GLGizmoAlignment::AlignType::X_MIN) || + alignment_helper.can_align(GLGizmoAlignment::AlignType::Y_MAX) || + alignment_helper.can_align(GLGizmoAlignment::AlignType::Y_MIN) || + alignment_helper.can_align(GLGizmoAlignment::AlignType::Z_MAX) || + alignment_helper.can_align(GLGizmoAlignment::AlignType::Z_MIN); + + bool has_any_distribute = alignment_helper.can_distribute(GLGizmoAlignment::AlignType::DISTRIBUTE_X) || + alignment_helper.can_distribute(GLGizmoAlignment::AlignType::DISTRIBUTE_Y) || + alignment_helper.can_distribute(GLGizmoAlignment::AlignType::DISTRIBUTE_Z); + + return has_any_align || has_any_distribute; + } + }, m_parent); +} + void MenuFactory::append_menu_item_sub_merge(wxMenu *menu) { append_menu_item( @@ -2058,6 +2359,7 @@ void MenuFactory::append_menu_item_fill_bed(wxMenu *menu) menu, wxID_ANY, _L("Fill bed with copies"), _L("Fill the remaining area of bed with copies of the selected object"), [](wxCommandEvent &) { plater()->fill_bed_with_instances(); }, "", nullptr, []() { return plater()->can_increase_instances(); }, m_parent); } + void MenuFactory::append_menu_item_plate_name(wxMenu *menu) { wxString name= _L("Edit Plate Name"); diff --git a/src/slic3r/GUI/GUI_Factories.hpp b/src/slic3r/GUI/GUI_Factories.hpp index 4ccfceb..90e6b0f 100644 --- a/src/slic3r/GUI/GUI_Factories.hpp +++ b/src/slic3r/GUI/GUI_Factories.hpp @@ -160,6 +160,7 @@ private: void append_menu_item_merge_to_multipart_object(wxMenu *menu); void append_menu_item_merge_to_single_object(wxMenu* menu); void append_menu_item_merge_parts_to_single_part(wxMenu *menu); + void append_menu_item_merge_some_parts_to_single_part(wxMenu *menu); void append_menu_items_mirror(wxMenu *menu); void append_menu_item_invalidate_cut_info(wxMenu *menu); void append_menu_item_edit_text(wxMenu *menu); @@ -179,6 +180,7 @@ private: void append_menu_item_locked(wxMenu* menu); void append_menu_item_fill_bed(wxMenu *menu); void append_menu_item_plate_name(wxMenu *menu); + void append_menu_item_align_distribute(wxMenu *menu); }; }} diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 5aa10e2..e0b7447 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -266,6 +266,8 @@ ObjectList::ObjectList(wxWindow* parent) : } #else //__WXOSX__ Bind(wxEVT_CHAR, [this](wxKeyEvent& event) { key_event(event); }); // doesn't work on OSX + // QDS: °ó¶¨¶¨Ê±Æ÷ʼþÒÔÖ§³Ö¶àλÊý¿ì½Ý¼ü + m_timer_set_extruder.Bind(wxEVT_TIMER, &ObjectList::on_set_extruder_timer, this); #endif #ifdef __WXMSW__ @@ -734,7 +736,8 @@ void ObjectList::update_filament_values_for_items_when_delete_filament(const siz } m_objects_model->SetExtruder(extruder, item); - static const char *keys[] = {"support_filament", "support_interface_filament"}; + static const char *keys[] = {"support_filament", "support_interface_filament", + "sparse_infill_filament", "solid_infill_filament", "wall_filament"}; for (auto key : keys) { if (object->config.has(key)) { if(object->config.opt_int(key) == filament_id + 1) @@ -1754,13 +1757,17 @@ void ObjectList::key_event(wxKeyEvent& event) //else if (event.GetUnicodeKey() == 'p') // toggle_printable_state(); else if (filaments_count() > 1) { - std::vector numbers = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; - wxChar key_char = event.GetUnicodeKey(); - if (std::find(numbers.begin(), numbers.end(), key_char) != numbers.end()) { - long extruder_number; - if (wxNumberFormatter::FromString(wxString(key_char), &extruder_number) && - filaments_count() >= extruder_number) - set_extruder_for_selected_items(int(extruder_number)); + // QDS: Ö§³Ö¶àλÊý¿ì½Ý¼ü£¨Óë3D³¡¾°±£³ÖÒ»Ö£¬Ö§³Ö11~16£© + int keyCode = event.GetKeyCode(); + if (keyCode >= '0' && keyCode <= '9') { + int digit = keyCode - '0'; + if (m_extruder_input_value < 0 || !m_timer_set_extruder.IsRunning()) { + m_extruder_input_value = digit; + } else { + m_extruder_input_value = m_extruder_input_value * 10 + digit; + } + if (m_timer_set_extruder.IsRunning()) m_timer_set_extruder.Stop(); + m_timer_set_extruder.StartOnce(500); } else event.Skip(); @@ -2513,7 +2520,8 @@ void ObjectList::load_shape_object(const std::string &type_name) TriangleMesh mesh = create_mesh(type_name, bb); const Slic3r::DynamicPrintConfig& full_config = wxGetApp().preset_bundle->full_config(); // rotate the overhang faces to the machine cooling fan - if (full_config.has("fan_direction") && full_config.has("auxiliary_fan")) + //y75 + if (full_config.has("fan_direction") && full_config.has("auxiliary_fan") && full_config.option("seal")->value) { int fan_config_idx = full_config.option>("fan_direction")->value; FanDirection config_dir = static_cast(fan_config_idx); @@ -5532,8 +5540,8 @@ ModelVolume* ObjectList::get_selected_model_volume() const auto obj_idx = get_selected_obj_idx(); if (vol_idx < 0 || obj_idx < 0) return nullptr; - - return (*m_objects)[obj_idx]->volumes[vol_idx]; + auto new_vol_idx = m_objects_model->get_real_volume_index_in_3d(obj_idx, vol_idx); + return (*m_objects)[obj_idx]->volumes[new_vol_idx]; } void ObjectList::change_part_type() @@ -6188,6 +6196,16 @@ void ObjectList::OnEditingDone(wxDataViewEvent &event) plater->set_current_canvas_as_dirty(); } +// QDS: ¶¨Ê±Æ÷»Øµ÷º¯Êý£¬´¦Àí¶àλÊý¿ì½Ý¼üÊäÈë +void ObjectList::on_set_extruder_timer(wxTimerEvent& evt) +{ + if (m_extruder_input_value > 0 && filaments_count() >= m_extruder_input_value) { + set_extruder_for_selected_items(m_extruder_input_value); + } + m_extruder_input_value = -1; + m_timer_set_extruder.Stop(); +} + // QDS: remove "const" qualifier void ObjectList::set_extruder_for_selected_items(const int extruder) { diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 1d47faa..a5f8391 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "Event.hpp" #include "wxExtensions.hpp" @@ -494,6 +495,11 @@ private: std::vector m_columns_width; wxSize m_last_size; + + // QDS: Ö§³Ö¶àλÊý¿ì½Ý¼üÉèÖü·³ö»ú£¨Óë3D³¡¾°±£³ÖÒ»Ö£© + wxTimer m_timer_set_extruder; + int m_extruder_input_value = -1; + void on_set_extruder_timer(wxTimerEvent& evt); }; diff --git a/src/slic3r/GUI/GUI_Utils.cpp b/src/slic3r/GUI/GUI_Utils.cpp index d683e33..0faba33 100644 --- a/src/slic3r/GUI/GUI_Utils.cpp +++ b/src/slic3r/GUI/GUI_Utils.cpp @@ -482,5 +482,125 @@ bool generate_image(const std::string &filename, wxImage &image, wxSize img_size std::deque dialogStack; +WikiPanel::WikiPanel(wxWindow *parent, const wxString &wiki_text, const wxString &tooltip, const std::string &wiki_url) + : wxPanel(parent, wxID_ANY), m_wiki_url(wiki_url), m_wiki_text(wiki_text), m_tooltip(tooltip) +{ + init_components(); + setup_layout(); + bind_events(); + + // Initially hidden by default + Show(); +} + +void WikiPanel::init_components() +{ + SetBackgroundColour(*wxWHITE); + + // Create scalable icons + m_wiki_icon = new ScalableBitmap(this, "wiki", 16); + m_wiki_icon_hover = new ScalableBitmap(this, "wiki_hover", 16); + + // Create bitmap control + m_wiki_bmp = new wxStaticBitmap(this, wxID_ANY, m_wiki_icon->bmp()); + + // Create text label + m_wiki_label = new wxStaticText(this, wxID_ANY, m_wiki_text); + m_wiki_label->SetForegroundColour(wxColour("#6B6B6B")); + m_wiki_label->SetFont(Label::Body_13); + + // Set tooltip if provided + if (!m_tooltip.IsEmpty()) { + SetToolTip(m_tooltip); + m_wiki_bmp->SetToolTip(m_tooltip); + m_wiki_label->SetToolTip(m_tooltip); + } + wxGetApp().UpdateDarkUI(this); +} + +void WikiPanel::setup_layout() +{ + m_main_sizer = new wxBoxSizer(wxHORIZONTAL); + m_main_sizer->Add(m_wiki_bmp, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT | wxBOTTOM, 2); + m_main_sizer->Add(m_wiki_label, 0, wxALIGN_CENTER_VERTICAL); + + SetSizer(m_main_sizer); +} + +void WikiPanel::bind_events() +{ + auto set_hover = [this](bool hover) { set_hover_state(hover); }; + + auto open_wiki = [this](wxMouseEvent &) { open_wiki_url(); }; + + // Bind events for both bitmap and label + m_wiki_bmp->Bind(wxEVT_LEFT_DOWN, open_wiki); + m_wiki_label->Bind(wxEVT_LEFT_DOWN, open_wiki); + + m_wiki_bmp->Bind(wxEVT_ENTER_WINDOW, [set_hover](wxMouseEvent &) { set_hover(true); }); + m_wiki_label->Bind(wxEVT_ENTER_WINDOW, [set_hover](wxMouseEvent &) { set_hover(true); }); + + m_wiki_bmp->Bind(wxEVT_LEAVE_WINDOW, [set_hover](wxMouseEvent &) { set_hover(false); }); + m_wiki_label->Bind(wxEVT_LEAVE_WINDOW, [set_hover](wxMouseEvent &) { set_hover(false); }); +} + +void WikiPanel::set_hover_state(bool hover) +{ + wxColour color = hover ? wxColour("#4479fb") : wxColour("#6B6B6B"); + m_wiki_bmp->SetBitmap(hover ? m_wiki_icon_hover->bmp() : m_wiki_icon->bmp()); + m_wiki_label->SetForegroundColour(color); + m_wiki_label->SetFont(hover ? Label::Body_13.Underlined() : Label::Body_13); + m_wiki_label->SetCursor(hover ? wxCURSOR_HAND : wxCURSOR_ARROW); + + // Refresh to update the display + Refresh(); +} + +void WikiPanel::open_wiki_url() +{ + if (!m_wiki_url.empty()) { wxLaunchDefaultBrowser(m_wiki_url); } +} + +bool WikiPanel::Show(bool show) +{ + if (m_wiki_bmp) m_wiki_bmp->Show(show); + if (m_wiki_label) m_wiki_label->Show(show); + return wxPanel::Show(show); +} + +void WikiPanel::SetWikiText(const wxString &text) +{ + m_wiki_text = text; + if (m_wiki_label) { + m_wiki_label->SetLabel(text); + Layout(); + } +} + +void WikiPanel::SetWikiUrl(const std::string &url) { + m_wiki_url = url; +} + +void WikiPanel::SetTooltip(const wxString &tooltip) +{ + m_tooltip = tooltip; + if (!tooltip.IsEmpty()) { + SetToolTip(tooltip); + if (m_wiki_bmp) m_wiki_bmp->SetToolTip(tooltip); + if (m_wiki_label) m_wiki_label->SetToolTip(tooltip); + } +} + +void WikiPanel::msw_rescale() +{ + if (m_wiki_icon) { + m_wiki_icon->msw_rescale(); + m_wiki_bmp->SetBitmap(m_wiki_icon->bmp()); + } + if (m_wiki_icon_hover) { + m_wiki_icon_hover->msw_rescale(); + } +} + } } diff --git a/src/slic3r/GUI/GUI_Utils.hpp b/src/slic3r/GUI/GUI_Utils.hpp index d008b85..6bd63ad 100644 --- a/src/slic3r/GUI/GUI_Utils.hpp +++ b/src/slic3r/GUI/GUI_Utils.hpp @@ -24,6 +24,7 @@ #include "Event.hpp" #include "../libslic3r/libslic3r_version.h" #include "../libslic3r/Utils.hpp" +#include "wxExtensions.hpp" class wxCheckBox; @@ -519,6 +520,42 @@ public: bool is_first() const { return m_count == 0; } }; +class WikiPanel : public wxPanel +{ +public: + WikiPanel(wxWindow *parent, const wxString &wiki_text = wxString("Wiki"), const wxString &tooltip = wxEmptyString, const std::string &wiki_url = "https://wiki.qidi3d.com/en/home"); + ~WikiPanel() = default; + + bool Show(bool show = true) override; + void SetWikiText(const wxString &text); + void SetWikiUrl(const std::string &url); + void SetTooltip(const wxString &tooltip); + + wxStaticBitmap *GetWikiBitmap() const { return m_wiki_bmp; } + wxStaticText *GetWikiLabel() const { return m_wiki_label; } + + void msw_rescale(); + +private: + void init_components(); + void setup_layout(); + void bind_events(); + void set_hover_state(bool hover); + void open_wiki_url(); + +private: + wxStaticBitmap *m_wiki_bmp = nullptr; + wxStaticText *m_wiki_label = nullptr; + wxBoxSizer *m_main_sizer = nullptr; + + ScalableBitmap *m_wiki_icon = nullptr; + ScalableBitmap *m_wiki_icon_hover = nullptr; + + std::string m_wiki_url; + wxString m_wiki_text; + wxString m_tooltip; +}; + /* Image Generator */ #define _3MF_COVER_SIZE wxSize(240, 240) #define PRINTER_THUMBNAIL_SMALL_SIZE wxSize(252, 188) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoAlignment.cpp b/src/slic3r/GUI/Gizmos/GLGizmoAlignment.cpp new file mode 100644 index 0000000..69d75db --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoAlignment.cpp @@ -0,0 +1,813 @@ +#include "GLGizmoAlignment.hpp" +#include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/Plater.hpp" +#include "libslic3r/Model.hpp" + +namespace Slic3r { +namespace GUI { + +GLGizmoAlignment::GLGizmoAlignment(GLCanvas3D& canvas) : m_canvas(canvas) +{ +} + +bool GLGizmoAlignment::align_objects(AlignType type) +{ + if (!validate_selection_for_align()) { + return false; + } + + switch (type) { + case AlignType::CENTER_X: + return align_to_center_x(); + case AlignType::CENTER_Y: + return align_to_center_y(); + case AlignType::CENTER_Z: + return align_to_center_z(); + case AlignType::Y_MAX: + return align_to_y_max(); + case AlignType::Y_MIN: + return align_to_y_min(); + case AlignType::X_MAX: + return align_to_x_max(); + case AlignType::X_MIN: + return align_to_x_min(); + case AlignType::Z_MAX: + return align_to_z_max(); + case AlignType::Z_MIN: + return align_to_z_min(); + default: + return false; + } +} + +bool GLGizmoAlignment::distribute_objects(AlignType type) +{ + if (!can_distribute(type)) { + return false; + } + + switch (type) { + case AlignType::DISTRIBUTE_X: + return distribute_x(); + case AlignType::DISTRIBUTE_Y: + return distribute_y(); + case AlignType::DISTRIBUTE_Z: + return distribute_z(); + default: + return false; + } +} + +bool GLGizmoAlignment::align_to_y_max() +{ + return align_objects_generic( + [](const ObjectInfo& obj) { return obj.bbox.max.y(); }, + [](Vec3d& displacement, double target, double current_max) { + displacement.y() = target - current_max; + }, + "Align Y Max" + ); +} + +bool GLGizmoAlignment::align_to_y_min() +{ + return align_objects_generic( + [](const ObjectInfo& obj) { return obj.bbox.min.y(); }, + [](Vec3d& displacement, double target, double current_min) { + displacement.y() = target - current_min; + }, + "Align Y Min" + ); +} + +bool GLGizmoAlignment::align_to_x_max() +{ + return align_objects_generic( + [](const ObjectInfo& obj) { return obj.bbox.max.x(); }, + [](Vec3d& displacement, double target, double current_max) { + displacement.x() = target - current_max; + }, + "Align X Max" + ); +} + +bool GLGizmoAlignment::align_to_x_min() +{ + return align_objects_generic( + [](const ObjectInfo& obj) { return obj.bbox.min.x(); }, + [](Vec3d& displacement, double target, double current_min) { + displacement.x() = target - current_min; + }, + "Align X Min" + ); +} + +bool GLGizmoAlignment::align_to_center_x() +{ + const Selection& selection = get_selection(); + // Handle single object selection + /*if (selection.is_single_full_object()) { + auto objects = get_selected_objects_info(); + if (objects.empty()) return false; + + GUI::PartPlate* curr_plate = GUI::wxGetApp().plater()->get_partplate_list().get_selected_plate(); + Vec3d plate_center = curr_plate->get_center_origin(); + double bed_center_x = plate_center.x(); + + if (!take_snapshot("Align to Bed Center X")) { + return false; + } + + get_selection().setup_cache(); + + for (const auto& obj : objects) { + double offset_x = bed_center_x - obj.center.x(); + if (std::abs(offset_x) > 1e-6) { + Vec3d displacement(offset_x, 0, 0); + apply_transformation(obj.object_idx, obj.instance_idx, displacement); + } + } + + finish_operation("Align to Bed Center X"); + return true; + }*/ + + // Handle multiple volume selection + if (selection.is_single_full_object() || selection.is_multiple_volume() || selection.is_multiple_modifier()) { + if (!take_snapshot("Align Parts Center X")) { + return false; + } + + get_selection().setup_cache(); + + const BoundingBoxf3& selection_bbox = selection.get_bounding_box(); + double selection_center_x = selection_bbox.center().x(); + + // Move each selected volume to align to the center of their collective bounding box + for (unsigned int idx : selection.get_volume_idxs()) { + const GLVolume* volume = selection.get_volume(idx); + if (volume) { + double current_x = volume->transformed_convex_hull_bounding_box().center().x(); + double offset_x = selection_center_x - current_x; + if (std::abs(offset_x) > 1e-6) { + Vec3d displacement(offset_x, 0, 0); + get_selection().translate(volume->object_idx(), volume->instance_idx(), volume->volume_idx(), displacement); + } + } + } + + finish_operation("Align Parts Center X",true); + return true; + } + + // Handle single volume selection + if (selection.is_single_volume() || selection.is_single_modifier()) { + auto objects = get_selected_objects_info(); + if (objects.empty()) return false; + + const auto& obj_info = objects[0]; + double object_center_x = obj_info.center.x(); + + if (!take_snapshot("Align Part to Object Center X")) { + return false; + } + + get_selection().setup_cache(); + + // Single part: align to parent object's center X + const GLVolume* volume = selection.get_first_volume(); + if (volume) { + double current_x = volume->transformed_convex_hull_bounding_box().center().x(); + double offset_x = object_center_x - current_x; + if (std::abs(offset_x) > 1e-6) { + Vec3d displacement(offset_x, 0, 0); + get_selection().translate(volume->object_idx(), volume->instance_idx(), volume->volume_idx(), displacement); + } + } + + finish_operation("Align Part to Object Center X"); + return true; + } + auto objects = get_selected_objects_info(); + if (objects.empty()) return false; + // Handle multiple objects or parts selection + if (selection.is_multiple_full_object()) { + BoundingBoxf3 selection_bbox = selection.get_bounding_box(); + double selection_center_x = selection_bbox.center().x(); + + if (!take_snapshot("Align Objects Center X")) { + return false; + } + + get_selection().setup_cache(); + + // Multiple objects: use selection bounding box for alignment + for (const auto& obj : objects) { + double offset_x = selection_center_x - obj.center.x(); + if (std::abs(offset_x) > 1e-6) { + Vec3d displacement(offset_x, 0, 0); + apply_transformation(obj.object_idx, obj.instance_idx, displacement); + } + } + + finish_operation("Align Objects Center X"); + return true; + } + + if (selection.is_multiple_volume() || selection.is_multiple_modifier()) { + BoundingBoxf3 selection_bbox = selection.get_bounding_box(); + double selection_center_x = selection_bbox.center().x(); + + if (!take_snapshot("Align Parts Center X")) { + return false; + } + + get_selection().setup_cache(); + + // Multiple parts: use selection bounding box for alignment + for (unsigned int idx : selection.get_volume_idxs()) { + const GLVolume* volume = selection.get_volume(idx); + if (volume) { + double current_x = volume->transformed_convex_hull_bounding_box().center().x(); + double offset_x = selection_center_x - current_x; + if (std::abs(offset_x) > 1e-6) { + Vec3d displacement(offset_x, 0, 0); + get_selection().translate(volume->object_idx(), volume->instance_idx(), volume->volume_idx(), displacement); + } + } + } + + finish_operation("Align Parts Center X"); + return true; + } + return false; +} + +bool GLGizmoAlignment::align_to_center_y() +{ + const Selection& selection = get_selection(); + + /*if (selection.is_single_full_object()) { + auto objects = get_selected_objects_info(); + if (objects.empty()) return false; + + if (!take_snapshot("Align Object to Bed Center Y")) { + return false; + } + + get_selection().setup_cache(); + + GUI::PartPlate* curr_plate = GUI::wxGetApp().plater()->get_partplate_list().get_selected_plate(); + Vec3d plate_center = curr_plate->get_center_origin(); + double bed_center_y = plate_center.y(); + + for (const auto& obj : objects) { + double current_y = obj.bbox.center().y(); + Vec3d displacement = Vec3d::Zero(); + displacement.y() = bed_center_y - current_y; + + if (std::abs(displacement.y()) > 1e-6) { + apply_transformation(obj.object_idx, obj.instance_idx, displacement); + } + } + + finish_operation("Align Object to Bed Center Y"); + return true; + }*/ + + if (selection.is_single_full_object() || selection.is_single_volume() || selection.is_single_modifier()) { + auto objects = get_selected_objects_info(); + if (objects.empty()) return false; + + const auto& obj_info = objects[0]; + double object_center_y = obj_info.center.y(); + + if (!take_snapshot("Align Part to Object Center Y")) { + return false; + } + + get_selection().setup_cache(); + + const GLVolume* volume = selection.get_first_volume(); + if (volume) { + double current_y = volume->transformed_convex_hull_bounding_box().center().y(); + double offset_y = object_center_y - current_y; + if (std::abs(offset_y) > 1e-6) { + Vec3d displacement(0, offset_y, 0); + get_selection().translate(volume->object_idx(), volume->instance_idx(), volume->volume_idx(), displacement); + } + } + + finish_operation("Align Part to Object Center Y"); + return true; + } + + auto objects = get_selected_objects_info(); + if (objects.empty()) return false; + + if (selection.is_multiple_full_object()) { + BoundingBoxf3 selection_bbox = selection.get_bounding_box(); + double selection_center_y = selection_bbox.center().y(); + + if (!take_snapshot("Align Objects Center Y")) { + return false; + } + + get_selection().setup_cache(); + + for (const auto& obj : objects) { + double offset_y = selection_center_y - obj.center.y(); + if (std::abs(offset_y) > 1e-6) { + Vec3d displacement(0, offset_y, 0); + apply_transformation(obj.object_idx, obj.instance_idx, displacement); + } + } + + finish_operation("Align Objects Center Y"); + return true; + } + + if (selection.is_multiple_volume() || selection.is_multiple_modifier()) { + BoundingBoxf3 selection_bbox = selection.get_bounding_box(); + double selection_center_y = selection_bbox.center().y(); + + if (!take_snapshot("Align Parts Center Y")) { + return false; + } + + get_selection().setup_cache(); + + for (unsigned int idx : selection.get_volume_idxs()) { + const GLVolume* volume = selection.get_volume(idx); + if (volume) { + double current_y = volume->transformed_convex_hull_bounding_box().center().y(); + double offset_y = selection_center_y - current_y; + if (std::abs(offset_y) > 1e-6) { + Vec3d displacement(0, offset_y, 0); + get_selection().translate(volume->object_idx(), volume->instance_idx(), volume->volume_idx(), displacement); + } + } + } + + finish_operation("Align Parts Center Y"); + return true; + } + return false; +} + +bool GLGizmoAlignment::align_to_center_z() +{ + const Selection& selection = get_selection(); + + // Single part: align to parent object's center Z + if (selection.is_single_volume() || selection.is_single_modifier()) { + auto objects = get_selected_objects_info(); + if (objects.empty()) return false; + + const auto& obj_info = objects[0]; + double object_center_z = obj_info.center.z(); + + if (!take_snapshot("Align Part to Object Center Z")) { + return false; + } + get_selection().setup_cache(); + + const GLVolume* volume = selection.get_first_volume(); + if (volume) { + double current_z = volume->transformed_convex_hull_bounding_box().center().z(); + double offset_z = object_center_z - current_z; + if (std::abs(offset_z) > 1e-6) { + Vec3d displacement(0, 0, offset_z); + get_selection().translate(volume->object_idx(), volume->instance_idx(), volume->volume_idx(), displacement); + } + } + + finish_operation("Align Part to Object Center Z"); + return true; + } + + // Handle multiple parts selection (only parts support Z alignment) + if (selection.is_single_full_object() || selection.is_multiple_volume() || selection.is_multiple_modifier()) { + BoundingBoxf3 selection_bbox = selection.get_bounding_box(); + double selection_center_z = selection_bbox.center().z(); + + if (!take_snapshot("Align Parts Center Z")) { + return false; + } + + get_selection().setup_cache(); + + for (unsigned int idx : selection.get_volume_idxs()) { + const GLVolume* volume = selection.get_volume(idx); + if (volume) { + double current_z = volume->transformed_convex_hull_bounding_box().center().z(); + double offset_z = selection_center_z - current_z; + if (std::abs(offset_z) > 1e-6) { + Vec3d displacement(0, 0, offset_z); + get_selection().translate(volume->object_idx(), volume->instance_idx(), volume->volume_idx(), displacement); + } + } + } + + finish_operation("Align Parts Center Z"); + return true; + } + return false; +} + +bool GLGizmoAlignment::align_to_z_max() +{ + return align_objects_generic( + [](const ObjectInfo& obj) { return obj.bbox.max.z(); }, + [](Vec3d& displacement, double target, double current_max) { + displacement.z() = target - current_max; + }, + "Align Z Max" + ); +} + +bool GLGizmoAlignment::align_to_z_min() +{ + return align_objects_generic( + [](const ObjectInfo& obj) { return obj.bbox.min.z(); }, + [](Vec3d& displacement, double target, double current_min) { + displacement.z() = target - current_min; + }, + "Align Z Min" + ); +} + +bool GLGizmoAlignment::distribute_y() +{ + return distribute_objects_generic( + [](const ObjectInfo& obj) { return obj.center.y(); }, + 1, + "Distribute Y" + ); +} + +bool GLGizmoAlignment::distribute_x() +{ + return distribute_objects_generic( + [](const ObjectInfo& obj) { return obj.center.x(); }, + 0, + "Distribute X" + ); +} + +bool GLGizmoAlignment::distribute_z() +{ + return distribute_objects_generic( + [](const ObjectInfo& obj) { return obj.center.z(); }, + 2, + "Distribute Z" + ); +} + +template +bool GLGizmoAlignment::align_objects_generic(GetCoordFunc get_coord, SetCoordFunc set_coord, + const std::string& operation_name) +{ + const Selection& selection = get_selection(); + + // Handle parts selection differently + if (selection.is_single_full_object() || selection.is_multiple_volume() || selection.is_multiple_modifier()) { + if (!take_snapshot(operation_name)) { + return false; + } + + get_selection().setup_cache(); + + // Get all selected volumes + std::vector volumes; + for (unsigned int idx : selection.get_volume_idxs()) { + const GLVolume* volume = selection.get_volume(idx); + if (volume) { + volumes.push_back(volume); + } + } + + if (volumes.empty()) return false; + + // Find reference coordinate + double reference_coord = 0.0; + bool first = true; + + for (const GLVolume* volume : volumes) { + BoundingBoxf3 bbox = volume->transformed_convex_hull_bounding_box(); + double coord = 0.0; + + if (operation_name.find("X Max") != std::string::npos) { + coord = bbox.max.x(); + } else if (operation_name.find("X Min") != std::string::npos) { + coord = bbox.min.x(); + } else if (operation_name.find("Y Max") != std::string::npos) { + coord = bbox.max.y(); + } else if (operation_name.find("Y Min") != std::string::npos) { + coord = bbox.min.y(); + } else if (operation_name.find("Z Max") != std::string::npos) { + coord = bbox.max.z(); + } else if (operation_name.find("Z Min") != std::string::npos) { + coord = bbox.min.z(); + } + + if (first) { + reference_coord = coord; + first = false; + } else { + if (operation_name.find("Max") != std::string::npos) { + reference_coord = std::max(reference_coord, coord); + } else if (operation_name.find("Min") != std::string::npos) { + reference_coord = std::min(reference_coord, coord); + } + } + } + + for (const GLVolume* volume : volumes) { + BoundingBoxf3 bbox = volume->transformed_convex_hull_bounding_box(); + double current_coord = 0.0; + + if (operation_name.find("X Max") != std::string::npos) { + current_coord = bbox.max.x(); + } else if (operation_name.find("X Min") != std::string::npos) { + current_coord = bbox.min.x(); + } else if (operation_name.find("Y Max") != std::string::npos) { + current_coord = bbox.max.y(); + } else if (operation_name.find("Y Min") != std::string::npos) { + current_coord = bbox.min.y(); + } else if (operation_name.find("Z Max") != std::string::npos) { + current_coord = bbox.max.z(); + } else if (operation_name.find("Z Min") != std::string::npos) { + current_coord = bbox.min.z(); + } + + Vec3d displacement = Vec3d::Zero(); + + if (operation_name.find("X") != std::string::npos) { + displacement.x() = reference_coord - current_coord; + } else if (operation_name.find("Y") != std::string::npos) { + displacement.y() = reference_coord - current_coord; + } else if (operation_name.find("Z") != std::string::npos) { + displacement.z() = reference_coord - current_coord; + } + + if (displacement.norm() > 1e-6) { + get_selection().translate(volume->object_idx(), volume->instance_idx(), volume->volume_idx(), displacement); + } + } + + finish_operation(operation_name,true); + return true; + } + + // Handle objects selection (original logic) + auto objects = get_selected_objects_info(); + if (objects.empty()) return false; + + if (!take_snapshot(operation_name)) { + return false; + } + + get_selection().setup_cache(); + + double reference_coord = get_coord(objects[0]); + for (const auto& obj : objects) { + double coord = get_coord(obj); + if (operation_name.find("Max") != std::string::npos) { + reference_coord = std::max(reference_coord, coord); + } else if (operation_name.find("Min") != std::string::npos) { + reference_coord = std::min(reference_coord, coord); + } + } + + for (const auto& obj : objects) { + double current_coord = get_coord(obj); + Vec3d displacement = Vec3d::Zero(); + set_coord(displacement, reference_coord, current_coord); + + if (displacement.norm() > 1e-6) { + apply_transformation(obj.object_idx, obj.instance_idx, displacement); + } + } + + finish_operation(operation_name); + return true; +} + +template +bool GLGizmoAlignment::distribute_objects_generic(GetCoordFunc get_coord, int axis, const std::string& operation_name) +{ + const Selection& selection = get_selection(); + + if (selection.is_single_full_object() || selection.is_multiple_volume() || selection.is_multiple_modifier()) { + std::vector volumes; + for (unsigned int idx : selection.get_volume_idxs()) { + const GLVolume* v = selection.get_volume(idx); + if (v) volumes.push_back(v); + } + + if (volumes.size() < 3) return false; + if (!take_snapshot(operation_name)) { + return false; + } + get_selection().setup_cache(); + + struct VolEntry { const GLVolume* v; double coord; }; + std::vector entries; + entries.reserve(volumes.size()); + for (const GLVolume* v : volumes) { + BoundingBoxf3 bbox = v->transformed_convex_hull_bounding_box(); + Vec3d c = bbox.center(); + double coord = (axis == 0 ? c.x() : axis == 1 ? c.y() : c.z()); + entries.push_back({v, coord}); + } + + std::sort(entries.begin(), entries.end(), [](const VolEntry& a, const VolEntry& b){ return a.coord < b.coord; }); + + BoundingBoxf3 selection_bbox = selection.get_bounding_box(); + BoundingBoxf3 left_bbox = entries.front().v->transformed_convex_hull_bounding_box(); + BoundingBoxf3 right_bbox = entries.back().v->transformed_convex_hull_bounding_box(); + auto axis_extent = [&](const BoundingBoxf3 &bb) { return (axis == 0 ? (bb.max.x() - bb.min.x()) : axis == 1 ? (bb.max.y() - bb.min.y()) : (bb.max.z() - bb.min.z())); }; + double left_width = axis_extent(left_bbox); + double right_width = axis_extent(right_bbox); + double bbox_min = (axis == 0 ? selection_bbox.min.x() : axis == 1 ? selection_bbox.min.y() : selection_bbox.min.z()); + double bbox_max = (axis == 0 ? selection_bbox.max.x() : axis == 1 ? selection_bbox.max.y() : selection_bbox.max.z()); + double min_center = bbox_min + left_width * 0.5; + double max_center = bbox_max - right_width * 0.5; + double total_distance = max_center - min_center; + double interval = (entries.size() > 1) ? total_distance / (entries.size() - 1) : 0; + for (size_t i = 1; i < entries.size() - 1; ++i) { + double target_coord = min_center + i * interval; + double current_coord = entries[i].coord; + double offset = target_coord - current_coord; + + if (std::abs(offset) > 1e-6) { + Vec3d displacement = Vec3d::Zero(); + displacement[axis] = offset; + const GLVolume *v = entries[i].v; + get_selection().translate(v->object_idx(), v->instance_idx(), v->volume_idx(), displacement); + } + } + + finish_operation(operation_name,true); + return true; + } + + auto objects = get_selected_objects_info(); + if (objects.size() < 3) return false; + + if (!take_snapshot(operation_name)) { + return false; + } + + get_selection().setup_cache(); + + std::sort(objects.begin(), objects.end(), + [&get_coord](const ObjectInfo& a, const ObjectInfo& b) { + return get_coord(a) < get_coord(b); + }); + + double min_coord = get_coord(objects.front()); + double max_coord = get_coord(objects.back()); + double total_distance = max_coord - min_coord; + double interval = (objects.size() > 1) ? total_distance / (objects.size() - 1) : 0; + for (size_t i = 1; i < objects.size() - 1; ++i) { + double target_coord = min_coord + i * interval; + double current_coord = get_coord(objects[i]); + double offset = target_coord - current_coord; + + if (std::abs(offset) > 1e-6) { + Vec3d displacement = Vec3d::Zero(); + displacement[axis] = offset; + apply_transformation(objects[i].object_idx, objects[i].instance_idx, displacement); + } + } + + finish_operation(operation_name); + return true; +} + +bool GLGizmoAlignment::can_align(AlignType type) const +{ + const Selection& selection = get_selection(); + + bool is_single_object = selection.is_single_full_object(); + bool is_multiple_objects = selection.is_multiple_full_object(); + bool is_single_part = selection.is_single_volume() || selection.is_single_modifier(); + bool is_multiple_parts = selection.is_multiple_volume() || selection.is_multiple_modifier(); + + + return validate_selection_for_align(); +} + +bool GLGizmoAlignment::can_distribute(AlignType type) const +{ + const Selection& selection = get_selection(); + if (selection.is_single_full_object()) { + size_t count = selection.get_volume_idxs().size(); + if (count >= 3) + return true; + } + if (selection.is_single_volume() || + selection.is_single_modifier()) { + return false; + } + + if (selection.is_multiple_volume() || selection.is_multiple_modifier()) { + size_t count = selection.get_volume_idxs().size(); + if (count < 3) return false; + return (type == AlignType::DISTRIBUTE_X || + type == AlignType::DISTRIBUTE_Y || + type == AlignType::DISTRIBUTE_Z); + } + + if (selection.is_multiple_full_object()) { + auto objects = get_selected_objects_info(); + if (objects.size() < 3) return false; + return (type == AlignType::DISTRIBUTE_X || type == AlignType::DISTRIBUTE_Y || type == AlignType::DISTRIBUTE_Z); + } + + return validate_selection_for_distribute(); +} + +std::vector GLGizmoAlignment::get_selected_objects_info() const +{ + std::vector objects; + Selection& selection = get_selection(); + + if (selection.is_empty()) return objects; + + const auto& content = selection.get_content(); + for (const auto& obj_it : content) { + for (const auto& inst_idx : obj_it.second) { + int obj_idx = obj_it.first; + + if (obj_idx >= 0 && obj_idx < selection.get_model()->objects.size()) { + ModelObject* object = selection.get_model()->objects[obj_idx]; + if (inst_idx >= 0 && inst_idx < object->instances.size()) { + BoundingBoxf3 bbox = object->instance_bounding_box(inst_idx); + objects.emplace_back(obj_idx, inst_idx, bbox); + } + } + } + } + + return objects; +} + +Selection& GLGizmoAlignment::get_selection() const +{ + return m_canvas.get_selection(); +} + +bool GLGizmoAlignment::take_snapshot(const std::string& name) +{ + wxGetApp().plater()->take_snapshot(name); + return true; +} + +void GLGizmoAlignment::apply_transformation(int obj_idx, int inst_idx, const Vec3d& displacement) +{ + get_selection().translate(obj_idx, inst_idx, displacement); +} + +void GLGizmoAlignment::finish_operation(const std::string &operation_name, bool force_volume_move) +{ + get_selection().notify_instance_update(-1, -1); + m_canvas.do_move(operation_name, force_volume_move); +} + +bool GLGizmoAlignment::validate_selection_for_align() const +{ + const Selection& selection = get_selection(); + + if (selection.is_single_full_object() || + selection.is_single_volume() || + selection.is_single_modifier()) { + return true; + } + + return selection.get_volume_idxs().size() >= 2; +} + +bool GLGizmoAlignment::validate_selection_for_distribute() const +{ + const Selection& selection = get_selection(); + + if (selection.is_single_full_object() || selection.is_multiple_full_object()) { + std::set object_indices; + for (int volume_idx : selection.get_volume_idxs()) { + const GLVolume* volume = selection.get_volume(volume_idx); + if (volume) { + object_indices.insert(volume->object_idx()); + } + } + return object_indices.size() >= 3; + } + + return selection.get_volume_idxs().size() >= 3; +} + +} // namespace GUI +} // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/Gizmos/GLGizmoAlignment.hpp b/src/slic3r/GUI/Gizmos/GLGizmoAlignment.hpp new file mode 100644 index 0000000..a748c15 --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoAlignment.hpp @@ -0,0 +1,91 @@ +#ifndef slic3r_GLGizmoAlignment_hpp_ +#define slic3r_GLGizmoAlignment_hpp_ + +#include "libslic3r/Point.hpp" +#include "libslic3r/BoundingBox.hpp" +#include "slic3r/GUI/Selection.hpp" +#include +#include + +namespace Slic3r { +namespace GUI { + +class GLCanvas3D; + +class GLGizmoAlignment +{ +public: + enum class AlignType { + NONE = -1, + CENTER_X, + CENTER_Y, + CENTER_Z, + Y_MAX, + Y_MIN, + X_MAX, + X_MIN, + Z_MAX, + Z_MIN, + DISTRIBUTE_X, + DISTRIBUTE_Y, + DISTRIBUTE_Z + }; + struct ObjectInfo { + int object_idx; + int instance_idx; + BoundingBoxf3 bbox; + Vec3d center; + + ObjectInfo(int obj_idx, int inst_idx, const BoundingBoxf3& bb) + : object_idx(obj_idx), instance_idx(inst_idx), bbox(bb), center(bb.center()) {} + }; + + explicit GLGizmoAlignment(GLCanvas3D& canvas); + ~GLGizmoAlignment() = default; + + bool align_objects(AlignType type); + bool distribute_objects(AlignType type); + + bool align_to_center_x(); + bool align_to_center_y(); + bool align_to_center_z(); + bool align_to_y_max(); + bool align_to_y_min(); + bool align_to_x_max(); + bool align_to_x_min(); + bool align_to_z_max(); + bool align_to_z_min(); + + bool distribute_x(); + bool distribute_y(); + bool distribute_z(); + + bool can_align(AlignType type) const; + bool can_distribute(AlignType type) const; + + std::vector get_selected_objects_info() const; + +private: + GLCanvas3D& m_canvas; + + template + bool align_objects_generic(GetCoordFunc get_coord, SetCoordFunc set_coord, + const std::string& operation_name); + + template + bool distribute_objects_generic(GetCoordFunc get_coord, int axis, + const std::string& operation_name); + + Selection& get_selection() const; + bool take_snapshot(const std::string& name); + void apply_transformation(int obj_idx, int inst_idx, const Vec3d& displacement); + void finish_operation(const std::string &operation_name, bool force_volume_move = false); + + bool validate_selection_for_align() const; + bool validate_selection_for_distribute() const; +}; + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_GLGizmoAlignment_hpp_ \ No newline at end of file diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.cpp index c680261..442a90a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.cpp @@ -11,6 +11,7 @@ #include "libslic3r/CSGMesh/CSGMesh.hpp" #include "libslic3r/CSGMesh/ModelToCSGMesh.hpp" #include "libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp" +#include "slic3r/GUI/Jobs/BooleanOperationJob.hpp" #ifndef IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS @@ -23,6 +24,13 @@ namespace Slic3r { namespace GUI { +// ======================================================================================== +// Boolean Operation Mode Configuration +// Set to 1 = Use ASYNC mode (new, default) - background thread with progress & cancel +// Set to 0 = Use SYNC mode (old, stable) - UI thread blocking execution +// ======================================================================================== +#define USE_ASYNC_BOOLEAN_MODE 1 + // ========================== DEFINE STATIC WARNING MSG ========================== const std::string MeshBooleanWarnings::COMMON = _u8L("Unable to perform boolean operation on selected parts."); @@ -34,9 +42,9 @@ const std::string MeshBooleanWarnings::MIN_VOLUMES_DIFFERENCE = _u8L("Differen const std::string MeshBooleanWarnings::MIN_OBJECTS_UNION = _u8L("Union operation requires at least two objects."); const std::string MeshBooleanWarnings::MIN_OBJECTS_INTERSECTION = _u8L("Intersection operation requires at least two objects."); const std::string MeshBooleanWarnings::MIN_OBJECTS_DIFFERENCE = _u8L("Difference operation requires at least one object in both A and B lists."); -const std::string MeshBooleanWarnings::GROUPING = _u8L("Failed to group and merge parts by object. Please check for self-intersections, non-manifold geometry, or open meshes."); +const std::string MeshBooleanWarnings::GROUPING = _u8L("Failed to group parts by object. Please check for self-intersections, non-manifold geometry, or open meshes."); const std::string MeshBooleanWarnings::OVERLAPING = _u8L("No overlapping region found. Boolean intersection failed."); - +const std::string MeshBooleanWarnings::PREPAREING = _u8L("Mesh preparation failed."); // ========================== UTILITY FUNCTIONS ========================== namespace { @@ -170,42 +178,6 @@ namespace { } } // anonymous namespace -// ========================== COLOR OVERRIDES ========================== - -void GLGizmoMeshBoolean::ColorOverrideManager::apply_for_indices(const Selection &selection, - const std::vector& volume_indices, - const std::array& rgba) -{ - for (unsigned int idx : volume_indices) { - const GLVolume *glv_c = selection.get_volume(idx); - if (!glv_c) continue; - GLVolume *glv = const_cast(glv_c); - if (saved.find(idx) == saved.end()) { - saved.emplace(idx, SavedVolumeColorState{ glv->color, glv->force_native_color, glv->force_neutral_color }); - } - glv->force_native_color = true; - glv->force_neutral_color = false; - glv->set_color(rgba); - glv->set_render_color(rgba[0], rgba[1], rgba[2], rgba[3]); - } - applied = true; -} - -void GLGizmoMeshBoolean::ColorOverrideManager::restore_non_selected_indices(const Selection &selection) -{ - for (auto &it : saved) { - unsigned int vol_idx = it.first; - const GLVolume *glv_c = selection.get_volume(vol_idx); - if (!glv_c) continue; - GLVolume *glv = const_cast(glv_c); - const SavedVolumeColorState &s = it.second; - glv->force_native_color = s.original_force_native_color; - glv->force_neutral_color = s.original_force_neutral_color; - glv->set_color(s.original_color); - glv->set_render_color(s.original_color[0], s.original_color[1], s.original_color[2], s.original_color[3]); - } - clear(); -} // ========================== WARNING SYSTEM IMPLEMENTATION ========================== // All methods for managing and displaying warnings @@ -415,8 +387,30 @@ bool VolumeListManager::selection_changed(const Selection& selection){ for (unsigned int vol_idx : volume_idxs) { cur_volumes.push_back(vol_idx); } - return !is_equal_ignore_order(m_working_volumes, cur_volumes); + // First check: indices changed? + if (!is_equal_ignore_order(m_working_volumes, cur_volumes)) { + return true; + } + + // Second check: indices same but volumes were replaced (undo/redo case) + // Verify that all cached volume indices still point to valid volumes + // After undo/redo, GLVolume pointers may be invalidated even if indices match + for (unsigned int idx : m_working_volumes) { + const GLVolume* glv = selection.get_volume(idx); + if (!glv) { + // Volume index no longer valid - undo/redo occurred + return true; + } + + ModelVolume* mv = get_model_volume(*glv, selection.get_model()->objects); + if (!mv) { + // ModelVolume pointer invalid - undo/redo occurred + return true; + } + } + + return false; } void VolumeListManager::init_part_mode_lists(const Selection& selection) { clear_all(); @@ -723,7 +717,7 @@ static void attach_ignored_non_models_to_target(ModelObject* target_object, // Create and transform mesh TriangleMesh local_mesh = nv->mesh(); - local_mesh.transform(pre); + local_mesh.transform(pre, true); // Pass fix_left_handed=true to handle mirrored objects correctly // Create new volume and copy properties ModelVolume* attached = target_object->add_volume(std::move(local_mesh), nv->type(), false); @@ -744,9 +738,14 @@ static void attach_ignored_non_models_to_target(ModelObject* target_object, } } -// Core boolean operation engine - handles union, intersection, difference operations +// ======================================================================================== +// BOOLEAN OPERATION ENGINE IMPLEMENTATION +// ======================================================================================== + BooleanOperationEngine::BooleanOperationEngine() {} +// ========================== PUBLIC: MAIN OPERATION METHODS ========================== + BooleanOperationResult BooleanOperationEngine::perform_union(const VolumeListManager& volume_manager, const Selection& selection, const BooleanOperationSettings& settings) { @@ -796,13 +795,11 @@ BooleanOperationResult BooleanOperationEngine::perform_intersection(const Volume return result; } TriangleMesh m = *acc; - // Slic3r::MeshBoolean::self_union(m); pre_union_result.push_back(m); } TriangleMesh cur = pre_union_result[0]; for (size_t i = 1; i < pre_union_result.size(); ++i) { - // try { Slic3r::MeshBoolean::self_union(o); } catch (...) {} cur = execute_boolean_operation(cur, pre_union_result[i], MeshBooleanConfig::OP_INTERSECTION); if (cur.empty()) { result.error_message = MeshBooleanWarnings::OVERLAPING; @@ -865,7 +862,7 @@ BooleanOperationResult BooleanOperationEngine::perform_difference(const VolumeLi b_union = execute_boolean_operation(b_union, bm, MeshBooleanConfig::OP_UNION); if (b_union.empty()) { result.error_message = MeshBooleanWarnings::MIN_VOLUMES_DIFFERENCE; return result; } } catch (const std::exception &e) { - result.error_message = std::string("Boolean union (B group) failed: ") + e.what(); + result.error_message = MeshBooleanWarnings::GROUPING; return result; } } @@ -875,7 +872,6 @@ BooleanOperationResult BooleanOperationEngine::perform_difference(const VolumeLi result.error_message = MeshBooleanWarnings::MIN_VOLUMES_DIFFERENCE; return result; } - try { Slic3r::MeshBoolean::self_union(b_union); } catch (...) {} // For each A object, subtract all B volumes for (const auto &kv : a_by_object) { @@ -889,7 +885,7 @@ BooleanOperationResult BooleanOperationEngine::perform_difference(const VolumeLi try { accumulated_result = execute_boolean_operation(accumulated_result, b_union, MeshBooleanConfig::OP_DIFFERENCE); } catch (const std::exception& e) { - result.error_message = std::string("Boolean difference (object mode) failed: ") + e.what(); + result.error_message = MeshBooleanWarnings::JOB_FAILED; return result; } @@ -934,6 +930,7 @@ std::string BooleanOperationEngine::validate_operation(MeshBooleanOperation type return ""; } +// ========================== PRIVATE: VOLUME PROCESSING HELPERS ========================== std::vector BooleanOperationEngine::prepare_volumes( const std::vector& volume_indices, @@ -980,7 +977,7 @@ TriangleMesh BooleanOperationEngine::get_transformed_mesh(const VolumeInfo& volu mesh = volume_info.model_volume->mesh(); if (mesh.empty()) return mesh; - mesh.transform(volume_info.transformation); + mesh.transform(volume_info.transformation, true); return mesh; } @@ -997,45 +994,101 @@ TriangleMesh BooleanOperationEngine::execute_boolean_operation(const TriangleMes return TriangleMesh(); } -std::optional BooleanOperationEngine::execute_boolean_on_meshes(const std::vector &volumes, const std::string &operation) const +// ======================================================================================== +// ASYNCHRONOUS IMPLEMENTATION +// Only compiled when USE_ASYNC_BOOLEAN_MODE = 1 +// ======================================================================================== +#if USE_ASYNC_BOOLEAN_MODE + +// Core boolean logic with CGAL fallback - used by async version +std::optional BooleanOperationEngine::execute_boolean_on_meshes_async( + const std::vector& meshes, + const std::string& operation, + std::function cancel_cb, + std::function progress_cb) const { - if (volumes.empty()) return std::nullopt; + if (meshes.empty()) return std::nullopt; + + if (meshes.size() == 1) { + return meshes[0]; // Single mesh - no operation needed + } + + TriangleMesh accumulated_result = meshes[0]; + const size_t total_ops = meshes.size() - 1; + + for (size_t i = 1; i < meshes.size(); ++i) { + // Check for cancellation + if (cancel_cb && cancel_cb()) { + return std::nullopt; + } + + // Calculate progress for this step + const size_t current_op = i - 1; + std::function sub_progress_cb; + if (progress_cb) { + sub_progress_cb = [progress_cb, total_ops, current_op](float sub_progress) { + // Clamp sub_progress from mcut (should be 0-100, but defensive programming) + float clamped_sub = std::min(100.0f, std::max(0.0f, sub_progress)); + float sub_progress_normalized = clamped_sub / 100.0f; + float total_progress = (float(current_op) + sub_progress_normalized) / float(total_ops); + // Ensure total_progress is within [0, 1] + total_progress = std::min(1.0f, std::max(0.0f, total_progress)); + progress_cb(total_progress); + }; + } - TriangleMesh accumulated_result = get_transformed_mesh(volumes[0]); - for (size_t i = 1; i < volumes.size(); ++i) { - TriangleMesh next_mesh = get_transformed_mesh(volumes[i]); try { - accumulated_result = execute_boolean_operation(accumulated_result, next_mesh, operation); + // Execute boolean operation + std::vector result_meshes; + Slic3r::MeshBoolean::mcut::make_boolean( + accumulated_result, meshes[i], result_meshes, + operation, cancel_cb, sub_progress_cb); - // Check if result is empty - if (accumulated_result.empty()) { - BOOST_LOG_TRIVIAL(warning) << "Boolean operation returned empty mesh at step " << i; + // Check for cancellation after operation + if (cancel_cb && cancel_cb()) { + return std::nullopt; + } + + // Check if operation succeeded and get result + bool operation_returned_empty = result_meshes.empty(); + if (!operation_returned_empty) { + accumulated_result = std::move(result_meshes[0]); + } + + // Check if result is empty (either empty vector or empty mesh) + if (operation_returned_empty || accumulated_result.empty()) { + BOOST_LOG_TRIVIAL(warning) << "[Mesh Boolean] Boolean operation returned empty mesh at step " << i; // For INTERSECTION: try CGAL as fallback if (operation == MeshBooleanConfig::OP_INTERSECTION) { - BOOST_LOG_TRIVIAL(info) << "Attempting CGAL fallback for intersection..."; + BOOST_LOG_TRIVIAL(info) << "[Mesh Boolean] Attempting CGAL fallback for intersection..."; // Rebuild the mesh before the failed operation - TriangleMesh acc_before = get_transformed_mesh(volumes[0]); + TriangleMesh acc_before = meshes[0]; for (size_t j = 1; j < i; ++j) { - TriangleMesh temp_mesh = get_transformed_mesh(volumes[j]); - acc_before = execute_boolean_operation(acc_before, temp_mesh, operation); - // Minimal cleanup after each step - its_merge_vertices(acc_before.its); - its_remove_degenerate_faces(acc_before.its); + std::vector temp_result; + Slic3r::MeshBoolean::mcut::make_boolean( + acc_before, meshes[j], temp_result, + operation, cancel_cb, nullptr); + if (!temp_result.empty()) { + acc_before = std::move(temp_result[0]); + // Minimal cleanup after each step + its_merge_vertices(acc_before.its); + its_remove_degenerate_faces(acc_before.its); + } } try { - Slic3r::MeshBoolean::cgal::intersect(acc_before, next_mesh); + Slic3r::MeshBoolean::cgal::intersect(acc_before, meshes[i]); if (!acc_before.empty()) { - BOOST_LOG_TRIVIAL(info) << "CGAL fallback succeeded!"; + BOOST_LOG_TRIVIAL(info) << "[Mesh Boolean] CGAL fallback succeeded!"; accumulated_result = acc_before; } else { - BOOST_LOG_TRIVIAL(warning) << "CGAL also returned empty mesh"; + BOOST_LOG_TRIVIAL(warning) << "[Mesh Boolean] CGAL also returned empty mesh"; return std::nullopt; } } catch (const std::exception& e) { - BOOST_LOG_TRIVIAL(warning) << "CGAL fallback failed: " << e.what(); + BOOST_LOG_TRIVIAL(warning) << "[Mesh Boolean] CGAL fallback failed: " << e.what(); return std::nullopt; } } else { @@ -1051,112 +1104,23 @@ std::optional BooleanOperationEngine::execute_boolean_on_meshes(co its_merge_vertices(accumulated_result.its, true); its_remove_degenerate_faces(accumulated_result.its, true); its_compactify_vertices(accumulated_result.its, true); -#if 0 - // Rebuild mesh from scratch - indexed_triangle_set cleaned_its; - cleaned_its.vertices = accumulated_result.its.vertices; - cleaned_its.indices = accumulated_result.its.indices; - accumulated_result = TriangleMesh(std::move(cleaned_its)); - - its_merge_vertices(accumulated_result.its, true); - its_compactify_vertices(accumulated_result.its, true); } else { // Minimal cleanup for difference/union to preserve precision its_remove_degenerate_faces(accumulated_result.its); its_compactify_vertices(accumulated_result.its); -#endif } } catch (const std::exception &e) { - BOOST_LOG_TRIVIAL(warning) << "Executing boolean on meshes failed: " << e.what(); + BOOST_LOG_TRIVIAL(warning) << "[Mesh Boolean] Executing boolean on meshes failed: " << e.what(); return std::nullopt; } } return accumulated_result; } -BooleanOperationResult BooleanOperationEngine::part_level_boolean(const std::vector& volumes, - const BooleanOperationSettings& settings, - const std::string& operation, - bool allow_single_volume) const { - BooleanOperationResult result; - // Difference - if (operation == MeshBooleanConfig::OP_DIFFERENCE) { return part_level_sub(volumes, volumes, settings); } - // Union or Intersection - if (volumes.size() < 2 && !allow_single_volume) { - result.error_message = MeshBooleanWarnings::COMMON; - return result; - } - validate_before_boolean(volumes, result); - if (result.error_message != "") return result; +#endif // USE_ASYNC_BOOLEAN_MODE - auto acc = execute_boolean_on_meshes(volumes, operation); - if (!acc.has_value()) { - result.success = false; - result.error_message = operation == MeshBooleanConfig::OP_UNION ? MeshBooleanWarnings::JOB_FAILED : MeshBooleanWarnings::OVERLAPING; - return result; - } - TriangleMesh accumulated_mesh = *acc; - - result.success = true; - result.result_meshes.push_back(*acc); - result.source_transforms.push_back(volumes[0].transformation); - result.source_volumes.push_back(volumes[0].model_volume); // Use the first volume as the source for transform/context - update_delete_list(result, volumes, settings); - return result; -} - -BooleanOperationResult BooleanOperationEngine::part_level_sub(const std::vector& volumes_a, - const std::vector& volumes_b, - const BooleanOperationSettings& settings) const { - BooleanOperationResult result; - - if (volumes_a.empty() || volumes_b.empty()) { - result.error_message = MeshBooleanWarnings::COMMON; - return result; - } - validate_before_boolean(volumes_a, result); - if (result.error_message != "") return result; - validate_before_boolean(volumes_b, result); - if (result.error_message != "") return result; - // Process each A volume: A_i = A_i - ALL_B_volumes - for (size_t a_idx = 0; a_idx < volumes_a.size(); ++a_idx) { - TriangleMesh accumulated_result = get_transformed_mesh(volumes_a[a_idx]); - - // Subtract ALL B volumes sequentially - for (size_t b_idx = 0; b_idx < volumes_b.size(); ++b_idx) { - TriangleMesh b_mesh = get_transformed_mesh(volumes_b[b_idx]); - - try { - // Only A-B operation - accumulated_result = execute_boolean_operation(accumulated_result, b_mesh, MeshBooleanConfig::OP_DIFFERENCE); - - if (accumulated_result.empty()) { - result.error_message = "Boolean difference operation failed for volume " + volumes_a[a_idx].model_volume->name; - return result; - } - } catch (const std::exception& e) { - result.error_message = std::string("Boolean difference operation failed: ") + e.what(); - return result; - } - - } - - result.result_meshes.push_back(accumulated_result); - result.source_volumes.push_back(volumes_a[a_idx].model_volume); - result.source_transforms.push_back(volumes_a[a_idx].transformation); - } - - // Delete B volumes if not keeping original models - if (!settings.keep_original_models) { - for (const auto& volume_b : volumes_b) { - result.volumes_to_delete.push_back(volume_b.model_volume); - } - } - - result.success = true; - return result; -} +// ========================== PRIVATE: MODEL MANIPULATION HELPERS ========================== // Collect volumes that need to be deleted (excluding the source/first volume) // Note: volumes[0] is handled separately in apply_result_to_model via should_replace_source @@ -1283,10 +1247,25 @@ void BooleanOperationEngine::apply_result_to_model(const BooleanOperationResult& // ===== STEP 3: Delete source volumes (now that new ones exist) ===== // This is the "replace" operation: delete old volumes[0] from each result - for (ModelVolume* src : sources_to_replace) { + // Collect indices first and delete from back to front to avoid iterator invalidation + if (!sources_to_replace.empty()) { + std::vector indices_to_delete; auto &volumes = target_object->volumes; - if (auto it = std::find(volumes.begin(), volumes.end(), src); it != volumes.end()) - target_object->delete_volume(std::distance(volumes.begin(), it)); + + for (ModelVolume* src : sources_to_replace) { + auto it = std::find(volumes.begin(), volumes.end(), src); + if (it != volumes.end()) { + indices_to_delete.push_back(std::distance(volumes.begin(), it)); + } + } + + // Sort indices in descending order and delete from back to front + std::sort(indices_to_delete.begin(), indices_to_delete.end(), std::greater()); + for (size_t idx : indices_to_delete) { + if (idx < target_object->volumes.size()) { + target_object->delete_volume(idx); + } + } } // Attach ignored non-model volumes before any deletion to avoid dangling pointers @@ -1379,7 +1358,7 @@ ModelVolume* BooleanOperationEngine::create_result_volume(ModelObject* target_ob // World -> (target instance * source volume) local Transform3d world_to_target_local = (target_instance_matrix * source_volume_matrix).inverse(); - local_mesh.transform(world_to_target_local); + local_mesh.transform(world_to_target_local, true); // Create new volume with the local coordinate mesh (no re-centering), then copy metadata ModelVolumeType result_type = source_volume->type(); @@ -1442,6 +1421,182 @@ void BooleanOperationEngine::delete_volumes_from_model(const std::vector BooleanOperationEngine::execute_boolean_on_meshes( + const std::vector &volumes, + const std::string &operation) const +{ + if (volumes.empty()) return std::nullopt; + + TriangleMesh accumulated_result = get_transformed_mesh(volumes[0]); + for (size_t i = 1; i < volumes.size(); ++i) { + TriangleMesh next_mesh = get_transformed_mesh(volumes[i]); + try { + accumulated_result = execute_boolean_operation(accumulated_result, next_mesh, operation); + + // Check if result is empty + if (accumulated_result.empty()) { + BOOST_LOG_TRIVIAL(warning) << "Boolean operation returned empty mesh at step " << i; + + // For INTERSECTION: try CGAL as fallback + if (operation == MeshBooleanConfig::OP_INTERSECTION) { + BOOST_LOG_TRIVIAL(info) << "Attempting CGAL fallback for intersection..."; + + // Rebuild the mesh before the failed operation + TriangleMesh acc_before = get_transformed_mesh(volumes[0]); + for (size_t j = 1; j < i; ++j) { + TriangleMesh temp_mesh = get_transformed_mesh(volumes[j]); + acc_before = execute_boolean_operation(acc_before, temp_mesh, operation); + // Minimal cleanup after each step + its_merge_vertices(acc_before.its); + its_remove_degenerate_faces(acc_before.its); + } + + try { + Slic3r::MeshBoolean::cgal::intersect(acc_before, next_mesh); + if (!acc_before.empty()) { + BOOST_LOG_TRIVIAL(info) << "CGAL fallback succeeded!"; + accumulated_result = acc_before; + } else { + BOOST_LOG_TRIVIAL(warning) << "CGAL also returned empty mesh"; + return std::nullopt; + } + } catch (const std::exception& e) { + BOOST_LOG_TRIVIAL(warning) << "CGAL fallback failed: " << e.what(); + return std::nullopt; + } + } else { + // For other operations, just return nullopt + return std::nullopt; + } + } + + // Apply mesh cleanup based on operation type + if (operation == MeshBooleanConfig::OP_INTERSECTION) { + // Aggressive cleanup for intersection + its_remove_degenerate_faces(accumulated_result.its); + its_merge_vertices(accumulated_result.its, true); + its_remove_degenerate_faces(accumulated_result.its, true); + its_compactify_vertices(accumulated_result.its, true); + } else { + // Minimal cleanup for difference/union to preserve precision + its_remove_degenerate_faces(accumulated_result.its); + its_compactify_vertices(accumulated_result.its); + } + + } catch (const std::exception &e) { + BOOST_LOG_TRIVIAL(warning) << "Executing boolean on meshes failed: " << e.what(); + return std::nullopt; + } + } + return accumulated_result; +} + +// Part level boolean operation +// NOTE: This is NOT used by async version +BooleanOperationResult BooleanOperationEngine::part_level_boolean( + const std::vector& volumes, + const BooleanOperationSettings& settings, + const std::string& operation, + bool allow_single_volume) const +{ + BooleanOperationResult result; + // Difference + if (operation == MeshBooleanConfig::OP_DIFFERENCE) { + return part_level_sub(volumes, volumes, settings); + } + // Union or Intersection + if (volumes.size() < 2 && !allow_single_volume) { + result.error_message = MeshBooleanWarnings::COMMON; + return result; + } + validate_before_boolean(volumes, result); + if (result.error_message != "") return result; + + auto acc = execute_boolean_on_meshes(volumes, operation); + if (!acc.has_value()) { + result.success = false; + result.error_message = operation == MeshBooleanConfig::OP_UNION ? MeshBooleanWarnings::JOB_FAILED : MeshBooleanWarnings::OVERLAPING; + return result; + } + TriangleMesh accumulated_mesh = *acc; + + // Set result + result.result_meshes.push_back(accumulated_mesh); + result.source_volumes.push_back(volumes[0].model_volume); + result.source_transforms.push_back(volumes[0].transformation); + + // Delete list + update_delete_list(result, volumes, settings); + + result.success = true; + return result; +} + +// Part level subtraction (difference) operation +// NOTE: This is NOT used by async version +BooleanOperationResult BooleanOperationEngine::part_level_sub( + const std::vector& volumes_a, + const std::vector& volumes_b, + const BooleanOperationSettings& settings) const +{ + BooleanOperationResult result; + + if (volumes_a.empty() || volumes_b.empty()) { + result.error_message = MeshBooleanWarnings::COMMON; + return result; + } + validate_before_boolean(volumes_a, result); + if (result.error_message != "") return result; + validate_before_boolean(volumes_b, result); + if (result.error_message != "") return result; + + // Process each A volume: A_i = A_i - ALL_B_volumes + for (size_t a_idx = 0; a_idx < volumes_a.size(); ++a_idx) { + TriangleMesh accumulated_result = get_transformed_mesh(volumes_a[a_idx]); + + // Subtract ALL B volumes sequentially + for (size_t b_idx = 0; b_idx < volumes_b.size(); ++b_idx) { + TriangleMesh b_mesh = get_transformed_mesh(volumes_b[b_idx]); + + try { + // Only A-B operation + accumulated_result = execute_boolean_operation(accumulated_result, b_mesh, MeshBooleanConfig::OP_DIFFERENCE); + + if (accumulated_result.empty()) { + result.error_message = MeshBooleanWarnings::JOB_FAILED; + return result; + } + } catch (const std::exception& e) { + result.error_message = MeshBooleanWarnings::JOB_FAILED; + return result; + } + + } + + result.result_meshes.push_back(accumulated_result); + result.source_volumes.push_back(volumes_a[a_idx].model_volume); + result.source_transforms.push_back(volumes_a[a_idx].transformation); + } + + // Delete B volumes if not keeping original models + if (!settings.keep_original_models) { + for (const auto& volume_b : volumes_b) { + result.volumes_to_delete.push_back(volume_b.model_volume); + } + } + + result.success = true; + return result; +} + // ========================== GIZMO LIFECYCLE METHODS ========================== GLGizmoMeshBoolean::GLGizmoMeshBoolean(GLCanvas3D& parent, unsigned int sprite_id) @@ -1496,15 +1651,37 @@ GLGizmoMeshBoolean::GLGizmoMeshBoolean(GLCanvas3D& parent, unsigned int sprite_i // Refresh object caches after list changes m_volume_manager.update_obj_lists(m_parent.get_selection()); - // Handle color overrides - if (m_color_overrides.applied) { - restore_list_color_overrides(); - } + // Handle color overrides - refresh them after list changes + restore_list_color_overrides(); + apply_color_overrides_for_mode(m_operation_mode); + m_warning_manager.clear_warnings(); refresh_canvas(); return true; // Delete successful }; + // Setup async callbacks + m_ui->is_async_enabled = [this]() -> bool { + return true; // Async is always enabled + }; + + m_ui->is_async_busy = [this]() -> bool { + return m_async_job_running; + }; + + m_ui->get_async_progress = [this]() -> float { + // Return progress from thread-safe member variable (0-100) + std::lock_guard lk(m_async_mutex); + return m_async_boolean_operation_progress; + }; + + m_ui->on_cancel_async_operation = [this]() { + m_async_job_cancel_requested = true; + if (m_current_job_data) { + m_current_job_data->state = EBooleanOperationState::Cancelling; + } + }; + } GLGizmoMeshBoolean::~GLGizmoMeshBoolean() @@ -1787,6 +1964,15 @@ void GLGizmoMeshBoolean::on_set_state() } else if (m_state == EState::Off) { //NOTE: check out + + // Cancel any running async job when exiting gizmo + if (m_async_job_running) { + m_async_job_cancel_requested = true; + if (m_current_job_data) { + m_current_job_data->state = EBooleanOperationState::Cancelling; + } + } + restore_list_color_overrides(); m_volume_manager.clear_all(); m_target_mode = BooleanTargetMode::Unknown; @@ -1839,6 +2025,13 @@ void GLGizmoMeshBoolean::on_render_input_window(float x, float y, float bottom_l return; } + // Safety check: if UI is not available, close the gizmo + if (!m_ui) { + restore_list_color_overrides(); + m_state = EState::Off; + return; + } + // Manage ImGui window lifecycle (Gizmo's responsibility) y = std::min(y, bottom_limit - ImGui::GetWindowHeight()); @@ -1934,11 +2127,7 @@ void GLGizmoMeshBoolean::init_volume_manager(){ else m_volume_manager.init_object_mode_lists(selection); } -void GLGizmoMeshBoolean::apply_color_overrides_for_mode(MeshBooleanOperation mode) -{ - if (mode == MeshBooleanOperation::Difference) apply_a_b_list_color_overrides(mode); - else apply_working_list_color_overrides(mode); -} + void GLGizmoMeshBoolean::on_change_color_mode(bool is_dark) { @@ -1948,15 +2137,6 @@ void GLGizmoMeshBoolean::on_change_color_mode(bool is_dark) // Force redraw to update col = trueors immediately set_dirty(); refresh_canvas(); - - //TODO: Rescale when dpi changed - // Recreate UI icons when scale or theme may change to keep crisp - if (m_ui) { - // allow reload at new scale - // m_ui->reload_icons_if_scale_changed(); // future hook - // For now, just mark not loaded so next on_init()/open will reload - // (safe because load_icons is idempotent) - } } void GLGizmoMeshBoolean::on_load(cereal::BinaryInputArchive &ar) @@ -1969,6 +2149,797 @@ void GLGizmoMeshBoolean::on_save(cereal::BinaryOutputArchive &ar) const ar(m_enable, m_operation_mode, m_target_mode, m_keep_original_models, m_entity_only); } +// ======================================================================================== +// GLGizmoMeshBoolean ASYNC JOB IMPLEMENTATION +// Only compiled when USE_ASYNC_BOOLEAN_MODE = 1 +// ======================================================================================== +#if USE_ASYNC_BOOLEAN_MODE + +BooleanJobData::VolumeData GLGizmoMeshBoolean::prepare_volume_data( + const BooleanOperationEngine::VolumeInfo& vol_info) const +{ + BooleanJobData::VolumeData data; + + // Deep copy mesh data (critical for thread safety) + data.mesh = vol_info.model_volume->mesh(); + data.mesh.transform(vol_info.transformation, true); // Pre-apply transformation + + // Copy transformation matrix + data.transformation = vol_info.transformation; + + // Save IDs for later locating the original volumes + data.volume_id = vol_info.model_volume->id(); + data.object_id = vol_info.model_volume->get_object()->id(); + data.object_index = vol_info.object_index; + data.name = vol_info.model_volume->name; + + return data; +} + +BooleanJobData GLGizmoMeshBoolean::prepare_job_data() +{ + BooleanJobData job_data; + const Selection& selection = m_parent.get_selection(); + + // Prepare settings + job_data.operation_mode = m_operation_mode; + job_data.settings.keep_original_models = m_keep_original_models; + job_data.settings.entity_only = m_entity_only; + job_data.settings.target_mode = m_target_mode; + + // Collect non-model volumes for Object mode + // Only collect from objects that will be deleted based on keep_original setting + if (job_data.settings.entity_only && job_data.settings.target_mode == BooleanTargetMode::Object) { + auto collect_non_models = [&](const std::vector& vec, bool is_b = false) { + for (unsigned int idx : vec) { + const GLVolume* glv = selection.get_volume(idx); + if (!glv) continue; + ModelVolume* mv = get_model_volume(*glv, selection.get_model()->objects); + if (!mv) continue; + if (!mv->is_model_part()) { + // Skip B group volumes in Object mode (they're never attached to result) + if (is_b && m_target_mode == BooleanTargetMode::Object) continue; + else job_data.settings.non_model_volumes_to_attach.push_back(mv); + } + } + }; + + if (m_operation_mode == MeshBooleanOperation::Difference) { + // Difference mode: A objects are always deleted, so collect their non-model volumes + collect_non_models(m_volume_manager.get_list_a()); + // B objects are only deleted if keep_original=false + // Note: B non-model volumes are never collected in Object mode (handled by is_b flag) + collect_non_models(m_volume_manager.get_list_b(), true); + } else { + // Union/Intersection: Only collect if objects will be deleted (keep_original=false) + if (!job_data.settings.keep_original_models) { + collect_non_models(m_volume_manager.get_working_list()); + } + } + } + + // Prepare volume data (deep copy for thread safety) + // Helper: Filter volumes - remove empty meshes and non-model-parts if entity_only + auto filter_volumes = [&](std::vector& vols) { + vols.erase(std::remove_if(vols.begin(), vols.end(), + [&](const BooleanOperationEngine::VolumeInfo& info) { + if (!info.model_volume || info.model_volume->mesh().empty()) + return true; + if (job_data.settings.entity_only && !info.model_volume->is_model_part()) + return true; + return false; + }), vols.end()); + }; + + // Helper: Collect unique object IDs from volumes (for Object mode deletion) + auto collect_object_ids = [&](const std::vector& vols, std::set& object_ids) { + for (const auto& vol : vols) { + if (vol.model_volume) { + ModelObject* obj = vol.model_volume->get_object(); + if (obj) { + object_ids.insert(obj->id()); + } + } + } + }; + + if (m_operation_mode == MeshBooleanOperation::Difference) { + // Difference mode: prepare A and B groups + auto volumes_a = m_boolean_engine.prepare_volumes( + m_volume_manager.get_list_a(), selection); + auto volumes_b = m_boolean_engine.prepare_volumes( + m_volume_manager.get_list_b(), selection); + + filter_volumes(volumes_a); + filter_volumes(volumes_b); + + for (const auto& vol : volumes_a) { + job_data.volumes_a.push_back(prepare_volume_data(vol)); + } + for (const auto& vol : volumes_b) { + job_data.volumes_b.push_back(prepare_volume_data(vol)); + } + + job_data.total_steps = volumes_a.size() * volumes_b.size(); + } else { + // Union/Intersection mode: only use A group + auto volumes = m_boolean_engine.prepare_volumes( + m_volume_manager.get_working_list(), selection); + + filter_volumes(volumes); + + for (const auto& vol : volumes) { + job_data.volumes_a.push_back(prepare_volume_data(vol)); + } + + job_data.total_steps = volumes.size(); + } + + return job_data; +} + +// Helper: Group volumes by object and union within each object (for Object mode) +std::vector GLGizmoMeshBoolean::group_and_union_by_object( + const std::vector& volumes, + std::function cancel_cb) +{ + // Group volumes by object_index + std::map> volumes_by_object; + for (size_t i = 0; i < volumes.size(); ++i) { + volumes_by_object[volumes[i].object_index].push_back(i); + } + + std::vector per_object_meshes; + per_object_meshes.reserve(volumes_by_object.size()); + + // Union volumes within each object + for (const auto& [obj_idx, vol_indices] : volumes_by_object) { + if (cancel_cb && cancel_cb()) { + return {}; // Canceled + } + + if (vol_indices.size() == 1) { + // Single volume - no union needed + per_object_meshes.push_back(volumes[vol_indices[0]].mesh); + } else { + // Multiple volumes - union them + TriangleMesh obj_merged = volumes[vol_indices[0]].mesh; + for (size_t i = 1; i < vol_indices.size(); ++i) { + std::vector temp_result; + Slic3r::MeshBoolean::mcut::make_boolean( + obj_merged, volumes[vol_indices[i]].mesh, temp_result, + MeshBooleanConfig::OP_UNION, cancel_cb, nullptr); + + if (temp_result.empty()) { + return {}; // Failed + } + obj_merged = std::move(temp_result[0]); + + // Cleanup after each internal union + its_remove_degenerate_faces(obj_merged.its); + its_merge_vertices(obj_merged.its, true); + } + per_object_meshes.push_back(std::move(obj_merged)); + } + } + + return per_object_meshes; +} + +// Helper: Prepare meshes for operation (group by object if in Object mode, otherwise extract directly) +std::vector GLGizmoMeshBoolean::prepare_meshes_for_operation( + const std::vector& volumes, + BooleanTargetMode target_mode, + std::function cancel_cb) +{ + if (target_mode == BooleanTargetMode::Object) { + // Object mode: Group by object and union within each object + return group_and_union_by_object(volumes, cancel_cb); + } else { + // Part mode: Extract meshes directly + std::vector meshes; + meshes.reserve(volumes.size()); + for (const auto& vol : volumes) { + meshes.push_back(vol.mesh); + } + return meshes; + } +} + +// Helper: Accumulate boolean operations on a list of meshes (for Union/Intersection) +std::optional GLGizmoMeshBoolean::accumulate_boolean_operations( + const std::vector& meshes, + const std::string& operation, + BooleanJobData& data, + JobNew::Ctl& ctl, + BooleanOperationResult& result) +{ + if (meshes.empty()) { + result.error_message = MeshBooleanWarnings::PREPAREING; + return std::nullopt; + } + + // Setup progress tracking + data.total_steps = static_cast(meshes.size() - 1); + data.current_step = 0; + + // Use the shared core logic from BooleanOperationEngine + auto opt_result = m_boolean_engine.execute_boolean_on_meshes_async( + meshes, operation, data.cancel_cb, data.progress_cb); + + // Check result and set appropriate error message + if (!opt_result.has_value()) { + // Check if cancelled + if (ctl.was_canceled() || (data.cancel_cb && data.cancel_cb())) { + result.error_message = MeshBooleanWarnings::JOB_CANCELED; + } else { + // Set error based on operation type + if (operation == MeshBooleanConfig::OP_INTERSECTION) { + result.error_message = MeshBooleanWarnings::OVERLAPING; + } else { + result.error_message = MeshBooleanWarnings::JOB_FAILED; + } + } + return std::nullopt; + } + + // Update current step to completion + data.current_step = static_cast(meshes.size() - 1); + + return opt_result; +} + +BooleanOperationResult GLGizmoMeshBoolean::perform_boolean_operation_async( + BooleanJobData& data, JobNew::Ctl& ctl) +{ + BooleanOperationResult result; + result.success = false; + + // Execute based on operation type + switch (data.operation_mode) { + case MeshBooleanOperation::Union: + case MeshBooleanOperation::Intersection: { + // Validate minimum volumes + if (data.volumes_a.size() < 2) { + result.error_message = (data.operation_mode == MeshBooleanOperation::Union) ? + MeshBooleanWarnings::MIN_VOLUMES_UNION : MeshBooleanWarnings::MIN_VOLUMES_INTERSECTION; + return result; + } + + // Prepare meshes (handles Object/Part mode internally) + std::vector meshes = prepare_meshes_for_operation( + data.volumes_a, data.settings.target_mode, data.cancel_cb); + + if (meshes.empty()) { + result.error_message = MeshBooleanWarnings::JOB_CANCELED; + return result; + } + + // For Object mode with single object, union result is ready; intersection needs 2+ + if (data.settings.target_mode == BooleanTargetMode::Object && meshes.size() == 1) { + if (data.operation_mode == MeshBooleanOperation::Union) { + result.result_meshes.push_back(meshes[0]); + result.success = true; + break; + } else { + result.error_message = MeshBooleanWarnings::MIN_OBJECTS_INTERSECTION; + return result; + } + } + + // Execute operation + const std::string& operation = (data.operation_mode == MeshBooleanOperation::Union) ? + MeshBooleanConfig::OP_UNION : MeshBooleanConfig::OP_INTERSECTION; + + auto opt_result = accumulate_boolean_operations(meshes, operation, data, ctl, result); + + if (opt_result.has_value()) { + result.result_meshes.push_back(*opt_result); + result.success = true; + } + break; + } + + case MeshBooleanOperation::Difference: { + // A - B operation + if (data.volumes_a.empty() || data.volumes_b.empty()) { + result.error_message = MeshBooleanWarnings::MIN_VOLUMES_DIFFERENCE; + return result; + } + + // Object mode uses different algorithm than Part mode + // Object: A_i - (B1 ¡È B2 ¡È ... ¡È Bn) for each A_i + // Part: A_i - B1 - B2 - ... - Bn for each A_i + if (data.settings.target_mode == BooleanTargetMode::Object) { + // ===== OBJECT MODE: Same strategy as synchronous version (line 837-910) ===== + // Progress allocation: + // - Phase 1 (Preparing B union): 0% ¡ú 30% (or 0% ¡ú 50% if many B objects) + // - Phase 2 (A - B operations): 30%/50% ¡ú 100% + + const size_t b_union_ops = data.volumes_b.size() - 1; // Number of union operations + const size_t a_diff_ops = data.volumes_a.size(); // Number of difference operations + const size_t total_ops = b_union_ops + a_diff_ops; + + // Weight: B union phase gets proportional share based on operation count + const float b_union_weight = total_ops > 0 ? float(b_union_ops) / float(total_ops) : 0.0f; + const float a_diff_weight = 1.0f - b_union_weight; + + // Build unified B mesh by unioning all B volumes first + TriangleMesh b_union; + bool b_union_init = false; + size_t b_idx = 0; + + // Union all B volumes into one mesh (with progress reporting) + for (const auto& vol_b : data.volumes_b) { + // Check cancellation + if (data.cancel_cb && data.cancel_cb()) { + result.error_message = MeshBooleanWarnings::JOB_CANCELED; + return result; + } + + TriangleMesh b_mesh = vol_b.mesh; + if (!b_union_init) { + b_union = b_mesh; + b_union_init = true; + } else { + // Create progress callback for this B union operation + auto b_progress_cb = data.progress_cb ? [&data, b_union_weight, b_union_ops, b_idx](float sub_progress) { + if (data.progress_cb) { + // Map sub_progress (0-1) to B union phase progress + float clamped_sub = std::min(1.0f, std::max(0.0f, sub_progress)); + float b_phase_progress = (float(b_idx - 1) + clamped_sub) / float(b_union_ops); + float total_progress = b_phase_progress * b_union_weight; + total_progress = std::min(1.0f, std::max(0.0f, total_progress)); + data.progress_cb(total_progress); + } + } : std::function(); + + // Union this B volume with accumulated B union + std::vector union_meshes = {b_union, b_mesh}; + auto opt_union = m_boolean_engine.execute_boolean_on_meshes_async( + union_meshes, MeshBooleanConfig::OP_UNION, data.cancel_cb, b_progress_cb); + + if (!opt_union.has_value()) { + result.error_message = MeshBooleanWarnings::GROUPING; + return result; + } + b_union = *opt_union; + } + b_idx++; + } + + if (!b_union_init) { + result.error_message = MeshBooleanWarnings::MIN_VOLUMES_DIFFERENCE; + return result; + } + + // Initialize progress tracking for A - B phase + data.total_steps = static_cast(data.volumes_a.size()); + data.current_step = 0; + + // Process each A volume: subtract the unified B mesh + for (size_t a_idx = 0; a_idx < data.volumes_a.size(); ++a_idx) { + // Check for cancellation + if (data.cancel_cb && data.cancel_cb()) { + result.error_message = MeshBooleanWarnings::JOB_CANCELED; + return result; + } + + // Calculate progress callback for this A volume + // Map to the remaining progress range: [b_union_weight, 1.0] + const size_t total_a_volumes = data.volumes_a.size(); + auto progress_cb_for_a = data.progress_cb ? [&data, b_union_weight, a_diff_weight, total_a_volumes, a_idx](float sub_progress) { + if (data.progress_cb) { + // Map sub-progress (0-1) to this A volume's progress within A - B phase + float clamped_sub = std::min(1.0f, std::max(0.0f, sub_progress)); + float a_phase_progress = (float(a_idx) + clamped_sub) / float(total_a_volumes); + // Map to total progress: start from b_union_weight, span a_diff_weight + float total_progress = b_union_weight + (a_phase_progress * a_diff_weight); + total_progress = std::min(1.0f, std::max(0.0f, total_progress)); + data.progress_cb(total_progress); + } + } : std::function(); + + // Subtract unified B mesh from this A volume: A_i - B_union + std::vector diff_meshes = {data.volumes_a[a_idx].mesh, b_union}; + auto opt_diff = m_boolean_engine.execute_boolean_on_meshes_async( + diff_meshes, MeshBooleanConfig::OP_DIFFERENCE, data.cancel_cb, progress_cb_for_a); + + if (!opt_diff.has_value()) { + // Check if cancelled + if (data.cancel_cb && data.cancel_cb()) { + result.error_message = MeshBooleanWarnings::JOB_CANCELED; + } else { + result.error_message = MeshBooleanWarnings::JOB_FAILED; + } + return result; + } + + TriangleMesh result_mesh = *opt_diff; + if (!result_mesh.empty()) { + result.result_meshes.push_back(result_mesh); + result.source_transforms.push_back(data.volumes_a[a_idx].transformation); + } + + // Update step counter + data.current_step = static_cast(a_idx + 1); + } + + result.success = !result.result_meshes.empty(); + break; + } + + // ===== PART MODE: Original implementation ===== + // OPTIMIZATION: Sort A volumes by face count (small to large) + // Processing smaller A meshes first reduces overall computation time + std::sort(data.volumes_a.begin(), data.volumes_a.end(), + [](const BooleanJobData::VolumeData& a, const BooleanJobData::VolumeData& b) { + return a.mesh.its.indices.size() < b.mesh.its.indices.size(); + }); + + // Initialize progress tracking + data.total_steps = static_cast(data.volumes_a.size()); + data.current_step = 0; + + // Process each A volume: A_i = A_i - ALL_B_volumes (using shared core logic) + for (size_t a_idx = 0; a_idx < data.volumes_a.size(); ++a_idx) { + // Check for cancellation + if (data.cancel_cb && data.cancel_cb()) { + result.error_message = MeshBooleanWarnings::JOB_CANCELED; + return result; + } + + // Build mesh list: [A_i, B1, B2, ..., Bn] + std::vector meshes_for_subtraction; + meshes_for_subtraction.push_back(data.volumes_a[a_idx].mesh); + for (const auto& vol_b : data.volumes_b) { + meshes_for_subtraction.push_back(vol_b.mesh); + } + + // Calculate progress callback for this A volume + const size_t total_a_volumes = data.volumes_a.size(); + auto progress_cb_for_a = data.progress_cb ? [&data, total_a_volumes, a_idx](float sub_progress) { + if (data.progress_cb) { + // Map sub-progress (0-1) to overall progress + // Clamp sub_progress to [0, 1] range (defensive programming) + float clamped_sub = std::min(1.0f, std::max(0.0f, sub_progress)); + float total_progress = (float(a_idx) + clamped_sub) / float(total_a_volumes); + // Ensure total_progress is within [0, 1] + total_progress = std::min(1.0f, std::max(0.0f, total_progress)); + data.progress_cb(total_progress); + } + } : std::function(); + + // Use the shared core logic from BooleanOperationEngine + auto opt_result = m_boolean_engine.execute_boolean_on_meshes_async( + meshes_for_subtraction, + MeshBooleanConfig::OP_DIFFERENCE, + data.cancel_cb, + progress_cb_for_a); + + // Check result + if (!opt_result.has_value()) { + // Check if cancelled + if (ctl.was_canceled() || (data.cancel_cb && data.cancel_cb())) { + result.error_message = MeshBooleanWarnings::JOB_CANCELED; + } else { + result.error_message = MeshBooleanWarnings::JOB_FAILED; + } + return result; + } + + TriangleMesh accumulated_mesh = *opt_result; + if (!accumulated_mesh.empty()) { + result.result_meshes.push_back(accumulated_mesh); + result.source_transforms.push_back(data.volumes_a[a_idx].transformation); + } + + // Update step counter + data.current_step = static_cast(a_idx + 1); + } + + result.success = !result.result_meshes.empty(); + break; + } + + default: + result.error_message = MeshBooleanWarnings::JOB_FAILED; + return result; + } + + return result; +} + +void GLGizmoMeshBoolean::apply_boolean_result_from_job(const BooleanJobData& job_data) +{ + if (!job_data.result.success || job_data.result.result_meshes.empty()) { + return; + } + + const Selection& selection = m_parent.get_selection(); + Model* model = selection.get_model(); + if (!model || model->objects.empty()) return; + + // Suppress automatic snapshots during result application + // The snapshot was already taken at the start of execute_mesh_boolean + // Without this, each volume/object creation/deletion would trigger unwanted snapshots + Plater::SuppressSnapshots suppress(wxGetApp().plater()); + + // Helper: Find volume by ID in a specific object + auto find_volume_in_object = [](ModelObject* obj, const ObjectID& vol_id) -> ModelVolume* { + for (auto* vol : obj->volumes) { + if (vol->id() == vol_id) return vol; + } + return nullptr; + }; + + // Helper: Find object containing a volume with given ID + auto find_object_by_volume_id = [&](const ObjectID& vol_id) -> ModelObject* { + for (auto* obj : model->objects) { + if (find_volume_in_object(obj, vol_id)) return obj; + } + return nullptr; + }; + + // Part mode: Apply results to the same object + if (job_data.settings.target_mode == BooleanTargetMode::Part) { + // Check volumes_a is not empty before accessing [0] + if (job_data.volumes_a.empty()) { + m_warning_manager.add_error(MeshBooleanWarnings::PREPAREING); + BOOST_LOG_TRIVIAL(error) << "[Mesh Boolean] Async boolean completion: volumes_a is empty"; + return; + } + + // Find target ModelObject by volume_id + ModelObject* target_object = find_object_by_volume_id(job_data.volumes_a[0].volume_id); + + if (!target_object) { + m_warning_manager.add_error(MeshBooleanWarnings::PREPAREING); + BOOST_LOG_TRIVIAL(error) << "[Mesh Boolean] Async boolean completion: target_object not found"; + return; + } + + // Collect all source volumes that will be replaced + std::vector sources_to_replace; + + // Create result volumes + for (size_t i = 0; i < job_data.result.result_meshes.size(); ++i) { + // Find source volume + ModelVolume* source_volume = nullptr; + if (i < job_data.volumes_a.size()) { + source_volume = find_volume_in_object(target_object, job_data.volumes_a[i].volume_id); + } + + if (!source_volume) continue; + + // Create new result volume + ModelVolume* new_volume = m_boolean_engine.create_result_volume( + target_object, job_data.result.result_meshes[i], source_volume); + + if (!new_volume) continue; + + // Force result to be MODEL_PART (same as sync version) + new_volume->set_type(ModelVolumeType::MODEL_PART); + + // Determine if source should be replaced based on operation mode + bool should_replace = false; + if (job_data.operation_mode == MeshBooleanOperation::Difference) { + // Difference: ALWAYS replace A volumes (even if keeping originals) + should_replace = true; + } else { + // Union/Intersection: Replace only if not keeping originals + should_replace = !job_data.settings.keep_original_models; + } + + if (should_replace && job_data.settings.entity_only && !source_volume->is_model_part()) { + should_replace = false; // Don't replace non-model-parts in entity_only mode + } + + if (should_replace) { + sources_to_replace.push_back(source_volume); + } + } + + // Delete source volumes that need to be replaced + // Collect indices first and delete from back to front to avoid iterator invalidation + if (!sources_to_replace.empty()) { + std::vector indices_to_delete; + auto& volumes = target_object->volumes; + + for (ModelVolume* src : sources_to_replace) { + auto it = std::find(volumes.begin(), volumes.end(), src); + if (it != volumes.end()) { + indices_to_delete.push_back(std::distance(volumes.begin(), it)); + } + } + + // Sort indices in descending order and delete from back to front + std::sort(indices_to_delete.begin(), indices_to_delete.end(), std::greater()); + for (size_t idx : indices_to_delete) { + if (idx < target_object->volumes.size()) { + target_object->delete_volume(idx); + } + } + } + + // Delete other participating volumes based on mode + std::vector volumes_to_delete; + + if (job_data.operation_mode == MeshBooleanOperation::Difference) { + // Difference: Delete B volumes if not keeping originals + if (!job_data.settings.keep_original_models) { + for (const auto& vol_data : job_data.volumes_b) { + ModelVolume* vol = find_volume_in_object(target_object, vol_data.volume_id); + if (vol && !(job_data.settings.entity_only && !vol->is_model_part())) { + volumes_to_delete.push_back(vol); + } + } + } + } else { + // Union/Intersection: Delete all other A volumes if not keeping originals + if (!job_data.settings.keep_original_models) { + for (size_t i = 1; i < job_data.volumes_a.size(); ++i) { + ModelVolume* vol = find_volume_in_object(target_object, job_data.volumes_a[i].volume_id); + if (vol && !(job_data.settings.entity_only && !vol->is_model_part())) { + volumes_to_delete.push_back(vol); + } + } + } + } + + // Delete collected volumes + if (!volumes_to_delete.empty()) { + m_boolean_engine.delete_volumes_from_model(volumes_to_delete); + } + + target_object->invalidate_bounding_box(); + + // Ensure the result is on the build plate (same as sync version line 1328) + target_object->ensure_on_bed(); + + // Update UI + auto* obj_list = wxGetApp().obj_list(); + if (obj_list && target_object) { + auto& objects = *obj_list->objects(); + if (auto it = std::find(objects.begin(), objects.end(), target_object); + it != objects.end()) { + int obj_idx = int(it - objects.begin()); + BOOST_LOG_TRIVIAL(info) << "[Mesh Boolean] Boolean Part mode: Refreshing sidebar for object " << obj_idx + << ", volumes count: " << target_object->volumes.size(); + + // Use the same refresh sequence as synchronous version + obj_list->update_info_items(obj_idx); + obj_list->reorder_volumes_and_get_selection(obj_idx); + obj_list->changed_object(obj_idx); + } + } + } + // Object mode: Create ONE new result object (same as sync version) + else if (job_data.settings.target_mode == BooleanTargetMode::Object) { + // Find first source object to get Model reference + ModelObject* first_source_obj = !job_data.volumes_a.empty() ? + find_object_by_volume_id(job_data.volumes_a[0].volume_id) : nullptr; + + if (!first_source_obj) { + m_warning_manager.add_error(MeshBooleanWarnings::PREPAREING); + BOOST_LOG_TRIVIAL(error) << "[Mesh Boolean] Async boolean completion (Object mode): first_source_obj not found"; + return; + } + + // Create single new object (same as sync version) + ModelObject* new_obj = model->add_object(); + new_obj->name = first_source_obj->name + "_Boolean"; + new_obj->config.assign_config(first_source_obj->config); + + // Copy instance + if (!first_source_obj->instances.empty() && first_source_obj->instances[0]) { + new_obj->add_instance(*first_source_obj->instances[0]); + } else { + new_obj->add_instance(); + } + + // Initialize assemble transformation + if (!new_obj->instances.empty() && new_obj->instances[0] && + !new_obj->instances[0]->is_assemble_initialized()) { + new_obj->instances[0]->set_assemble_transformation( + new_obj->instances[0]->get_transformation()); + } + + // Add ALL result meshes to this single object (same as sync version) + for (size_t i = 0; i < job_data.result.result_meshes.size(); ++i) { + // Find source volume + ModelVolume* source_volume = nullptr; + if (i < job_data.volumes_a.size()) { + ModelObject* src_obj = find_object_by_volume_id(job_data.volumes_a[i].volume_id); + source_volume = src_obj ? + find_volume_in_object(src_obj, job_data.volumes_a[i].volume_id) : nullptr; + } + + if (!source_volume) continue; + + // Create result volume + ModelVolume* new_volume = m_boolean_engine.create_result_volume( + new_obj, job_data.result.result_meshes[i], source_volume); + + if (new_volume) { + // Force result to be MODEL_PART + new_volume->set_type(ModelVolumeType::MODEL_PART); + } + } + + // Attach non-model volumes BEFORE deletion (same as sync version line 1256) + attach_ignored_non_models_to_target(new_obj, job_data.settings); + + // Collect source objects to delete (same logic as sync version line 1265-1301) + auto& objects = *wxGetApp().obj_list()->objects(); + std::set obj_indices_to_delete; + + // Helper: Collect object index by volume ID + auto add_obj_idx_by_volume = [&](const ObjectID& vol_id) { + ModelObject* obj = find_object_by_volume_id(vol_id); + if (obj && obj != new_obj) { // Don't delete the new object we just created + auto it = std::find(objects.begin(), objects.end(), obj); + if (it != objects.end()) { + obj_indices_to_delete.insert(static_cast(it - objects.begin())); + } + } + }; + + if (job_data.operation_mode == MeshBooleanOperation::Difference) { + // Difference: Always delete A group objects + for (const auto& vol_data : job_data.volumes_a) { + add_obj_idx_by_volume(vol_data.volume_id); + } + + // Delete B group objects only if not keeping originals + if (!job_data.settings.keep_original_models) { + for (const auto& vol_data : job_data.volumes_b) { + add_obj_idx_by_volume(vol_data.volume_id); + } + } + } else { + // Union/Intersection: Delete all participating objects only if not keeping originals + if (!job_data.settings.keep_original_models) { + for (const auto& vol_data : job_data.volumes_a) { + add_obj_idx_by_volume(vol_data.volume_id); + } + } + } + + // Delete source objects + if (!obj_indices_to_delete.empty()) { + std::vector items; + for (int idx : obj_indices_to_delete) { + items.emplace_back(ItemType::itObject, idx, -1); + } + wxGetApp().obj_list()->delete_from_model_and_list(items); + } + + // Ensure the new object is on the build plate (missing in original async implementation) + new_obj->ensure_on_bed(); + + // Add new object to sidebar and update UI (same as sync version line 1165-1173) + auto* obj_list = wxGetApp().obj_list(); + if (obj_list && new_obj) { + auto& objects_list = *obj_list->objects(); + auto it = std::find(objects_list.begin(), objects_list.end(), new_obj); + if (it != objects_list.end()) { + int obj_idx = int(it - objects_list.begin()); + obj_list->add_object_to_list(obj_idx); + // Don't select the new object to avoid triggering mode re-detection + // which could cause gizmo to degrade to Part mode if result has multiple volumes + // obj_list->select_item(obj_list->GetModel()->GetItemById(obj_idx)); + obj_list->update_info_items(obj_idx); + } + } + } + + // Update plater to refresh object positions and collision detection (same as sync version line 1329) + wxGetApp().plater()->update(); +} + +#endif // USE_ASYNC_BOOLEAN_MODE + // ========================== BOOLEAN OPERATION EXECUTION ========================== void GLGizmoMeshBoolean::execute_mesh_boolean() @@ -2030,6 +3001,8 @@ void GLGizmoMeshBoolean::execute_mesh_boolean() init_volume_manager(); } +#if !USE_ASYNC_BOOLEAN_MODE + // ==== SYNCHRONOUS EXECUTION MODE ==== auto perform_current_operation = [&](MeshBooleanOperation mode) { ModelObject* current_model_object = m_c->selection_info() ? m_c->selection_info()->model_object() : nullptr; int current_selected_index = m_parent.get_selection().get_object_idx(); @@ -2047,15 +3020,15 @@ void GLGizmoMeshBoolean::execute_mesh_boolean() break; default: result.success = false; - result.error_message = "Unknown operation type"; + result.error_message = MeshBooleanWarnings::JOB_FAILED; break; } if (result.success) { // For Part mode: clear color overrides before applying result (which may delete volumes) // to avoid index mismatch after volume deletion - if (settings.target_mode == BooleanTargetMode::Part && m_color_overrides.applied) { - m_color_overrides.clear(); + if (settings.target_mode == BooleanTargetMode::Part) { + restore_list_color_overrides(); } // Collect unique ModelObject* from volume indices @@ -2103,8 +3076,177 @@ void GLGizmoMeshBoolean::execute_mesh_boolean() } }; - // Execute boolean operation synchronously + // Synchronous execution call perform_current_operation(m_operation_mode); +#else + // ==== ASYNCHRONOUS EXECUTION MODE ==== + // Prepare Job data (deep copy all data in UI thread) + BooleanJobData job_data = prepare_job_data(); + + // Validate after filtering (same as sync mode) + if (m_operation_mode == MeshBooleanOperation::Difference) { + if (job_data.volumes_a.empty() || job_data.volumes_b.empty()) { + m_warning_manager.add_warning(MeshBooleanWarnings::MIN_VOLUMES_DIFFERENCE); + return; + } + } else if (job_data.volumes_a.size() < 2) { + m_warning_manager.add_warning(m_operation_mode == MeshBooleanOperation::Union ? + MeshBooleanWarnings::MIN_VOLUMES_UNION : MeshBooleanWarnings::MIN_VOLUMES_INTERSECTION); + return; + } + + // Store job data for progress tracking + m_current_job_data = std::make_shared(job_data); + m_async_job_running = true; + m_async_job_cancel_requested = false; + + // Initialize progress to 0 + { + std::lock_guard lk(m_async_mutex); + m_async_boolean_operation_progress = 0.0f; + } + + // Create async Job + auto boolean_job = std::make_unique>(); + + // Set process callback (runs in background thread) + boolean_job->set_process_callback( + [this](JobNew::Ctl& ctl, BooleanJobData& data) { + data.state = EBooleanOperationState::Running; + + try { + // Execute boolean operation (uses deep copied mesh data) + data.result = perform_boolean_operation_async(data, ctl); + + // Check for cancellation + if (data.cancel_cb && data.cancel_cb()) { + data.state = EBooleanOperationState::Canceled; + data.result.success = false; + data.result.error_message = MeshBooleanWarnings::JOB_CANCELED; + return; + } + + data.state = data.result.success ? + EBooleanOperationState::Finished : + EBooleanOperationState::Failed; + + } catch (const std::exception& e) { + data.state = EBooleanOperationState::Failed; + data.result.success = false; + data.result.error_message = std::string("Exception: ") + e.what(); + + if (data.failed_cb) { + data.failed_cb(); + } + } + } + ); + + // Set finalize callback (runs in UI thread) + boolean_job->set_finalize_callback( + [this](BooleanJobData& data) { + // Update shared job data with final state + if (m_current_job_data) { + m_current_job_data->state = data.state; + m_current_job_data->current_step = data.current_step; + m_current_job_data->result = data.result; + } + + // If gizmo was closed while job was running, skip result application + // Only clean up job state and exit + if (m_state == EState::Off) { + m_async_job_running = false; + m_async_job_cancel_requested = false; + m_current_job_data.reset(); + return; + } + + if (data.state == EBooleanOperationState::Finished && data.result.success) { + // Success: apply result to model + apply_boolean_result_from_job(data); + + // Clear warnings + m_warning_manager.clear_mode_specific_warnings(m_operation_mode); + + // Clear volume manager and rebuild if needed (same as sync version line 2986-2991) + m_volume_manager.clear_all(); + if (check_if_active()) { + // For Part mode: restore color and reinit (same as sync) + if (data.settings.target_mode == BooleanTargetMode::Part) { + restore_list_color_overrides(); + init_volume_manager(); + apply_color_overrides_for_mode(m_operation_mode); + } + // For Object mode: clear global selection to prevent mode degradation + // After deleting A objects, Selection may auto-select remaining B objects + // If B is a single object with multiple volumes, this would trigger Part mode + else { + restore_list_color_overrides(); + m_parent.get_selection().clear(); + } + } + + } else if (data.state == EBooleanOperationState::Canceled) { + // Canceled: show cancel message + m_warning_manager.add_warning(MeshBooleanWarnings::JOB_CANCELED); + + } else if (data.state == EBooleanOperationState::Failed) { + // Failed: show error or warning depending on the message type + std::string message = data.result.error_message.empty() ? + MeshBooleanWarnings::JOB_FAILED : data.result.error_message; + + // Only JOB_FAILED should be red Error, all others are orange Warning + if (message == MeshBooleanWarnings::JOB_FAILED) { + m_warning_manager.add_error(message); + } else { + m_warning_manager.add_warning(message, WarningSeverity::Warning); + } + } + + // Clean up job state + m_async_job_running = false; + m_async_job_cancel_requested = false; + m_current_job_data.reset(); + + // Refresh UI + refresh_canvas(); + } + ); + + // Set cancel callback + boolean_job->set_cancel_callback( + [this](JobNew::Ctl& ctl, BooleanJobData& data) -> bool { + // Check if cancellation was requested from UI + return ctl.was_canceled() || m_async_job_cancel_requested; + } + ); + + // Set progress callback + boolean_job->set_progress_callback( + [this](float progress) { + // Check if gizmo is still active + if (!check_if_active()) { + return; + } + // Thread-safe update of progress percentage (0-100) + { + std::lock_guard lk(m_async_mutex); + // Convert 0-1 to 0-100 and clamp to valid range + float progress_percent = progress * 100.0f; + m_async_boolean_operation_progress = std::min(100.0f, std::max(0.0f, progress_percent)); + } + // Request UI update + set_dirty(); + m_parent.schedule_extra_frame(0); + } + ); + + // Set data and submit to worker queue + boolean_job->set_data(job_data); + + Worker& worker = wxGetApp().plater()->get_ui_job_worker(); + worker.push(std::move(boolean_job)); +#endif // USE_ASYNC_BOOLEAN_MODE } // Helper to convert ARGB (0xAARRGGBB) to RGBA floats [0,1] @@ -2130,12 +3272,12 @@ void GLGizmoMeshBoolean::apply_color_overrides_for_list(const std::vectorobjects); if (!mv) continue; if (mv->is_model_part()) - m_color_overrides.apply_for_indices(selection, {idx}, color_for_model_part); + m_parent.set_volume_color_override(idx, color_for_model_part); else - m_color_overrides.apply_for_indices(selection, {idx}, abgr_u32_to_rgba(MeshBooleanConfig::COLOR_NON_MODEL)); + m_parent.set_volume_color_override(idx, abgr_u32_to_rgba(MeshBooleanConfig::COLOR_NON_MODEL)); } } else { - m_color_overrides.apply_for_indices(selection, list, color_for_model_part); + m_parent.set_volumes_color_override(list, color_for_model_part); } } @@ -2147,6 +3289,15 @@ bool GLGizmoMeshBoolean::get_cur_entity_only() const return m_ui ? m_ui->get_entity_only() : m_entity_only; } +void GLGizmoMeshBoolean::apply_color_overrides_for_mode(MeshBooleanOperation mode) +{ + // Enable volume color override + m_parent.set_use_volume_color_override(true); + + if (mode == MeshBooleanOperation::Difference) apply_a_b_list_color_overrides(mode); + else apply_working_list_color_overrides(mode); +} + void GLGizmoMeshBoolean::apply_color_overrides_to_lists( const std::vector&, std::array>>& lists_and_colors, bool entity_only) @@ -2194,9 +3345,8 @@ void GLGizmoMeshBoolean::apply_working_list_color_overrides(MeshBooleanOperation void GLGizmoMeshBoolean::restore_list_color_overrides() { - if (!m_color_overrides.applied) return; - m_color_overrides.restore_non_selected_indices(m_parent.get_selection()); - m_parent.update_volumes_colors_by_extruder(); // wipe tower update here + // Clear all volume color overrides + m_parent.clear_all_volume_color_overrides(); refresh_canvas(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.hpp index 45a77ac..8a5eec4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.hpp @@ -4,6 +4,8 @@ #include "GLGizmoBase.hpp" #include "GLGizmosCommon.hpp" #include "libslic3r/Model.hpp" +#include "slic3r/GUI/Jobs/JobNew.hpp" +#include "slic3r/GUI/Jobs/BooleanOperationJob.hpp" #include "imgui/imgui.h" #include #include @@ -12,6 +14,7 @@ #include #include #include +#include namespace Slic3r { class GLVolume; // forward declaration in the correct namespace @@ -76,7 +79,7 @@ struct MeshBooleanConfig { // ========================== BOOLEAN OPERATIONS ========================== static constexpr const char* OP_UNION = "UNION"; static constexpr const char* OP_INTERSECTION = "INTERSECTION"; - static constexpr const char* OP_DIFFERENCE = "SUBSTRACTION"; + static constexpr const char* OP_DIFFERENCE = "A_NOT_B"; // ========================== UI COLORS ========================== static constexpr unsigned int COLOR_LIST_A = 0xFFFFCC75; // List A title (light blue) @@ -207,7 +210,46 @@ struct BooleanOperationResult { std::vector volumes_to_delete; }; +// ========================== ASYNC JOB DATA ========================== +// Note: EBooleanOperationState is defined in BooleanOperationJob.hpp + +struct BooleanJobData { + // Thread-safe volume data (deep copied) + struct VolumeData { + TriangleMesh mesh; // Deep copied mesh data + Transform3d transformation; // Transformation matrix + ObjectID volume_id; // For locating original volume + ObjectID object_id; // For locating object + int object_index; + std::string name; // For debugging + }; + + std::vector volumes_a; // Group A data + std::vector volumes_b; // Group B data (for difference) + + MeshBooleanOperation operation_mode; + BooleanOperationSettings settings; + + // Output result + BooleanOperationResult result; + + // State control + EBooleanOperationState state = EBooleanOperationState::NotStart; + + // Callbacks (set by Job automatically) + std::function cancel_cb; + std::function failed_cb; + std::function progress_cb; + + // Progress tracking + int current_step = 0; + int total_steps = 0; +}; + class BooleanOperationEngine { + // Allow GLGizmoMeshBoolean to access prepare_volumes for async operations + friend class GLGizmoMeshBoolean; + public: BooleanOperationEngine(); @@ -237,18 +279,28 @@ public: }; private: - // Volume processing helpers + // ========================== VOLUME PROCESSING HELPERS ========================== std::vector prepare_volumes(const std::vector& volume_indices, const Selection& selection) const; TriangleMesh get_transformed_mesh(const VolumeInfo& volume_info) const; TriangleMesh execute_boolean_operation(const TriangleMesh& mesh_a, const TriangleMesh& mesh_b, const std::string& operation_name) const; - // Core implementation methods + // ========================== SYNCHRONOUS IMPLEMENTATION ========================== + // Synchronous methods - only compiled when USE_ASYNC_BOOLEAN_MODE = 0 BooleanOperationResult part_level_boolean(const std::vector& volumes, const BooleanOperationSettings& settings, const std::string& operation, bool allow_single_volume = false) const; BooleanOperationResult part_level_sub(const std::vector& volumes_a, const std::vector& volumes_b, const BooleanOperationSettings& settings) const; std::optional execute_boolean_on_meshes( const std::vector& volumes, const std::string& operation) const; - // Model manipulation helpers + + // ========================== ASYNC CORE LOGIC ========================== + // Core boolean logic with CGAL fallback - used by async version + std::optional execute_boolean_on_meshes_async( + const std::vector& meshes, + const std::string& operation, + std::function cancel_cb = nullptr, + std::function progress_cb = nullptr) const; + + // ========================== MODEL MANIPULATION HELPERS ========================== ModelVolume* create_result_volume(ModelObject* target_object, const TriangleMesh& result_mesh, ModelVolume* source_volume); void delete_volumes_from_model(const std::vector& volumes_to_delete); void update_delete_list(BooleanOperationResult& result, const std::vector& volumes, const BooleanOperationSettings& settings) const; @@ -269,6 +321,7 @@ struct MeshBooleanWarnings { static const std::string MIN_OBJECTS_DIFFERENCE; static const std::string GROUPING; static const std::string OVERLAPING; + static const std::string PREPAREING; }; // ========================== WARNING SYSTEM ========================== @@ -346,30 +399,9 @@ protected: void on_save(cereal::BinaryOutputArchive &ar) const override; private: - struct SavedVolumeColorState { - std::array original_color; - bool original_force_native_color; - bool original_force_neutral_color; - }; - - // Helper to manage temporary volume color overrides during gizmo lifetime - struct ColorOverrideManager { - std::unordered_map saved; // key: global volume index - bool applied { false }; - - void apply_for_indices(const Selection &selection, - const std::vector& volume_indices, - const std::array& rgba); - - void restore_non_selected_indices(const Selection &selection); - void clear() { saved.clear(); applied = false; } - }; - - // ========================== CORE MODULES ========================== bool m_enable{ false }; BooleanWarningManager m_warning_manager; - ColorOverrideManager m_color_overrides; VolumeListManager m_volume_manager; BooleanOperationEngine m_boolean_engine; mutable int m_last_plate_idx_for_visibility {-1}; @@ -381,6 +413,31 @@ private: bool is_on_same_plate(const Selection& selection) const; BooleanTargetMode update_cur_mode(const Selection& selection) const; + // ========================== ASYNC JOB METHODS ========================== + BooleanJobData::VolumeData prepare_volume_data(const BooleanOperationEngine::VolumeInfo& vol_info) const; + BooleanJobData prepare_job_data(); + BooleanOperationResult perform_boolean_operation_async(BooleanJobData& data, JobNew::Ctl& ctl); + void apply_boolean_result_from_job(const BooleanJobData& job_data); + + // Helper: Group volumes by object and union within each object (for Object mode) + std::vector group_and_union_by_object( + const std::vector& volumes, + std::function cancel_cb); + + // Helper: Prepare meshes for operation (group by object if in Object mode, otherwise extract directly) + std::vector prepare_meshes_for_operation( + const std::vector& volumes, + BooleanTargetMode target_mode, + std::function cancel_cb); + + // Helper: Accumulate boolean operations on a list of meshes (for Union/Intersection) + std::optional accumulate_boolean_operations( + const std::vector& meshes, + const std::string& operation, + BooleanJobData& data, + JobNew::Ctl& ctl, + BooleanOperationResult& result); + // ========================== UI LAYER ========================== std::unique_ptr m_ui; MeshBooleanOperation m_operation_mode = MeshBooleanOperation::Union; @@ -389,6 +446,13 @@ private: bool m_entity_only = true; size_t m_last_snapshot_time = 0; + // ========================== ASYNC JOB TRACKING ========================== + std::shared_ptr m_current_job_data; // Shared data with running job + bool m_async_job_running = false; + bool m_async_job_cancel_requested = false; + float m_async_boolean_operation_progress = 0.0f; // Progress percentage (0-100) + mutable std::mutex m_async_mutex; // Mutex for thread-safe progress updates + // ================= VISUAL ELEMENTS ========================== // Helper methods to apply / restore color overrides for lists of volumes void apply_a_b_list_color_overrides(MeshBooleanOperation mode = MeshBooleanOperation::Difference); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp index 0d21735..9bbcb53 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp @@ -189,9 +189,6 @@ void GLGizmoMove3D::on_render() { Selection& selection = m_parent.get_selection(); if (selection.is_empty()) { return; } - glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); - glsafe(::glEnable(GL_DEPTH_TEST)); - const auto &[box, box_trafo] = selection.get_bounding_box_in_current_reference_system(); m_bounding_box = box; m_center = box_trafo.translation(); @@ -204,6 +201,139 @@ void GLGizmoMove3D::on_render() if (!p_ogl_manager) { return; } + glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); + glsafe(::glEnable(GL_DEPTH_TEST)); + if (m_object_manipulation) { + m_object_manipulation->set_dark_mode(m_is_dark_mode); + if (m_object_manipulation->m_align_type != GLGizmoAlignment::AlignType::NONE) { + if (!m_align_plane.is_initialized()) { + indexed_triangle_set its = its_make_xoy_center_rect(1.f, 1.f);//1.f + m_align_plane.init_from(its); + } + float scale = 1.1f; + // update m_align_plane pos and tran + Geometry::Transformation cur_tran; + auto box_size = m_bounding_box.size(); + switch (m_object_manipulation->m_align_type) { + case GLGizmoAlignment::AlignType::CENTER_X: { + cur_tran.set_scaling_factor(Vec3d(box_size[2] * scale, box_size[1] * scale, 1)); + auto offset = m_orient_matrix * m_bounding_box.center(); + cur_tran.set_offset(offset); + cur_tran.set_rotation(Vec3d(0, PI / 2.f, 0)); + break; + } + case GLGizmoAlignment::AlignType::DISTRIBUTE_X: + case GLGizmoAlignment::AlignType::X_MIN: { + cur_tran.set_scaling_factor(Vec3d(box_size[2] * scale, box_size[1] * scale, 1)); + auto center = m_bounding_box.center(); + center[0] = m_bounding_box.min[0]; + auto offset = m_orient_matrix * center; + cur_tran.set_offset(offset); + cur_tran.set_rotation(Vec3d(0, PI / 2.f, 0)); + break; + } + case GLGizmoAlignment::AlignType::X_MAX: { + cur_tran.set_scaling_factor(Vec3d(box_size[2] * scale, box_size[1] * scale, 1)); + auto center = m_bounding_box.center(); + center[0] = m_bounding_box.max[0]; + auto offset = m_orient_matrix * center; + cur_tran.set_offset(offset); + cur_tran.set_rotation(Vec3d(0, PI / 2.f, 0)); + break; + } + case GLGizmoAlignment::AlignType::CENTER_Y: { + cur_tran.set_scaling_factor(Vec3d(box_size[0] * scale, box_size[2] * scale, 1)); + auto center = m_bounding_box.center(); + auto offset = m_orient_matrix * center; + cur_tran.set_offset(offset); + cur_tran.set_rotation(Vec3d(PI / 2.f, 0, 0)); + break; + } + case GLGizmoAlignment::AlignType::DISTRIBUTE_Y: + case GLGizmoAlignment::AlignType::Y_MIN: { + cur_tran.set_scaling_factor(Vec3d(box_size[0] * scale, box_size[2] * scale, 1)); + auto center = m_bounding_box.center(); + center[1] = m_bounding_box.min[1]; + auto offset = m_orient_matrix * center; + cur_tran.set_offset(offset); + cur_tran.set_rotation(Vec3d(PI / 2.f, 0, 0)); + break; + } + case GLGizmoAlignment::AlignType::Y_MAX: { + cur_tran.set_scaling_factor(Vec3d(box_size[0] * scale, box_size[2] * scale, 1)); + auto center = m_bounding_box.center(); + center[1] = m_bounding_box.max[1]; + auto offset = m_orient_matrix * center; + cur_tran.set_offset(offset); + cur_tran.set_rotation(Vec3d(PI / 2.f, 0, 0)); + break; + } + case GLGizmoAlignment::AlignType::CENTER_Z: { + cur_tran.set_scaling_factor(Vec3d(box_size[0] * scale, box_size[1] * scale, 1)); + auto center = m_bounding_box.center(); + auto offset = m_orient_matrix * center; + cur_tran.set_offset(offset); + break; + } + case GLGizmoAlignment::AlignType::DISTRIBUTE_Z: + case GLGizmoAlignment::AlignType::Z_MIN: { + cur_tran.set_scaling_factor(Vec3d(box_size[0] * scale, box_size[1] * scale, 1)); + auto center = m_bounding_box.center(); + center[2] = m_bounding_box.min[2]; + auto offset = m_orient_matrix * center; + cur_tran.set_offset(offset); + break; + } + case GLGizmoAlignment::AlignType::Z_MAX: { + cur_tran.set_scaling_factor(Vec3d(box_size[0] * scale, box_size[1] * scale, 1)); + auto center = m_bounding_box.center(); + center[2] = m_bounding_box.max[2]; + auto offset = m_orient_matrix * center; + cur_tran.set_offset(offset); + break; + } + default: break; + } + // render + glsafe(::glEnable(GL_DEPTH_TEST)); + glsafe(::glDisable(GL_CULL_FACE)); + glsafe(::glEnable(GL_BLEND)); + glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + + ColorRGBA cp_clr = {1.0f, 1.0f, 1.0f, 0.5f}; + + const Camera &camera = wxGetApp().plater()->get_camera(); + const auto & view_matrix = camera.get_view_matrix(); + const auto & projection_matrix = camera.get_projection_matrix(); + auto volume_size = selection.get_volume_idxs().size(); + if (GLGizmoAlignment::AlignType::DISTRIBUTE_X == m_object_manipulation->m_align_type) { + float step = m_bounding_box.size()[0] / (volume_size + 1); + auto start = cur_tran.get_offset(); + for (int i = 0; i < volume_size; i++) { + cur_tran.set_offset(start + Vec3d((i + 1) * step, 0.f, 0.f)); + render_glmodel(m_align_plane, cp_clr.get_data(), view_matrix * cur_tran.get_matrix(), projection_matrix); + } + } else if (GLGizmoAlignment::AlignType::DISTRIBUTE_Y == m_object_manipulation->m_align_type) { + float step = m_bounding_box.size()[1] / (volume_size + 1); + auto start = cur_tran.get_offset(); + for (int i = 0; i < volume_size; i++) { + cur_tran.set_offset(start + Vec3d(0.f, (i + 1) * step, 0.f)); + render_glmodel(m_align_plane, cp_clr.get_data(), view_matrix * cur_tran.get_matrix(), projection_matrix); + } + } else if (GLGizmoAlignment::AlignType::DISTRIBUTE_Z == m_object_manipulation->m_align_type) { + float step = m_bounding_box.size()[2] / (volume_size + 1); + auto start = cur_tran.get_offset(); + for (int i = 0; i < volume_size; i++) { + cur_tran.set_offset(start + Vec3d(0.f, 0.f, (i + 1) * step)); + render_glmodel(m_align_plane, cp_clr.get_data(), view_matrix * cur_tran.get_matrix(), projection_matrix); + } + } else { + render_glmodel(m_align_plane, cp_clr.get_data(), view_matrix * cur_tran.get_matrix(), projection_matrix); + } + glsafe(::glEnable(GL_CULL_FACE)); + glsafe(::glDisable(GL_BLEND)); + } + } float space_size = 20.f * INV_ZOOM * GLGizmoBase::Grabber::GrabberSizeFactor; modify_radius(space_size); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp index 8a39f44..b3a4465 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp @@ -27,7 +27,7 @@ class GLGizmoMove3D : public GLGizmoBase Vec3d m_starting_box_bottom_center; GLModel m_vbo_cone; - + GLModel m_align_plane; //QDS: add size adjust related GizmoObjectManipulation* m_object_manipulation; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index 427c5fb..7fceb60 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -544,7 +544,7 @@ void GLGizmoRotate::init_data_from_selection(const Selection &selection) { if (m_custom_tran.has_value()) { Geometry::Transformation tran(m_custom_tran.value()); m_center = tran.get_offset(); - m_orient_matrix = tran.get_matrix(); + m_orient_matrix = tran.get_matrix_no_scaling_factor(); } else { m_center = sphere.first; m_orient_matrix = box_trafo; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index 3d88daa..724415f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -494,11 +494,9 @@ double GLGizmoScale3D::calc_ratio(const UpdateData& data) const Vec3d inters = data.mouse_ray.a + (m_starting.drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir; // vector from the starting position to the found intersection Vec3d inters_vec = inters - m_starting.drag_position; + double proj = inters_vec.dot(starting_vec.normalized()); - double proj = inters_vec.norm(); - const double sign = inters_vec.dot(starting_vec) > 1e-6f ? 1.0f : -1.0f; - - ratio = (len_starting_vec + proj * sign) / len_starting_vec; + ratio = (len_starting_vec + proj) / len_starting_vec; } if (wxGetKeyState(WXK_SHIFT)) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoText.cpp b/src/slic3r/GUI/Gizmos/GLGizmoText.cpp index ef138cf..d49a654 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoText.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoText.cpp @@ -1097,7 +1097,7 @@ void GLGizmoText::update_default_boldness() std::optional &boldness = m_style_manager.get_font_prop().boldness; if (m_bold) { set_default_boldness(boldness); - m_custom_boldness = *boldness; + m_custom_boldness = boldness.value_or(0.f); } else { boldness = 0.0f; m_custom_boldness = 0.0f; @@ -1575,8 +1575,19 @@ void GLGizmoText::load_init_text(bool first_open_text) if (selection.is_single_volume_or_modifier() || selection.is_single_full_object()) { auto model_volume = get_selected_model_volume(m_parent); if (model_volume) { - TextInfo text_info = model_volume->get_text_info(); if (model_volume->is_text()) { + { + const auto & ff = m_style_manager.get_font_file_with_cache(); + FontProp & font_prop = m_style_manager.get_font_prop(); + const FontFile::Info &font_info = get_font_info(*ff.font_file, font_prop); + // min max value + int min_boldness = static_cast(font_info.ascent * limits.boldness.gui.min); + int max_boldness = static_cast(font_info.ascent * limits.boldness.gui.max); + float min_skew = static_cast(limits.skew.gui.min); + float max_skew = static_cast(limits.skew.gui.max); + model_volume->check_boldness_skew_min_max(min_boldness, max_boldness, min_skew, max_skew); + } + TextInfo text_info = model_volume->get_text_info(); if (m_last_text_mv == model_volume && !m_is_serializing) { return; } @@ -3430,9 +3441,8 @@ void GLGizmoText::load_from_text_info(const TextInfo &text_info) wxString font_name = wxString::FromUTF8(m_font_name.c_str());//wxString(m_font_name.c_str(), wxConvUTF8); select_facename(font_name,false); } - m_custom_boldness = *text_info.text_configuration.style.prop.boldness; - m_custom_skew = *text_info.text_configuration.style.prop.skew; - + m_custom_boldness = text_info.text_configuration.style.prop.boldness.value_or(0.f); + m_custom_skew = text_info.text_configuration.style.prop.skew.value_or(0.f); if (is_text_changed) { process(true,std::nullopt,false); } @@ -3903,6 +3913,13 @@ EmbossShape &TextDataBase::create_shape() } else { text2vshapes(shape, m_font_file, text_w, fp, shape.scale, was_canceled); } + if (shape.shapes_with_ids.size() == 1 && shape.shapes_with_ids[0].expoly.empty()) {//empty deal + if (support_backup_fonts) { + text2vshapes(shape, m_font_file, L"?", fp, shape.scale, was_canceled, ft_fn); + } else { + text2vshapes(shape, m_font_file, L"?", fp, shape.scale, was_canceled); + } + } return shape; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 662af78..e84f574 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -211,6 +211,110 @@ bool GLGizmosManager::init_icon_textures() else return false; + if (IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/align_x_min.svg", 64, 64, texture_id)) + icon_list.insert(std::make_pair((int)IC_ALIGN_X_MIN, texture_id)); + else + return false; + if (IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/align_x_min_dark.svg", 64, 64, texture_id)) + icon_list.insert(std::make_pair((int) IC_ALIGN_X_MIN_DARK, texture_id)); + else + return false; + + if (IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/align_x_center.svg", 64, 64, texture_id)) + icon_list.insert(std::make_pair((int)IC_ALIGN_X_CENTER, texture_id)); + else + return false; + if (IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/align_x_center_dark.svg", 64, 64, texture_id)) + icon_list.insert(std::make_pair((int) IC_ALIGN_X_CENTER_DARK, texture_id)); + else + return false; + + if (IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/align_x_max.svg", 64, 64, texture_id)) + icon_list.insert(std::make_pair((int)IC_ALIGN_X_MAX, texture_id)); + else + return false; + if (IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/align_x_max_dark.svg", 64, 64, texture_id)) + icon_list.insert(std::make_pair((int) IC_ALIGN_X_MAX_DARK, texture_id)); + else + return false; + + if (IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/align_y_min.svg", 64, 64, texture_id)) + icon_list.insert(std::make_pair((int)IC_ALIGN_Y_MIN, texture_id)); + else + return false; + if (IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/align_y_min_dark.svg", 64, 64, texture_id)) + icon_list.insert(std::make_pair((int) IC_ALIGN_Y_MIN_DARK, texture_id)); + else + return false; + + if (IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/align_y_center.svg", 64, 64, texture_id)) + icon_list.insert(std::make_pair((int)IC_ALIGN_Y_CENTER, texture_id)); + else + return false; + if (IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/align_y_center_dark.svg", 64, 64, texture_id)) + icon_list.insert(std::make_pair((int) IC_ALIGN_Y_CENTER_DARK, texture_id)); + else + return false; + + if (IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/align_y_max.svg", 64, 64, texture_id)) + icon_list.insert(std::make_pair((int)IC_ALIGN_Y_MAX, texture_id)); + else + return false; + if (IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/align_y_max_dark.svg", 64, 64, texture_id)) + icon_list.insert(std::make_pair((int) IC_ALIGN_Y_MAX_DARK, texture_id)); + else + return false; + + if (IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/align_z_min.svg", 64, 64, texture_id)) + icon_list.insert(std::make_pair((int)IC_ALIGN_Z_MIN, texture_id)); + else + return false; + if (IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/align_z_min_dark.svg", 64, 64, texture_id)) + icon_list.insert(std::make_pair((int) IC_ALIGN_Z_MIN_DARK, texture_id)); + else + return false; + + if (IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/align_z_center.svg", 64, 64, texture_id)) + icon_list.insert(std::make_pair((int)IC_ALIGN_Z_CENTER, texture_id)); + else + return false; + if (IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/align_z_center_dark.svg", 64, 64, texture_id)) + icon_list.insert(std::make_pair((int) IC_ALIGN_Z_CENTER_DARK, texture_id)); + else + return false; + + if (IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/align_z_max.svg", 64, 64, texture_id)) + icon_list.insert(std::make_pair((int)IC_ALIGN_Z_MAX, texture_id)); + else + return false; + if (IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/align_z_max_dark.svg", 64, 64, texture_id)) + icon_list.insert(std::make_pair((int) IC_ALIGN_Z_MAX_DARK, texture_id)); + else + return false; + + if (IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/distribute_x.svg", 64, 64, texture_id)) + icon_list.insert(std::make_pair((int)IC_DISTRIBUTE_X, texture_id)); + else + return false; + if (IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/distribute_x_dark.svg", 64, 64, texture_id)) + icon_list.insert(std::make_pair((int) IC_DISTRIBUTE_X_DARK, texture_id)); + else + return false; + + if (IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/distribute_y.svg", 64, 64, texture_id)) + icon_list.insert(std::make_pair((int)IC_DISTRIBUTE_Y, texture_id)); + else + return false; + if (IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/distribute_y_dark.svg", 64, 64, texture_id)) + icon_list.insert(std::make_pair((int) IC_DISTRIBUTE_Y_DARK, texture_id)); + else + return false; + + if (IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/distribute_z_dark.svg", 64, 64, texture_id)) + icon_list.insert(std::make_pair((int)IC_DISTRIBUTE_Z_DARK, texture_id)); + else + return false; + return true; } @@ -576,6 +680,14 @@ bool GLGizmosManager::is_allow_select_all() const { return false; } +bool GLGizmosManager::is_allow_multi_select_parts_or_objects() const +{ + if (m_current == Undefined || m_current == EType::Move || m_current == EType::Rotate || m_current == EType::Scale) { + return true; + } + return false; +} + bool GLGizmosManager::is_allow_show_volume_highlight_outline() const { if (m_current == EType::Cut) { @@ -1554,5 +1666,54 @@ void GLGizmosManager::set_highlight(EType gizmo, bool highlight_shown) m_highlight = std::pair(gizmo, highlight_shown); } +// Lazy-load an icon on demand if it wasn't loaded during init_icon_textures +void* GLGizmosManager::ensure_icon_loaded(MENU_ICON_NAME icon) +{ + auto it = icon_list.find((int)icon); + if (it != icon_list.end()) + return it->second; + + std::string path; + int w = 20, h = 20; // default size for most icons + + switch (icon) { + case IC_TOOLBAR_RESET: path = "/images/toolbar_reset.svg"; w = h = 14; break; + case IC_TOOLBAR_RESET_HOVER: path = "/images/toolbar_reset_hover.svg"; w = h = 14; break; + case IC_TOOLBAR_RESET_ZERO: path = "/images/toolbar_reset_zero.svg"; w = h = 14; break; + case IC_TOOLBAR_RESET_ZERO_HOVER: path = "/images/toolbar_reset_zero_hover.svg"; w = h = 14; break; + case IC_TOOLBAR_TOOLTIP: path = "/images/toolbar_tooltip.svg"; w = 30; h = 22; break; + case IC_TOOLBAR_TOOLTIP_HOVER: path = "/images/toolbar_tooltip_hover.svg"; w = 30; h = 22; break; + case IC_FIT_CAMERA: path = "/images/fit_camera.svg"; w = h = 64; break; + case IC_FIT_CAMERA_HOVER: path = "/images/fit_camera_hover.svg"; w = h = 64; break; + case IC_FIT_CAMERA_DARK: path = "/images/fit_camera_dark.svg"; w = h = 64; break; + case IC_FIT_CAMERA_DARK_HOVER: path = "/images/fit_camera_dark_hover.svg"; w = h = 64; break; + case IC_TEXT_B: path = "/images/text_B.svg"; w = h = 20; break; + case IC_TEXT_B_DARK: path = "/images/text_B_dark.svg"; w = h = 20; break; + case IC_TEXT_T: path = "/images/text_T.svg"; w = h = 20; break; + case IC_TEXT_T_DARK: path = "/images/text_T_dark.svg"; w = h = 20; break; + case IC_ALIGN_X_MIN: path = "/images/align_x_min.svg"; w = h = 32; break; + case IC_ALIGN_X_CENTER: path = "/images/align_x_center.svg"; w = h = 32; break; + case IC_ALIGN_X_MAX: path = "/images/align_x_max.svg"; w = h = 32; break; + case IC_ALIGN_Y_MIN: path = "/images/align_y_min.svg"; w = h = 32; break; + case IC_ALIGN_Y_CENTER: path = "/images/align_y_center.svg"; w = h = 32; break; + case IC_ALIGN_Y_MAX: path = "/images/align_y_max.svg"; w = h = 32; break; + case IC_ALIGN_Z_MIN: path = "/images/align_z_min.svg"; w = h = 32; break; + case IC_ALIGN_Z_CENTER: path = "/images/align_z_center.svg"; w = h = 32; break; + case IC_ALIGN_Z_MAX: path = "/images/align_z_max.svg"; w = h = 32; break; + case IC_DISTRIBUTE_X: path = "/images/distribute_x.svg"; w = h = 32; break; + case IC_DISTRIBUTE_Y: path = "/images/distribute_y.svg"; w = h = 32; break; + case IC_DISTRIBUTE_Z: path = "/images/distribute_z.svg"; w = h = 32; break; + case IC_NAME_COUNT: default: return nullptr; + } + + ImTextureID texture_id; + if (IMTexture::load_from_svg_file(Slic3r::resources_dir() + path, w, h, texture_id)) { + icon_list[(int)icon] = texture_id; + return texture_id; + } + + return nullptr; +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index 24f2321..2cea54f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -139,6 +139,30 @@ public: IC_FIT_CAMERA_DARK, IC_FIT_CAMERA_DARK_HOVER, IC_HELIO_ICON, + IC_ALIGN_X_MIN, + IC_ALIGN_X_CENTER, + IC_ALIGN_X_MAX, + IC_ALIGN_Y_MIN, + IC_ALIGN_Y_CENTER, + IC_ALIGN_Y_MAX, + IC_ALIGN_Z_MIN, + IC_ALIGN_Z_CENTER, + IC_ALIGN_Z_MAX, + IC_DISTRIBUTE_X, + IC_DISTRIBUTE_Y, + IC_DISTRIBUTE_Z, + IC_ALIGN_X_MIN_DARK, + IC_ALIGN_X_CENTER_DARK, + IC_ALIGN_X_MAX_DARK, + IC_ALIGN_Y_MIN_DARK, + IC_ALIGN_Y_CENTER_DARK, + IC_ALIGN_Y_MAX_DARK, + IC_ALIGN_Z_MIN_DARK, + IC_ALIGN_Z_CENTER_DARK, + IC_ALIGN_Z_MAX_DARK, + IC_DISTRIBUTE_X_DARK, + IC_DISTRIBUTE_Y_DARK, + IC_DISTRIBUTE_Z_DARK, }; explicit GLGizmosManager(GLCanvas3D& parent); @@ -222,16 +246,16 @@ public: //QDS void* get_icon_texture_id(MENU_ICON_NAME icon) { - if (icon_list.find((int)icon) != icon_list.end()) - return icon_list[icon]; - else - return nullptr; + auto it = icon_list.find((int)icon); + if (it != icon_list.end()) + return it->second; + return ensure_icon_loaded(icon); } void* get_icon_texture_id(MENU_ICON_NAME icon) const{ - if (icon_list.find((int)icon) != icon_list.end()) - return icon_list.at(icon); - else - return nullptr; + auto it = icon_list.find((int)icon); + if (it != icon_list.end()) + return it->second; + return ensure_icon_loaded(icon); } void update_paint_base_camera_rotate_rad(); Vec3d get_flattening_normal() const; @@ -249,6 +273,7 @@ public: bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position = Vec2d::Zero(), bool shift_down = false, bool alt_down = false, bool control_down = false); bool is_paint_gizmo()const; bool is_allow_select_all() const; + bool is_allow_multi_select_parts_or_objects() const; bool is_allow_show_volume_highlight_outline() const; bool is_allow_drag_volume() const; bool is_allow_mouse_drag_selected() const; @@ -296,6 +321,7 @@ private: bool is_svg_selected(int idx) const; std::string on_hover(int idx); void on_click(int idx); + static void* ensure_icon_loaded(MENU_ICON_NAME icon); private: bool m_object_located_outside_plate{false}; diff --git a/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp b/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp index 0042d65..cc1bf5d 100644 --- a/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp +++ b/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp @@ -2,6 +2,7 @@ #include #include "GizmoObjectManipulation.hpp" + #include "slic3r/GUI/GUI_ObjectList.hpp" //#include "I18N.hpp" #include "GLGizmosManager.hpp" @@ -19,6 +20,7 @@ #include + namespace Slic3r { namespace GUI @@ -48,7 +50,7 @@ static double get_volume_min_z(const GLVolume* volume) } GizmoObjectManipulation::GizmoObjectManipulation(GLCanvas3D& glcanvas) - : m_glcanvas(glcanvas) + : m_glcanvas(glcanvas), m_alignment_helper(new GLGizmoAlignment(glcanvas)) { m_imperial_units = wxGetApp().app_config->get("use_inches") == "1"; m_new_unit_string = m_imperial_units ? L("in") : L("mm"); @@ -60,6 +62,10 @@ GizmoObjectManipulation::GizmoObjectManipulation(GLCanvas3D& glcanvas) m_desc_move["part_selection"] = _L("Part selection"); m_desc_move["snap_step_caption"] = shift + _L("Left mouse button"); m_desc_move["snap_step"] = _L("Fixed step drag"); + m_desc_move["multiple_selected_objects_caption"] = _L("Keep holding down Ctrl") + "+" + _L("Left mouse button"); + m_desc_move["multiple_selected_objects"] = _L("Select multiple objects"); + m_desc_move["multiple_selected_parts_caption"] = _L("Keep holding down Alt") + "+" + _L("Left mouse button"); + m_desc_move["multiple_selected_parts"] = _L("Select multiple parts"); m_desc_rotate["part_selection_caption"] = alt + _L("Left mouse button"); m_desc_rotate["part_selection"] = _L("Part selection"); @@ -72,6 +78,11 @@ GizmoObjectManipulation::GizmoObjectManipulation(GLCanvas3D& glcanvas) m_desc_scale["single_sided"] = _L("Single sided scaling"); } +GizmoObjectManipulation::~GizmoObjectManipulation() +{ + delete m_alignment_helper; +} + void GizmoObjectManipulation::UpdateAndShow(const bool show) { if (show) { @@ -485,7 +496,7 @@ void GizmoObjectManipulation::on_change(const std::string &opt_key, int axis, do else if (opt_key == "rotation") change_rotation_value(axis, new_value); else if (opt_key == "absolute_rotation") - change_absolute_rotation_value(axis, new_value); + change_absolute_rotation_value(axis, new_value); else if (opt_key == "scale") change_scale_value(axis, new_value); else if (opt_key == "size") @@ -596,6 +607,10 @@ void GizmoObjectManipulation::reset_scale_value() change_scale_value(2, 100.); } +void GizmoObjectManipulation::set_dark_mode(bool flag) { + m_is_dark_mode = flag; +} + void GizmoObjectManipulation::set_uniform_scaling(const bool use_uniform_scale) { if (!use_uniform_scale) @@ -624,10 +639,10 @@ void GizmoObjectManipulation::set_coordinates_type(ECoordinatesType type) } static const char* label_values[3][3] = { - { "##position_x", "##position_y", "##position_z"}, - { "##rotation_x", "##rotation_y", "##rotation_z"}, - { "##absolute_rotation_x", "##absolute_rotation_y", "##absolute_rotation_z"} - }; +{ "##position_x", "##position_y", "##position_z"}, +{ "##rotation_x", "##rotation_y", "##rotation_z"}, +{ "##absolute_rotation_x", "##absolute_rotation_y", "##absolute_rotation_z"} +}; static const char* label_scale_values[2][3] = { { "##scale_x", "##scale_y", "##scale_z"}, @@ -713,7 +728,7 @@ void GizmoObjectManipulation::show_move_tooltip_information(ImGuiWrapper *imgui_ ImTextureID normal_id = m_glcanvas.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP); ImTextureID hover_id = m_glcanvas.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP_HOVER); - caption_max += imgui_wrapper->calc_text_size(": ").x + 35.f; + caption_max += imgui_wrapper->calc_text_size("x:").x + 35.f; float font_size = ImGui::GetFontSize(); ImVec2 button_size = ImVec2(font_size * 1.8, font_size * 1.3); @@ -728,12 +743,23 @@ void GizmoObjectManipulation::show_move_tooltip_information(ImGuiWrapper *imgui_ ImGui::SameLine(caption_max); imgui_wrapper->text_colored(ImGuiWrapper::COL_WINDOW_BG, text); }; - - for (const auto &t : std::array{"part_selection", "snap_step"}) - draw_text_with_caption(m_desc_move.at(t + "_caption") + ": ", m_desc_move.at(t)); + if (m_coordinates_type == ECoordinatesType::World) { + for (const auto &t : std::array{"part_selection", "snap_step", "multiple_selected_objects", "multiple_selected_parts"}) + draw_text_with_caption(m_desc_move.at(t + "_caption") + ": ", m_desc_move.at(t)); + } else { + for (const auto &t : std::array{"part_selection", "snap_step"}) + draw_text_with_caption(m_desc_move.at(t + "_caption") + ": ", m_desc_move.at(t)); + } ImGui::EndTooltip(); } ImGui::PopStyleVar(2); + //if (m_coordinates_type == ECoordinatesType::Instance) { + // ImVec2 current_pos0 = ImGui::GetCursorPos(); + // ImGui::SameLine(font_size * 4); + // ImVec2 current_pos = ImGui::GetCursorPos(); + // ImGui::SetCursorPos(ImVec2(current_pos.x, current_pos.y + 3.0f)); //ImGui::SetCursorPos(ImVec2(current_pos.x + 30.0f, current_pos.y - 25.0f)); + // imgui_wrapper->text(_L("Tip:If you want to use alignment function, please use Ctrl or Alt key to select more.")); + //} } void GizmoObjectManipulation::show_rotate_tooltip_information(ImGuiWrapper *imgui_wrapper, float caption_max, float x, float y) @@ -833,7 +859,7 @@ void GizmoObjectManipulation::set_init_rotation(const Geometry::Transformation & return -1; }; - float space_size = imgui_wrapper->get_style_scaling() * 8; + float space_size = imgui_wrapper->get_style_scaling() * 12; float position_size = imgui_wrapper->calc_text_size(_L("Position")).x + space_size; float caption_max = imgui_wrapper->calc_text_size(_L("Object coordinates")).x + 2 * space_size; float end_text_size = imgui_wrapper->calc_text_size(this->m_new_unit_string).x; @@ -848,6 +874,16 @@ void GizmoObjectManipulation::set_init_rotation(const Geometry::Transformation & // Rotation float unit_size = imgui_wrapper->calc_text_size(MAX_SIZE).x + space_size; + unit_size *= 2.0f; + { + //float min_width_inputs = caption_max + 3.0f * unit_size + 4.0f * space_size + end_text_size; + //float button_width = unit_size * 0.6f; + //float button_spacing = unit_size * 0.02f; + //float row_width = 4.0f * button_width + 3.0f * button_spacing; + //float min_width_buttons = caption_max + space_size + row_width + space_size; + //float target_width = (min_width_inputs > min_width_buttons) ? min_width_inputs : min_width_buttons; + ////ImGui::SetWindowSize(ImVec2(target_width, ImGui::GetWindowHeight())); + } int index = 1; int index_unit = 1; @@ -868,20 +904,23 @@ void GizmoObjectManipulation::set_init_rotation(const Geometry::Transformation & float caption_cs_size = imgui_wrapper->calc_text_size("").x; float caption_size = caption_cs_size + 2 * space_size; float combox_content_size = imgui_wrapper->calc_text_size(_L("Object coordinates")).x * 1.2 + imgui_wrapper->calc_text_size("xxx").x + imgui_wrapper->scaled(3); + float intput_box_space_size = space_size * 1.9f; + float temp_space_size = intput_box_space_size - space_size; ImGuiWrapper::push_combo_style(m_glcanvas.get_scale()); bool combox_changed = false; if (render_combo(imgui_wrapper, "", modes, selection_idx, caption_size, combox_content_size)) { combox_changed = true; } ImGuiWrapper::pop_combo_style(); - caption_max = combox_content_size - 4 * space_size; + caption_max = combox_content_size - 3 * space_size; + index = 2; ImGui::SameLine(caption_max + index * space_size); ImGui::PushItemWidth(unit_size); ImGui::TextAlignCenter("X"); - ImGui::SameLine(caption_max + unit_size + (++index) * space_size); + ImGui::SameLine(caption_max + unit_size + (++index) * space_size + temp_space_size); ImGui::PushItemWidth(unit_size); ImGui::TextAlignCenter("Y"); - ImGui::SameLine(caption_max + (++index_unit) * unit_size + (++index) * space_size); + ImGui::SameLine(caption_max + (++index_unit) * unit_size + (++index) * space_size + temp_space_size *1.2); ImGui::PushItemWidth(unit_size); ImGui::TextAlignCenter("Z"); @@ -894,19 +933,17 @@ void GizmoObjectManipulation::set_init_rotation(const Geometry::Transformation & else { imgui_wrapper->text(_L("Position")); } - - ImGui::SameLine(caption_max + index * space_size); + ImGui::SameLine(caption_max + index * space_size + space_size); ImGui::PushItemWidth(unit_size); ImGui::QDTInputDouble(label_values[0][0], &display_position[0], 0.0f, 0.0f, "%.2f"); - ImGui::SameLine(caption_max + unit_size + (++index) * space_size); + ImGui::SameLine(caption_max + unit_size + (++index) * space_size + intput_box_space_size); ImGui::PushItemWidth(unit_size); ImGui::QDTInputDouble(label_values[0][1], &display_position[1], 0.0f, 0.0f, "%.2f"); - ImGui::SameLine(caption_max + (++index_unit) * unit_size + (++index) * space_size); + ImGui::SameLine(caption_max + (++index_unit) * unit_size + (++index) * space_size + intput_box_space_size + space_size *0.75f); ImGui::PushItemWidth(unit_size); ImGui::QDTInputDouble(label_values[0][2], &display_position[2], 0.0f, 0.0f, "%.2f"); - ImGui::SameLine(caption_max + (++index_unit) * unit_size + (++index) * space_size); + ImGui::SameLine(caption_max + (++index_unit) * unit_size + (++index) * space_size + intput_box_space_size); imgui_wrapper->text(this->m_new_unit_string); - bool is_avoid_one_update{false}; if (combox_changed) { combox_changed = false; @@ -936,13 +973,115 @@ void GizmoObjectManipulation::set_init_rotation(const Geometry::Transformation & } } if (!focued_on_text) m_glcanvas.handle_sidebar_focus_event("", false); + + // Add align and distribute buttons using AlignmentHelper + Selection& align_selection = m_glcanvas.get_selection(); + size_t selection_count = align_selection.get_volume_idxs().size(); + if (selection_count >= 2 && m_coordinates_type == ECoordinatesType::World) { + float scale_icon = 1.2f; + float icon_size = ImGui::GetFrameHeight() * scale_icon; + float temp_tip_caption_max = imgui_wrapper->calc_text_size(_L("Align top-bottom center") + " (-X)").x * 1.3f; + ImGui::AlignTextToFramePadding(); + imgui_wrapper->text(_L("Align") + "/" + _L("Distribute"));//ImGui::Dummy(ImVec2(0.0f, ImGui::GetFrameHeight() * 0.3f)); + if (ImGui::IsItemHovered()) { + imgui_wrapper->tooltip(_L("Holded down Ctrl key continuously and click by left mouse button can select multiple objects, or holded down Alt key continuously and click by left mouse button can select multiple parts"), + temp_tip_caption_max *1.5); + } + + float button_spacing = 0; + float start_x = caption_max + space_size *1.5; + ImGui::SameLine(start_x); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(61.f / 255.f, 203.f / 255.f, 115.f / 255.f, 1.f)); + show_align_icon(imgui_wrapper, temp_tip_caption_max, GLGizmoAlignment::AlignType::X_MIN, + (int) m_is_dark_mode ? GLGizmosManager::MENU_ICON_NAME::IC_ALIGN_X_MIN_DARK : GLGizmosManager::MENU_ICON_NAME::IC_ALIGN_X_MIN, + icon_size, + _L("Align left") + " (-X)", ""); + + ImGui::SameLine(0, button_spacing); + show_align_icon(imgui_wrapper, temp_tip_caption_max, GLGizmoAlignment::AlignType::CENTER_X, + (int) m_is_dark_mode ? GLGizmosManager::MENU_ICON_NAME::IC_ALIGN_X_CENTER_DARK : GLGizmosManager::MENU_ICON_NAME::IC_ALIGN_X_CENTER, + icon_size, + _L("Align left-right center") + " (X)", ""); + + ImGui::SameLine(0, button_spacing); + show_align_icon(imgui_wrapper, temp_tip_caption_max, GLGizmoAlignment::AlignType::X_MAX, + (int) m_is_dark_mode ? GLGizmosManager::MENU_ICON_NAME::IC_ALIGN_X_MAX_DARK : GLGizmosManager::MENU_ICON_NAME::IC_ALIGN_X_MAX, + icon_size, + _L("Align right") + " (+X)", ""); + + + ImGui::SameLine(0, button_spacing); + show_align_icon(imgui_wrapper, temp_tip_caption_max, GLGizmoAlignment::AlignType::DISTRIBUTE_X, + (int) m_is_dark_mode ? GLGizmosManager::MENU_ICON_NAME::IC_DISTRIBUTE_X_DARK : GLGizmosManager::MENU_ICON_NAME::IC_DISTRIBUTE_X, + icon_size, + _L("Distribute left-right") + " (X)", _L("Please select at least 3 parts or objects"), true); + float new_space_size = space_size *0.8f; + ImGui::SameLine(start_x + unit_size + space_size + new_space_size); + show_align_icon(imgui_wrapper, temp_tip_caption_max, GLGizmoAlignment::AlignType::Y_MIN, + (int) m_is_dark_mode ? GLGizmosManager::MENU_ICON_NAME::IC_ALIGN_Y_MIN_DARK : GLGizmosManager::MENU_ICON_NAME::IC_ALIGN_Y_MIN, icon_size, + _L("Align front") + " (-Y)", ""); + + ImGui::SameLine(0, button_spacing); + show_align_icon(imgui_wrapper, temp_tip_caption_max, GLGizmoAlignment::AlignType::CENTER_Y, + (int) m_is_dark_mode ? GLGizmosManager::MENU_ICON_NAME::IC_ALIGN_Y_CENTER_DARK : GLGizmosManager::MENU_ICON_NAME::IC_ALIGN_Y_CENTER, + icon_size, + _L("Align front-back center") + " (Y)", ""); + + ImGui::SameLine(0, button_spacing); + show_align_icon(imgui_wrapper, temp_tip_caption_max, GLGizmoAlignment::AlignType::Y_MAX, + (int) m_is_dark_mode ? GLGizmosManager::MENU_ICON_NAME::IC_ALIGN_Y_MAX_DARK : GLGizmosManager::MENU_ICON_NAME::IC_ALIGN_Y_MAX, icon_size, + _L("Align back") + " (+Y)", ""); + + ImGui::SameLine(0, button_spacing); + show_align_icon(imgui_wrapper, temp_tip_caption_max, GLGizmoAlignment::AlignType::DISTRIBUTE_Y, + (int) m_is_dark_mode ? GLGizmosManager::MENU_ICON_NAME::IC_DISTRIBUTE_Y_DARK : GLGizmosManager::MENU_ICON_NAME::IC_DISTRIBUTE_Y, + icon_size, + _L("Distribute front-back") + " (Y)", _L("Please select at least 3 parts or objects"), true); + + ImGui::SameLine(start_x + 2 * (unit_size + space_size) + new_space_size + new_space_size); + show_align_icon(imgui_wrapper, temp_tip_caption_max, GLGizmoAlignment::AlignType::Z_MIN, + (int) m_is_dark_mode ? GLGizmosManager::MENU_ICON_NAME::IC_ALIGN_Z_MIN_DARK : GLGizmosManager::MENU_ICON_NAME::IC_ALIGN_Z_MIN, + icon_size, + _L("Align bottom") + " (-Z)", ""); + + ImGui::SameLine(0, button_spacing); + show_align_icon(imgui_wrapper, temp_tip_caption_max, GLGizmoAlignment::AlignType::CENTER_Z, + (int) m_is_dark_mode ? GLGizmosManager::MENU_ICON_NAME::IC_ALIGN_Z_CENTER_DARK : GLGizmosManager::MENU_ICON_NAME::IC_ALIGN_Z_CENTER, + icon_size, + _L("Align top-bottom center") + " (Z)", ""); + + ImGui::SameLine(0, button_spacing); + show_align_icon(imgui_wrapper, temp_tip_caption_max, GLGizmoAlignment::AlignType::Z_MAX, + (int) m_is_dark_mode ? GLGizmosManager::MENU_ICON_NAME::IC_ALIGN_Z_MAX_DARK : GLGizmosManager::MENU_ICON_NAME::IC_ALIGN_Z_MAX, + icon_size, + _L("Align top") + " (+Z)", ""); + + ImGui::SameLine(0, button_spacing); + show_align_icon(imgui_wrapper, temp_tip_caption_max, GLGizmoAlignment::AlignType::DISTRIBUTE_Z, + (int) m_is_dark_mode ? GLGizmosManager::MENU_ICON_NAME::IC_DISTRIBUTE_Z_DARK : GLGizmosManager::MENU_ICON_NAME::IC_DISTRIBUTE_Z, icon_size, + _L("Distribute top-bottom") + " (Z)", _L("Please select at least 3 parts or objects"), true); + + ImGui::PopStyleColor(); + if (!ImGui::IsAnyItemHovered()) { + m_align_type = GLGizmoAlignment::AlignType::NONE; + } + } + float get_cur_y = ImGui::GetContentRegionMax().y + ImGui::GetFrameHeight() + y; float tip_caption_max = 0.f; float total_text_max = 0.f; - for (const auto &t : std::array{"part_selection", "snap_step"}) { - tip_caption_max = std::max(tip_caption_max, imgui_wrapper->calc_text_size(m_desc_move[t + "_caption"]).x); - total_text_max = std::max(total_text_max, imgui_wrapper->calc_text_size(m_desc_move[t]).x); + if (m_coordinates_type == ECoordinatesType::World) { + for (const auto &t : std::array{"part_selection", "snap_step", "multiple_selected_objects", "multiple_selected_parts"}) { + tip_caption_max = std::max(tip_caption_max, imgui_wrapper->calc_text_size(m_desc_move[t + "_caption"]).x); + total_text_max = std::max(total_text_max, imgui_wrapper->calc_text_size(m_desc_move[t]).x); + } + } else { + for (const auto &t : std::array{"part_selection", "snap_step"}){ + tip_caption_max = std::max(tip_caption_max, imgui_wrapper->calc_text_size(m_desc_move[t + "_caption"]).x); + total_text_max = std::max(total_text_max, imgui_wrapper->calc_text_size(m_desc_move[t]).x); + } } + show_move_tooltip_information(imgui_wrapper, tip_caption_max, x, get_cur_y); m_last_active_item = current_active_id; last_move_input_window_width = ImGui::GetWindowWidth(); @@ -951,6 +1090,67 @@ void GizmoObjectManipulation::set_init_rotation(const Geometry::Transformation & ImGuiWrapper::pop_toolbar_style(); } +void GizmoObjectManipulation::show_align_icon(ImGuiWrapper * imgui_wrapper, + float max_tooltip_width, + GLGizmoAlignment::AlignType align_type, + int icon, + float icon_size, + const wxString & function_tip, + const wxString & enable_tip, + bool show_enable_tip) +{ + bool can_align = false; + if (GLGizmoAlignment::AlignType::DISTRIBUTE_X <= align_type) { + can_align = m_alignment_helper->can_distribute(align_type); + } else { + can_align = m_alignment_helper->can_align(align_type); + } + + ImTextureID normal_id = m_glcanvas.get_gizmos_manager().get_icon_texture_id((GLGizmosManager::MENU_ICON_NAME) icon); + if (!can_align) { + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.3f); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::GetStyle().Colors[ImGuiCol_Button]); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImGui::GetStyle().Colors[ImGuiCol_Button]); + } + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f); + if (ImGui::ImageButton3(normal_id, normal_id, ImVec2(icon_size, icon_size))) { + if (can_align) { + if (GLGizmoAlignment::AlignType::DISTRIBUTE_X <= align_type) { + m_alignment_helper->distribute_objects(align_type); + } else { + m_alignment_helper->align_objects(align_type); + } + } + } + ImGui::PopStyleVar(); + if (!can_align) { + ImGui::PopStyleColor(2); + ImGui::PopStyleVar(); + } + + if (ImGui::IsItemHovered()) { + if (can_align) { + m_align_type = align_type; + } else { + m_align_type = GLGizmoAlignment::AlignType::NONE; + } + //imgui_wrapper->tooltip(tip, max_tooltip_width); + ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BACKGROUND); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 1.0f, 1.0f)); + if (can_align) { + ImGui::SetTooltip("%s", function_tip.ToUTF8().data()); + } else { + if (show_enable_tip) { + wxString combinedStr = function_tip+"\n" + enable_tip; + ImGui::SetTooltip("%s", combinedStr.ToUTF8().data()); + } else { + ImGui::SetTooltip("%s", function_tip.ToUTF8().data()); + } + } + ImGui::PopStyleColor(2); + } +} + void GizmoObjectManipulation::do_render_rotate_window(ImGuiWrapper *imgui_wrapper, std::string window_name, float x, float y, float bottom_limit) { // QDS: GUI refactor: move gizmo to the right diff --git a/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.hpp b/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.hpp index 96e8aea..65bc051 100644 --- a/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.hpp +++ b/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.hpp @@ -6,12 +6,13 @@ #include "libslic3r/Point.hpp" #include #include "slic3r/GUI/Selection.hpp" -//#include "slic3r/GUI/GLCanvas3D.hpp" +#include "GLGizmoAlignment.hpp" namespace Slic3r { namespace GUI { class GLCanvas3D; +class GLGizmoAlignment; class GizmoObjectManipulation { @@ -56,6 +57,8 @@ public: bool m_imperial_units { false }; bool m_use_object_cs{false}; + + GLGizmoAlignment::AlignType m_align_type{GLGizmoAlignment::AlignType::NONE}; // Mirroring buttons and their current state //enum MirrorButtonState { // mbHidden, @@ -104,7 +107,7 @@ protected: public: GizmoObjectManipulation(GLCanvas3D& glcanvas); - ~GizmoObjectManipulation() {} + ~GizmoObjectManipulation(); bool IsShown(); void UpdateAndShow(const bool show); @@ -114,6 +117,7 @@ public: // Called from the App to update the UI if dirty. void update_if_dirty(); + void set_dark_mode(bool flag); void set_uniform_scaling(const bool uniform_scale); bool get_uniform_scaling() const { return m_uniform_scale; } void set_use_object_cs(bool flag){ if (m_use_object_cs != flag) m_use_object_cs = flag; } @@ -136,7 +140,6 @@ public: void do_render_scale_input_window(ImGuiWrapper* imgui_wrapper, std::string window_name, float x, float y, float bottom_limit); float max_unit_size(int number, Vec3d &vec1, Vec3d &vec2,std::string str); bool reset_button(ImGuiWrapper *imgui_wrapper, float caption_max, float unit_size, float space_size, float end_text_size); - bool reset_zero_button(ImGuiWrapper *imgui_wrapper, float caption_max, float unit_size, float space_size, float end_text_size); bool qdt_checkbox(const wxString &label, bool &value); @@ -145,6 +148,15 @@ public: void show_scale_tooltip_information(ImGuiWrapper *imgui_wrapper, float caption_max, float x, float y); void set_init_rotation(const Geometry::Transformation &value); + void show_align_icon(ImGuiWrapper *imgui_wrapper, + float max_tooltip_width, + GLGizmoAlignment::AlignType, + int icon, + float icon_size, + const wxString &function_tip, + const wxString &enable_tip, + bool show_enable_tip = false); + private: void reset_settings_value(); void update_settings_value(const Selection& selection); @@ -167,12 +179,15 @@ private: void reset_scale_value(); GLCanvas3D& m_glcanvas; + GLGizmoAlignment* m_alignment_helper; unsigned int m_last_active_item { 0 }; std::map m_desc_move; std::map m_desc_rotate; std::map m_desc_scale; Vec3d m_init_rotation; Transform3d m_init_rotation_scale_tran; + + bool m_is_dark_mode{false}; }; }} diff --git a/src/slic3r/GUI/HMS.cpp b/src/slic3r/GUI/HMS.cpp index 53b17fb..291bc93 100644 --- a/src/slic3r/GUI/HMS.cpp +++ b/src/slic3r/GUI/HMS.cpp @@ -10,7 +10,7 @@ static const char* HMS_PATH = "hms"; static const char* HMS_LOCAL_IMG_PATH = "hms/local_image"; // the local HMS info -static unordered_set package_dev_id_types {"094", "239", "093", "22E"}; +static unordered_set package_dev_id_types {"094", "239", "093", "22E", "31B"}; namespace Slic3r { namespace GUI { diff --git a/src/slic3r/GUI/HelioReleaseNote.cpp b/src/slic3r/GUI/HelioReleaseNote.cpp index 1e6f056..9a51598 100644 --- a/src/slic3r/GUI/HelioReleaseNote.cpp +++ b/src/slic3r/GUI/HelioReleaseNote.cpp @@ -644,6 +644,12 @@ static double s_round(double value, int n) return std::round(value * factor) / factor; } +// Named constants for Limits dropdown selection indices +static constexpr int LIMITS_HELIO_DEFAULT = 0; +static constexpr int LIMITS_SLICER_DEFAULT = 1; +// Width chosen to accommodate "Helio default (recommended)" text in dropdown +static constexpr int LIMITS_DROPDOWN_WIDTH = 350; + HelioInputDialog::HelioInputDialog(wxWindow *parent /*= nullptr*/) : DPIDialog(static_cast(wxGetApp().mainframe), wxID_ANY, wxString("Helio Additive"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX) { @@ -781,73 +787,93 @@ static double s_round(double value, int n) config_outerwall[1] = _L("Yes"); auto outerwall = create_combo_item(panel_optimization, "optimiza_outerwall", _L("Optimise Outer Walls"), config_outerwall, 0); - /*advanced Options*/ - wxBoxSizer* advace_setting_sizer = new wxBoxSizer(wxHORIZONTAL); - advanced_settings_link = new wxPanel(panel_optimization); - advanced_settings_link->SetBackgroundColour(*wxWHITE); + // layers to optimize - now top-level + auto plater = Slic3r::GUI::wxGetApp().plater(); + int layer_count = plater ? plater->get_gcode_layers_count() : 0; + wxBoxSizer* layers_to_optimize_item = create_input_optimize_layers(panel_optimization, layer_count); - Label* more_setting_tips = new Label(advanced_settings_link, Label::Head_14 ,_L("Advanced Settings")); - wxFont bold_font = more_setting_tips->GetFont(); - bold_font.SetWeight(wxFONTWEIGHT_BOLD); - more_setting_tips->SetFont(bold_font); - advace_setting_sizer->Add(more_setting_tips, 0, wxALIGN_LEFT | wxTOP, FromDIP(4)); - advanced_options_icon = new wxStaticBitmap(advanced_settings_link, wxID_ANY, create_scaled_bitmap("helio_advanced_option0", panel_optimization, 18), wxDefaultPosition, - wxSize(FromDIP(18), FromDIP(18))); - advace_setting_sizer->Add(advanced_options_icon, 0, wxALIGN_LEFT | wxTOP, FromDIP(4)); - advanced_settings_link->SetSizer(advace_setting_sizer); - advanced_settings_link->Layout(); - advanced_settings_link->Fit(); + // Limits dropdown + std::map config_limits; + config_limits[LIMITS_HELIO_DEFAULT] = _L("Helio default (recommended)"); + config_limits[LIMITS_SLICER_DEFAULT] = _L("Slicer default"); + // Create Limits dropdown with wider width to fit "Helio default (recommended)" text + auto limits = create_combo_item(panel_optimization, "limits", _L("Limits"), config_limits, LIMITS_HELIO_DEFAULT, LIMITS_DROPDOWN_WIDTH); - /*buy now*/ - //more_setting_tips->SetForegroundColour(wxColour(49, 49, 49)); - more_setting_tips->Bind(wxEVT_ENTER_WINDOW, [this](auto& e) {SetCursor(wxCURSOR_HAND); }); - more_setting_tips->Bind(wxEVT_LEAVE_WINDOW, [this](auto& e) {SetCursor(wxCURSOR_ARROW); }); - advanced_settings_link->Hide(); - - /*advanced option*/ - panel_advanced_option = new wxPanel(panel_optimization); + // Create panel for velocity and volumetric speed fields - visibility controlled by Limits selection + panel_velocity_volumetric = new wxPanel(panel_optimization); if (wxGetApp().dark_mode()) { - panel_advanced_option->SetBackgroundColour(StateColor::darkModeColorFor(*wxWHITE)); + panel_velocity_volumetric->SetBackgroundColour(StateColor::darkModeColorFor(*wxWHITE)); } else { - panel_advanced_option->SetBackgroundColour(*wxWHITE); + panel_velocity_volumetric->SetBackgroundColour(*wxWHITE); } - wxBoxSizer* sizer_advanced_option = new wxBoxSizer(wxVERTICAL); + wxBoxSizer* sizer_velocity_volumetric = new wxBoxSizer(wxVERTICAL); + // velocity and volumetric speed fields auto double_min_checker = TextInputValChecker::CreateDoubleMinChecker(0); // velocity float min_speed = 0.0; float max_speed = 0.0; - auto plater = Slic3r::GUI::wxGetApp().plater(); if (plater) { plater->get_preview_min_max_value_of_option((int) gcode::EViewType::Feedrate, min_speed, max_speed); } - wxBoxSizer* min_velocity_item = create_input_item(panel_advanced_option, "min_velocity", _L("Min Velocity"), wxT("mm/s"), { double_min_checker } ); + wxBoxSizer* min_velocity_item = create_input_item(panel_velocity_volumetric, "min_velocity", _L("Min Velocity"), wxT("mm/s"), { double_min_checker } ); m_input_items["min_velocity"]->GetTextCtrl()->SetLabel(wxString::Format("%.0f", s_round(min_speed, 0))); - wxBoxSizer* max_velocity_item = create_input_item(panel_advanced_option, "max_velocity", _L("Max Velocity"), wxT("mm/s"), { double_min_checker }); + wxBoxSizer* max_velocity_item = create_input_item(panel_velocity_volumetric, "max_velocity", _L("Max Velocity"), wxT("mm/s"), { double_min_checker }); m_input_items["max_velocity"]->GetTextCtrl()->SetLabel(wxString::Format("%.0f", s_round(max_speed, 0))); // volumetric speed float min_volumetric_speed = 0.0; float max_volumetric_speed = 0.0; if (plater) { plater->get_preview_min_max_value_of_option((int) gcode::EViewType::VolumetricRate, min_volumetric_speed, max_volumetric_speed); } - wxBoxSizer* min_volumetric_speed_item = create_input_item(panel_advanced_option, "min_volumetric_speed", _L("Min Volumetric Speed"), wxT("mm\u00B3/s"), { double_min_checker }); + wxBoxSizer* min_volumetric_speed_item = create_input_item(panel_velocity_volumetric, "min_volumetric_speed", _L("Min Volumetric Speed"), wxT("mm\u00B3/s"), { double_min_checker }); m_input_items["min_volumetric_speed"]->GetTextCtrl()->SetLabel(wxString::Format("%.2f", s_round(min_volumetric_speed, 2))); - wxBoxSizer* max_volumetric_speed_item = create_input_item(panel_advanced_option, "max_volumetric_speed", _L("Max Volumetric Speed"), wxT("mm\u00B3/s"), { double_min_checker }); + wxBoxSizer* max_volumetric_speed_item = create_input_item(panel_velocity_volumetric, "max_volumetric_speed", _L("Max Volumetric Speed"), wxT("mm\u00B3/s"), { double_min_checker }); m_input_items["max_volumetric_speed"]->GetTextCtrl()->SetLabel(wxString::Format("%.2f", s_round(max_volumetric_speed, 2))); - // layers to optimize - int layer_count = plater ? plater->get_gcode_layers_count() : 0; - wxBoxSizer* layers_to_optimize_item = create_input_optimize_layers(panel_advanced_option, layer_count); + sizer_velocity_volumetric->Add(min_velocity_item, 0, wxEXPAND, 0); + sizer_velocity_volumetric->Add(max_velocity_item, 0, wxEXPAND|wxTOP, FromDIP(6)); + sizer_velocity_volumetric->Add(min_volumetric_speed_item, 0, wxEXPAND|wxTOP, FromDIP(6)); + sizer_velocity_volumetric->Add(max_volumetric_speed_item, 0, wxEXPAND|wxTOP, FromDIP(6)); + panel_velocity_volumetric->SetSizer(sizer_velocity_volumetric); + panel_velocity_volumetric->Layout(); + panel_velocity_volumetric->Fit(); - sizer_advanced_option->Add(min_velocity_item, 0, wxEXPAND, FromDIP(0)); - sizer_advanced_option->Add(max_velocity_item, 0, wxEXPAND|wxTOP, FromDIP(6)); - sizer_advanced_option->Add(min_volumetric_speed_item, 0, wxEXPAND|wxTOP, FromDIP(6)); - sizer_advanced_option->Add(max_volumetric_speed_item, 0, wxEXPAND|wxTOP, FromDIP(6)); - sizer_advanced_option->Add(layers_to_optimize_item, 0, wxEXPAND|wxTOP, FromDIP(6)); - panel_advanced_option->SetSizer(sizer_advanced_option); - panel_advanced_option->Layout(); - panel_advanced_option->Fit(); + // Hide velocity/volumetric speed fields by default (Helio default selected) + panel_velocity_volumetric->Hide(); + + // Add event handler for Limits dropdown to show/hide velocity/volumetric speed fields + m_combo_items["limits"]->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& e) { + // Prevent changing if only_advanced_settings is true (forced mode) + if (only_advanced_settings) { + // Force it back to "Slicer default" + m_combo_items["limits"]->SetSelection(LIMITS_SLICER_DEFAULT); + return; + } + + int selection = m_combo_items["limits"]->GetSelection(); + bool show_fields = (selection == LIMITS_SLICER_DEFAULT); + use_advanced_settings = show_fields; + + if (show_fields) { + panel_velocity_volumetric->Show(); + } else { + panel_velocity_volumetric->Hide(); + } + panel_optimization->Layout(); + panel_optimization->Fit(); + Layout(); + Fit(); + }); + + // Create empty panel_advanced_option for backward compatibility (kept hidden) + panel_advanced_option = new wxPanel(panel_optimization); + if (wxGetApp().dark_mode()) { + panel_advanced_option->SetBackgroundColour(StateColor::darkModeColorFor(*wxWHITE)); + } else { + panel_advanced_option->SetBackgroundColour(*wxWHITE); + } + panel_advanced_option->Hide(); sizer_optimization->Add(0, 0, 0, wxTOP, FromDIP(24)); sizer_optimization->Add(m_remain_usage_time, 0, wxEXPAND, 0); @@ -863,31 +889,17 @@ static double s_round(double value, int n) } sizer_optimization->Add(outerwall, 0, wxEXPAND, 0); sizer_optimization->Add(0, 0, 0, wxTOP, FromDIP(12)); - sizer_optimization->Add(advanced_settings_link, 0, wxEXPAND, 0); - sizer_optimization->Add(0, 0, 0, wxTOP, FromDIP(8)); - sizer_optimization->Add(panel_advanced_option, 0, wxEXPAND, 0); + sizer_optimization->Add(layers_to_optimize_item, 0, wxEXPAND, 0); + sizer_optimization->Add(0, 0, 0, wxTOP, FromDIP(12)); + sizer_optimization->Add(limits, 0, wxEXPAND, 0); + sizer_optimization->Add(0, 0, 0, wxTOP, FromDIP(12)); + sizer_optimization->Add(panel_velocity_volumetric, 0, wxEXPAND, 0); sizer_optimization->Add(0, 0, 0, wxTOP, FromDIP(8)); panel_optimization->SetSizer(sizer_optimization); panel_optimization->Layout(); panel_optimization->Fit(); - panel_optimization->Hide(); - panel_advanced_option->Hide(); - - more_setting_tips->Bind(wxEVT_LEFT_DOWN, [=](wxMouseEvent& e) { - if (only_advanced_settings) - return; - - if (!use_advanced_settings) { - use_advanced_settings = true; - } - else { - use_advanced_settings = false; - } - show_advanced_mode(); - }); - panel_optimization->Hide(); /*last trace id*/ @@ -925,7 +937,7 @@ static double s_round(double value, int n) last_tid_panel->Layout(); last_tid_panel->Fit(); - buy_now_link = new LinkLabel(this, _L("Buy Now"), "https://wiki.helioadditive.com/"); + buy_now_link = new LinkLabel(this, _L("Buy Now / Manage Account"), "https://wiki.helioadditive.com/"); buy_now_link->SeLinkLabelFColour(wxColour(175, 124, 255)); buy_now_link->Bind(wxEVT_ENTER_WINDOW, [this](auto& e) { SetCursor(wxCURSOR_HAND); }); buy_now_link->Bind(wxEVT_LEAVE_WINDOW, [this](auto& e) { SetCursor(wxCURSOR_ARROW); }); @@ -1040,13 +1052,11 @@ void HelioInputDialog::update_action(int action) if (auto temp_ptr = weak_ptr.lock()) { CallAfter([=]() { if (times > 0 || addons > 0) { - advanced_settings_link->Show(); m_button_confirm->Enable(); } else { - advanced_settings_link->Hide(); if (times <= 0) { - buy_now_link->setLabel(_L("Buy Now")); + buy_now_link->setLabel(_L("Buy Now / Manage Account")); if (m_remain_usage_time) { m_remain_usage_time->UpdateHelpTips(0); } } else { @@ -1083,24 +1093,38 @@ void HelioInputDialog::update_action(int action) try { - /*hide based mode when nozzle diameter = 0.2*/ + /*force use Slicer default (show velocity/volumetric speed fields) when nozzle diameter = 0.2*/ const auto& full_config = wxGetApp().preset_bundle->full_config(); const auto& project_config = wxGetApp().preset_bundle->project_config; double nozzle_diameter = full_config.option("nozzle_diameter")->values[0]; if (boost::str(boost::format("%.1f") % nozzle_diameter) == "0.2") { + // Force select "Slicer default" to show velocity/volumetric speed fields only_advanced_settings = true; use_advanced_settings = true; - show_advanced_mode(); + m_combo_items["limits"]->SetSelection(LIMITS_SLICER_DEFAULT); + m_combo_items["limits"]->Disable(); // Disable dropdown to prevent changing + panel_velocity_volumetric->Show(); + panel_optimization->Layout(); + panel_optimization->Fit(); + Layout(); + Fit(); } - /*hide based mode when preser has nozzle diameter = 0.2*/ + /*force use Slicer default when preset has layer height < 0.2*/ auto edited_preset = wxGetApp().preset_bundle->prints.get_edited_preset().config; auto layer_height = edited_preset.option("layer_height")->value; if (layer_height < 0.2) { + // Force select "Slicer default" to show velocity/volumetric speed fields only_advanced_settings = true; use_advanced_settings = true; - show_advanced_mode(); + m_combo_items["limits"]->SetSelection(LIMITS_SLICER_DEFAULT); + m_combo_items["limits"]->Disable(); // Disable dropdown to prevent changing + panel_velocity_volumetric->Show(); + panel_optimization->Layout(); + panel_optimization->Fit(); + Layout(); + Fit(); } } catch (...){} @@ -1123,14 +1147,7 @@ void HelioInputDialog::update_action(int action) void HelioInputDialog::show_advanced_mode() { - if (use_advanced_settings) { - advanced_options_icon->SetBitmap(create_scaled_bitmap("helio_advanced_option1", panel_optimization, 18)); - panel_advanced_option->Show(); - } - else { - advanced_options_icon->SetBitmap(create_scaled_bitmap("helio_advanced_option0", panel_optimization, 18)); - panel_advanced_option->Hide(); - } + // Deprecated: Advanced settings visibility is now controlled by the Limits dropdown event handler. This function does nothing. Layout(); Fit(); } @@ -1172,14 +1189,14 @@ wxBoxSizer* HelioInputDialog::create_input_item(wxWindow* parent, std::string ke return item_sizer; } -wxBoxSizer* HelioInputDialog::create_combo_item(wxWindow* parent, std::string key, wxString name, std::map combolist, int def) +wxBoxSizer* HelioInputDialog::create_combo_item(wxWindow* parent, std::string key, wxString name, std::map combolist, int def, int width) { wxBoxSizer* item_sizer = new wxBoxSizer(wxHORIZONTAL); Label* inout_title = new Label(parent, Label::Body_14, name); inout_title->SetFont(::Label::Body_14); inout_title->SetForegroundColour(wxColour("#262E30")); - auto combobox = new ::ComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(FromDIP(120), -1), 0, nullptr, wxCB_READONLY); + auto combobox = new ::ComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(FromDIP(width), -1), 0, nullptr, wxCB_READONLY); combobox->SetFont(::Label::Body_13); combobox->GetDropDown().SetFont(::Label::Body_13); @@ -1360,15 +1377,16 @@ Slic3r::HelioQuery::OptimizationInput HelioInputDialog::get_optimization_input(b data.layers_to_optimize[1] = temp; } - if (!use_advanced_settings) { - ok = true; - return data; + // Check Limits selection - only read velocity/volumetric speed when "Slicer default" is selected + int limits_selection = m_combo_items["limits"]->GetSelection(); + if (limits_selection == LIMITS_SLICER_DEFAULT) { + // Read velocity and volumetric speed fields + if (!s_get_double_val(m_input_items["min_velocity"], data.min_velocity)) { return data; } + if (!s_get_double_val(m_input_items["max_velocity"], data.max_velocity)) { return data; } + if (!s_get_double_val(m_input_items["min_volumetric_speed"], data.min_volumetric_speed)) { return data; } + if (!s_get_double_val(m_input_items["max_volumetric_speed"], data.max_volumetric_speed)) { return data; } } - - if (!s_get_double_val(m_input_items["min_velocity"], data.min_velocity)) { return data; } - if (!s_get_double_val(m_input_items["max_velocity"], data.max_velocity)) { return data; } - if (!s_get_double_val(m_input_items["min_volumetric_speed"], data.min_volumetric_speed)) { return data; } - if (!s_get_double_val(m_input_items["max_volumetric_speed"], data.max_volumetric_speed)) { return data; } + // When "Helio default" (selection == 0) is selected, skip reading these fields - backend will use Helio defaults ok = true; return data; } diff --git a/src/slic3r/GUI/HelioReleaseNote.hpp b/src/slic3r/GUI/HelioReleaseNote.hpp index 3c95885..f7b9a30 100644 --- a/src/slic3r/GUI/HelioReleaseNote.hpp +++ b/src/slic3r/GUI/HelioReleaseNote.hpp @@ -137,6 +137,7 @@ private: wxPanel* panel_simulation{nullptr}; wxPanel* panel_pay_optimization{nullptr}; wxPanel* panel_optimization{nullptr}; + wxPanel* panel_velocity_volumetric{nullptr}; wxPanel* advanced_settings_link{nullptr}; LinkLabel* buy_now_link{nullptr}; @@ -166,7 +167,7 @@ public: private: wxBoxSizer* create_input_item(wxWindow* parent, std::string key, wxString name, wxString unit, const std::vector>& checkers); - wxBoxSizer* create_combo_item(wxWindow* parent, std::string key, wxString name, std::map combolist, int def); + wxBoxSizer* create_combo_item(wxWindow* parent, std::string key, wxString name, std::map combolist, int def, int width = 120); wxBoxSizer* create_input_optimize_layers(wxWindow* parent, int layer_count); void on_selected_simulation(wxMouseEvent& e) { update_action(0); } diff --git a/src/slic3r/GUI/HttpServer.cpp b/src/slic3r/GUI/HttpServer.cpp index 9a9bef9..5389b3f 100644 --- a/src/slic3r/GUI/HttpServer.cpp +++ b/src/slic3r/GUI/HttpServer.cpp @@ -3,8 +3,118 @@ #include "GUI_App.hpp" #include "slic3r/Utils/Http.hpp" #include "slic3r/Utils/NetworkAgent.hpp" +#include namespace Slic3r { + + +namespace Scramble { +static const char B64URL_ALPHABET[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + +static inline int b64url_val(char c) +{ + if (c >= 'A' && c <= 'Z') return c - 'A'; + if (c >= 'a' && c <= 'z') return c - 'a' + 26; + if (c >= '0' && c <= '9') return c - '0' + 52; + if (c == '-') return 62; + if (c == '_') return 63; + return -1; +} + +std::string base64url_encode(const std::string &in) +{ + const unsigned char *p = (const unsigned char *) in.data(); + size_t n = in.size(); + std::string out; + out.reserve(((n + 2) / 3) * 4); + + size_t i = 0; + while (i + 2 < n) { + uint32_t tri = (uint32_t(p[i]) << 16) | (uint32_t(p[i + 1]) << 8) | uint32_t(p[i + 2]); + out.push_back(B64URL_ALPHABET[(tri >> 18) & 0x3F]); + out.push_back(B64URL_ALPHABET[(tri >> 12) & 0x3F]); + out.push_back(B64URL_ALPHABET[(tri >> 6) & 0x3F]); + out.push_back(B64URL_ALPHABET[tri & 0x3F]); + i += 3; + } + + if (i < n) { + int remain = int(n - i); + uint32_t tri = (uint32_t(p[i]) << 16); + if (remain == 2) tri |= (uint32_t(p[i + 1]) << 8); + out.push_back(B64URL_ALPHABET[(tri >> 18) & 0x3F]); + out.push_back(B64URL_ALPHABET[(tri >> 12) & 0x3F]); + if (remain == 2) out.push_back(B64URL_ALPHABET[(tri >> 6) & 0x3F]); + } + return out; +} + +std::string base64url_decode(const std::string &in) +{ + size_t n = in.size(); + if (n == 0) return std::string(); + + std::string out; + out.reserve((n * 3) / 4 + 3); + size_t i = 0; + while (i < n) { + int v0 = -1, v1 = -1, v2 = -1, v3 = -1; + v0 = (i < n) ? b64url_val(in[i++]) : -1; + v1 = (i < n) ? b64url_val(in[i++]) : -1; + if (v0 < 0 || v1 < 0) return std::string(); + if (i < n) { + int t = b64url_val(in[i]); + if (t >= 0) { + v2 = t; + ++i; + } + } + if (i < n) { + int t = b64url_val(in[i]); + if (t >= 0) { + v3 = t; + ++i; + } + } + + if (v2 >= 0 && v3 >= 0) { + uint32_t tri = (v0 << 18) | (v1 << 12) | (v2 << 6) | v3; + out.push_back(char((tri >> 16) & 0xFF)); + out.push_back(char((tri >> 8) & 0xFF)); + out.push_back(char(tri & 0xFF)); + } else if (v2 >= 0 && v3 < 0) { + uint32_t tri = (v0 << 18) | (v1 << 12) | (v2 << 6); + out.push_back(char((tri >> 16) & 0xFF)); + out.push_back(char((tri >> 8) & 0xFF)); + } else if (v2 < 0 && v3 < 0) { + uint32_t tri = (v0 << 18) | (v1 << 12); + out.push_back(char((tri >> 16) & 0xFF)); + } else { + return std::string(); + } + } + return out; +} + +std::string xor_obfuscate(const std::string &key, const std::string &data) +{ + if (key.empty()) return std::string(); + std::string out; + out.resize(data.size()); + const size_t klen = key.size(); + for (size_t i = 0; i < data.size(); ++i) { + unsigned char d = (unsigned char) data[i]; + unsigned char k = (unsigned char) key[i % klen]; + out[i] = (char) (d ^ k); + } + return out; +} + +std::string scrambleWithKey(const std::string &plaintext, const std::string &key) { return base64url_encode(xor_obfuscate(key, plaintext)); } + +std::string descrambleWithKey(const std::string &token, const std::string &key) { return xor_obfuscate(key, base64url_decode(token)); } +} // Scramble + namespace GUI { struct TokenResp { @@ -146,6 +256,16 @@ static bool is_http_scheme(const std::string& url) return url.rfind("http://", 0) == 0 || url.rfind("https://", 0) == 0; } +static HttpReqType parse_request_type(const boost::beast::http::request& req) +{ + std::string target = req.target(); + if (boost::starts_with(target, "/refresh_token")) { + return HttpReqType::RefreshToken; + } + + return HttpReqType::Login; +} + static std::optional parse_login_params(const boost::beast::http::request& req) { @@ -153,6 +273,7 @@ parse_login_params(const boost::beast::http::requestsecond); - if (out.ticket.empty() || out.redirect_url.empty()) + if (out.ticket.empty() || out.redirect_url.empty()) { + BOOST_LOG_TRIVIAL(info) << "third_party_login: ticket or redirect_url empty"; return std::nullopt; + } - if (!is_http_scheme(out.redirect_url)) + if (!is_http_scheme(out.redirect_url)) { + BOOST_LOG_TRIVIAL(info) << "third_party_login: redirect_url empty is not a valid http url"; return std::nullopt; + } return out; } @@ -222,32 +347,149 @@ void session::do_write_302(LoginParams params, bool result) }); } -void session::handle_request() +void session::handle_login() { using namespace boost::beast; using http::field; using http::status; using http::string_body; + BOOST_LOG_TRIVIAL(info) << "third_party_login: new request received"; + auto login_params = parse_login_params(req_); if (login_params) { auto login_info = TicketLoginTask::perform_sync(login_params->ticket); - if (login_info.empty()) + if (login_info.empty()) { + BOOST_LOG_TRIVIAL(info) << "third_party_login: login info empty, login failed"; do_write_302(*login_params, false); + } else { NetworkAgent *agent = wxGetApp().getAgent(); agent->change_user(login_info); if (agent->is_user_login()) { wxGetApp().request_user_login(1); + BOOST_LOG_TRIVIAL(info) << "third_party_login: all good, login successful"; do_write_302(*login_params, true); - } else + } + else { + BOOST_LOG_TRIVIAL(info) << "third_party_login: after applying the login information, the application remains unlogged, login failed"; do_write_302(*login_params, false); + } GUI::wxGetApp().CallAfter([this] { wxGetApp().ShowUserLogin(false); }); } } - else + else { + BOOST_LOG_TRIVIAL(info) << "third_party_login: login param empty, login failed"; do_write_404(); + } +} + +void session::send_text_response(const std::string& text_data) +{ + using namespace boost::beast; + using http::field; + using http::status; + using http::string_body; + + auto res = std::make_shared>(status::ok, 11); + res->set(field::content_type, "text/plain; charset=utf-8"); + res->keep_alive(false); + res->body() = Scramble::scrambleWithKey(text_data, m_scramble_key); + res->prepare_payload(); + + auto self = shared_from_this(); + http::async_write(socket_, *res, [self, res](boost::beast::error_code ec, std::size_t bytes_transferred) { + if (!ec) { + BOOST_LOG_TRIVIAL(info) << "Successfully sent " << bytes_transferred << " bytes response"; + boost::system::error_code ignored_ec; + self->socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_send, ignored_ec); + } else { + BOOST_LOG_TRIVIAL(error) << "Failed to send response: " << ec.message(); + } + }); +} + +static void refresh_agora_url(char const* device, char const* dev_ver, char const* channel, std::shared_ptr sess) +{ + std::string device2 = device; + device2 += "|"; + device2 += dev_ver; + device2 += "|\"agora\"|"; + device2 += channel; + + wxGetApp().getAgent()->get_camera_url_for_golive(device2, wxGetApp().app_config->get("slicer_uuid") + "-golive", + [sess](std::string url) { + sess->send_text_response(url); + }); +} + +void session::set_scramble_key(std::string key) +{ + m_scramble_key = key; +} + +void session::handle_refresh_token() +{ + BOOST_LOG_TRIVIAL(info) << "new request received: refresh token"; + + struct refresh_token_params + { + std::string device; + std::string dev_ver; + std::string channel; + } out; + std::string dkey; + + std::string target = req_.target(); // "/refresh_token?device=...&dev_ver=...&channel=..." + size_t qpos = target.find('?'); + if (qpos == std::string::npos) { + BOOST_LOG_TRIVIAL(info) << "refresh token: invalid format"; + return; + } + std::string query = target.substr(qpos + 1); + + auto params = parse_query(query); + if (auto it = params.find("dkey"); it != params.end()) { + dkey = url_decode(it->second); + set_scramble_key(dkey); + } + else { + BOOST_LOG_TRIVIAL(info) << "refresh token: invalid dkey"; + return; + } + + if (auto it = params.find("did"); it != params.end()) + out.device = Scramble::descrambleWithKey(it->second, dkey); + if (auto it = params.find("dver"); it != params.end()) + out.dev_ver = url_decode(it->second); + if (auto it = params.find("dcha"); it != params.end()) + out.channel = url_decode(it->second); + + if (out.device.empty() || out.dev_ver.empty() || out.channel.empty()) { + BOOST_LOG_TRIVIAL(info) << "refresh token: refresh token param empty, refresh failed"; + do_write_404(); + return; + } + + refresh_agora_url(out.device.c_str(), out.dev_ver.c_str(), out.channel.c_str(), shared_from_this()); +} + +void session::handle_request() +{ + HttpReqType req_type = parse_request_type(req_); + switch (req_type) { + case HttpReqType::RefreshToken: + handle_refresh_token(); + break; + case HttpReqType::Login: + handle_login(); + break; + default: + BOOST_LOG_TRIVIAL(info) << "Unknown request type"; + do_write_404(); + break; + } } std::string TicketLoginTask::perform_sync(const std::string &ticket) @@ -279,13 +521,17 @@ std::optional TicketLoginTask::do_request_login_info(const std::str NetworkAgent *agent = wxGetApp().getAgent(); unsigned int http_code; std::string http_body; - if (agent->get_my_token(ticket, &http_code, &http_body) < 0) + if (agent->get_my_token(ticket, &http_code, &http_body) < 0) { + BOOST_LOG_TRIVIAL(info) << "third_party_login: get_my_token failed, http_code = " << http_code; return std::nullopt; + } else { auto token_resp = nlohmann::json::parse(http_body); auto token_data = token_resp.get(); - if (agent->get_my_profile(token_data.accessToken, &http_code, &http_body) < 0) + if (agent->get_my_profile(token_data.accessToken, &http_code, &http_body) < 0) { + BOOST_LOG_TRIVIAL(info) << "third_party_login: get_my_profile failed, http_code = " << http_code; return std::nullopt; + } else { auto profile_resp = nlohmann::json::parse(http_body); auto profile_data = profile_resp.get(); @@ -299,12 +545,14 @@ std::optional TicketLoginTask::do_request_login_info(const std::str j["data"]["user"]["name"] = profile_data.name; j["data"]["user"]["account"] = profile_data.account; j["data"]["user"]["avatar"] = profile_data.avatar; + BOOST_LOG_TRIVIAL(info) << "third_party_login: login info ready"; return std::string(j.dump()); } } } catch (...) { + BOOST_LOG_TRIVIAL(info) << "third_party_login: do_request_login_info exception"; return std::nullopt; } } diff --git a/src/slic3r/GUI/HttpServer.hpp b/src/slic3r/GUI/HttpServer.hpp index 515ff78..3999dc1 100644 --- a/src/slic3r/GUI/HttpServer.hpp +++ b/src/slic3r/GUI/HttpServer.hpp @@ -35,12 +35,22 @@ namespace GUI { std::string redirect_url; }; + enum class HttpReqType { + Login, + RefreshToken, + Unknown, + }; struct session : public std::enable_shared_from_this { explicit session(boost::asio::ip::tcp::socket s) : socket_(std::move(s)) {} void run() { do_read(); } + void send_text_response(const std::string& text_data); + + void set_scramble_key(std::string key); + private: + std::string m_scramble_key = ""; void do_read() { auto self = shared_from_this(); req_ = {}; // reset @@ -55,6 +65,10 @@ namespace GUI { void do_write_302(LoginParams params, bool result); + void handle_login(); + + void handle_refresh_token(); + void handle_request(); void fail_close(const boost::beast::error_code& ec) { diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index b7c0a5f..70e92cb 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -1021,7 +1021,7 @@ bool ImGuiWrapper::checkbox(const wxString &label, bool &value) return ImGui::Checkbox(label_utf8.c_str(), &value); } -bool ImGuiWrapper::qdt_checkbox(const wxString &label, bool &value) +bool ImGuiWrapper::qdt_checkbox(const wxString &label, bool &value, bool enabled, bool b_dark_mode) { bool result; bool b_value = value; @@ -1030,10 +1030,25 @@ bool ImGuiWrapper::qdt_checkbox(const wxString &label, bool &value) ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(0.27f, 0.47f, 0.98f, 1.00f)); ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(0.27f, 0.47f, 0.98f, 1.00f)); } + if (!enabled) { + float factor = b_value ? 0.8f : 1.0f; + if (b_dark_mode) { + ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(factor * 39.0f / 255.0f, factor * 39.0f / 255.0f, factor * 39.0f / 255.0f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(factor * 108.0f / 255.0f, factor * 108.0f / 255.0f, factor * 108.0f / 255.0f, 1.0f)); + } + else { + ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(factor * 230.0f / 255.0f, factor * 230.0f / 255.0f, factor * 230.0f / 255.0f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(factor * 163.0f / 255.0f, factor * 163.0f / 255.0f, factor * 163.0f / 255.0f, 1.0f)); + } + } auto label_utf8 = into_u8(label); result = ImGui::QDTCheckbox(label_utf8.c_str(), &value); + if (!enabled) { + ImGui::PopStyleColor(2); + } if (b_value) { ImGui::PopStyleColor(3);} + return result; } diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index 84f2d47..2dd2652 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -151,7 +151,7 @@ public: bool input_double(const wxString &label, const double &value, const std::string &format = "%.3f"); bool input_vec3(const std::string &label, const Vec3d &value, float width, const std::string &format = "%.3f"); bool checkbox(const wxString &label, bool &value); - bool qdt_checkbox(const wxString &label, bool &value); + bool qdt_checkbox(const wxString &label, bool &value, bool enabled = true, bool b_dark_mode = false); bool qdt_radio_button(const char *label, bool active); bool qdt_sliderin(const char *label, int *v, int v_min, int v_max, const char *format = "%d", ImGuiSliderFlags flags = 0); static void text(const char *label); diff --git a/src/slic3r/GUI/Jobs/BooleanOperationJob.cpp b/src/slic3r/GUI/Jobs/BooleanOperationJob.cpp new file mode 100644 index 0000000..2abd667 --- /dev/null +++ b/src/slic3r/GUI/Jobs/BooleanOperationJob.cpp @@ -0,0 +1,6 @@ +#include "BooleanOperationJob.hpp" + +namespace Slic3r { + namespace GUI { + } // namespace Slic3r::GUI +} // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/Jobs/BooleanOperationJob.hpp b/src/slic3r/GUI/Jobs/BooleanOperationJob.hpp new file mode 100644 index 0000000..d501d8a --- /dev/null +++ b/src/slic3r/GUI/Jobs/BooleanOperationJob.hpp @@ -0,0 +1,118 @@ +#ifndef slic3r_BooleanOperationJob_hpp_ +#define slic3r_BooleanOperationJob_hpp_ + +#include +#include +#include +#include +#include + +#include "slic3r/GUI/Camera.hpp" +#include "JobNew.hpp" +#include "libslic3r/TriangleMesh.hpp" + +namespace Slic3r { + namespace GUI { + enum EBooleanOperationState + { + NotStart, + Running, + Finished, + Cancelling, + Canceled, + Failed + }; + template + class BooleanOperationJob : public JobNew + { + public: + using BoolearOperationCancelCallback = std::function; + using BoolearOperationProcessCallback = std::function; + using BoolearOperationCallback = std::function; + using BoolearOperationProgressCallback = std::function; + using BoolearOperationFailedCallback = std::function; + explicit BooleanOperationJob() + { + + } + ~BooleanOperationJob() + { + + } + + void set_process_callback(const BoolearOperationProcessCallback& cb) + { + m_process_cb = cb; + } + + void set_finalize_callback(const BoolearOperationCallback& cb) + { + m_finalize_cb = cb; + } + + void set_cancel_callback(const BoolearOperationCancelCallback& cb) + { + m_cancel_cb = cb; + } + + void set_progress_callback(const BoolearOperationProgressCallback& cb) + { + m_progress_cb = cb; + } + + void set_failed_callback(const BoolearOperationFailedCallback& cb) + { + m_failed_cb = cb; + } + + void set_data(JobData& data) + { + m_data = data; + } + + void process(Ctl& ctl) override + { + if (m_process_cb) { + if (m_cancel_cb) { + m_data.cancel_cb = [&]()->bool { + return m_cancel_cb(ctl, m_data); + }; + } + if (m_failed_cb) { + m_data.failed_cb = [&]()->void { + m_failed_cb(m_data); + }; + } + m_data.progress_cb = m_progress_cb; + m_process_cb(ctl, m_data); + } + } + + void finalize(bool canceled, std::exception_ptr& eptr) override + { + if (m_finalize_cb) { + if (eptr) { + m_data.state = EBooleanOperationState::Failed; + } + m_finalize_cb(m_data); + } + } + + bool show_busy_cursor() override + { + return false; + }; + + private: + BoolearOperationProcessCallback m_process_cb{ nullptr }; + BoolearOperationCallback m_finalize_cb{ nullptr }; + BoolearOperationCancelCallback m_cancel_cb{ nullptr }; + BoolearOperationProgressCallback m_progress_cb{ nullptr }; + BoolearOperationFailedCallback m_failed_cb{ nullptr }; + + JobData m_data; + }; + } // namespace Slic3r::GUI +} // namespace Slic3r + +#endif // slic3r_BooleanOperationJob_hpp_ diff --git a/src/slic3r/GUI/Jobs/JobNew.hpp b/src/slic3r/GUI/Jobs/JobNew.hpp index 5d671a8..9c2256b 100644 --- a/src/slic3r/GUI/Jobs/JobNew.hpp +++ b/src/slic3r/GUI/Jobs/JobNew.hpp @@ -61,6 +61,8 @@ public: // function return to prevent further action. Leaving it with a non-null // value will result in rethrowing by the worker. virtual void finalize(bool /*canceled*/, std::exception_ptr &) {} + + virtual bool show_busy_cursor() { return true; }; }; }} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Jobs/OrientJob.cpp b/src/slic3r/GUI/Jobs/OrientJob.cpp index 77897a9..e71a651 100644 --- a/src/slic3r/GUI/Jobs/OrientJob.cpp +++ b/src/slic3r/GUI/Jobs/OrientJob.cpp @@ -228,7 +228,8 @@ orientation::OrientMesh OrientJob::get_orient_mesh(ModelInstance* instance) om.overhang_angle = full_config.opt_int("support_threshold_angle"); } - if (full_config.has("fan_direction") && full_config.has("auxiliary_fan")) + //y75 + if (full_config.has("fan_direction") && full_config.has("auxiliary_fan") && full_config.option("seal")->value) { int fan_config_idx = full_config.option>("fan_direction")->value; FanDirection config_dir = static_cast(fan_config_idx); diff --git a/src/slic3r/GUI/Jobs/PlaterWorker.hpp b/src/slic3r/GUI/Jobs/PlaterWorker.hpp index 8e344e6..6cb7f53 100644 --- a/src/slic3r/GUI/Jobs/PlaterWorker.hpp +++ b/src/slic3r/GUI/Jobs/PlaterWorker.hpp @@ -62,7 +62,9 @@ class PlaterWorker: public Worker { } wctl{c}; - CursorSetterRAII busycursor{wctl}; + if (m_job->show_busy_cursor()) { + CursorSetterRAII busycursor{ wctl }; + } using namespace std::chrono; steady_clock::time_point process_start = steady_clock::now(); diff --git a/src/slic3r/GUI/Jobs/PrintJob.cpp b/src/slic3r/GUI/Jobs/PrintJob.cpp index 3e8bdfb..788912d 100644 --- a/src/slic3r/GUI/Jobs/PrintJob.cpp +++ b/src/slic3r/GUI/Jobs/PrintJob.cpp @@ -274,6 +274,7 @@ void PrintJob::process() params.auto_bed_leveling = this->auto_bed_leveling; params.auto_flow_cali = this->auto_flow_cali; params.auto_offset_cali = this->auto_offset_cali; + params.extruder_cali_manual_mode = this->extruder_cali_manual_mode; params.task_ext_change_assist = this->task_ext_change_assist; params.try_emmc_print = this->could_emmc_print; //y71 @@ -306,15 +307,15 @@ void PrintJob::process() params.preset_name = profile_name->second; } catch (...) {} - } - + } + if (m_print_type != "from_sdcard_view") { auto model_name = model_info->metadata_items.find(QDT_DESIGNER_MODEL_TITLE_TAG); if (model_name != model_info->metadata_items.end()) { try { std::string mall_model_name = model_name->second; std::replace(mall_model_name.begin(), mall_model_name.end(), ' ', '_'); - const char *unusable_symbols = "#\'<>:\\|?*\""; + const char *unusable_symbols = "<>[]:/\\|?*\" "; for (const char *symbol = unusable_symbols; *symbol != '\0'; ++symbol) { std::replace(mall_model_name.begin(), mall_model_name.end(), *symbol, '_'); } std::regex pattern("_+"); diff --git a/src/slic3r/GUI/Jobs/PrintJob.hpp b/src/slic3r/GUI/Jobs/PrintJob.hpp index a344e25..3a4da5b 100644 --- a/src/slic3r/GUI/Jobs/PrintJob.hpp +++ b/src/slic3r/GUI/Jobs/PrintJob.hpp @@ -91,14 +91,17 @@ public: int auto_bed_leveling{0}; int auto_flow_cali{0}; int auto_offset_cali{0}; + int extruder_cali_manual_mode = -1; //y71 int enable_multi_box{0}; + //y void set_print_config(std::string bed_type, bool bed_leveling, bool flow_cali, bool vabration_cali, bool record_timelapse, bool layer_inspect, bool ext_change_assist, int auto_bed_levelingt, int auto_enable_multi_box, int auto_flow_calit, - int auto_offset_calit) + int auto_offset_calit, + int extruder_calit_manual_mode) { task_bed_type = bed_type; task_bed_leveling = bed_leveling; @@ -112,7 +115,7 @@ public: enable_multi_box = auto_enable_multi_box; auto_flow_cali = auto_flow_calit; auto_offset_cali = auto_offset_calit; - + extruder_cali_manual_mode = extruder_calit_manual_mode; } int status_range() const override diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index fc3c940..e42b58e 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -934,18 +934,9 @@ void MainFrame::update_layout() { // jump to 3deditor under preview_only mode if (evt.GetId() == tp3DEditor){ - Sidebar& sidebar = GUI::wxGetApp().sidebar(); - if (sidebar.need_auto_sync_after_connect_printer()) { - sidebar.set_need_auto_sync_after_connect_printer(false); - //y60 - // sidebar.sync_extruder_list(); - } - - m_plater->update(true); - - m_plater->show_wrapping_detect_dialog_if_necessary(); - - if (!preview_only_hint()) + wxCommandEvent event(EVT_SWITCH_TO_PREPARE_TAB); + wxPostEvent(m_plater, event); + if(!preview_only_hint()) return; } //y @@ -1166,6 +1157,24 @@ void MainFrame::init_tabpanel() //y53 new_sel = e.GetSelection(); + if (old_sel != wxNOT_FOUND && + new_sel != old_sel && + m_project != nullptr && + m_tabpanel->GetPage((size_t)old_sel) == m_project && + m_project->is_editing_page()) { + MessageDialog dlg(this, + _L("The current page has unsaved changes. You can continue editing or choose to save/discard before leaving."), + _L("Save"), + wxYES_NO | wxCANCEL |wxCENTRE); + int ret = dlg.ShowModal(); + if (ret == wxID_YES) { + m_project->save_project(); + } else { + e.Veto(); + return; + } + } + if (wxGetApp().preset_bundle && wxGetApp().preset_bundle->printers.get_edited_preset().is_qdt_vendor_preset(wxGetApp().preset_bundle) && new_sel == tpMonitor) { @@ -1230,6 +1239,10 @@ void MainFrame::init_tabpanel() #else m_tabpanel->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, [this](wxBookCtrlEvent& e) { #endif + if (e.GetEventObject() != m_tabpanel){ + return;// The event maybe from child TabPanel + } + // y5 m_printer_view->SetPauseThread(true); //QDS @@ -1243,9 +1256,6 @@ void MainFrame::init_tabpanel() m_param_panel->OnActivate(); } else if (sel == tpPreview) { - m_plater->reset_check_status(); - if (!m_plater->check_ams_status(m_slice_select == eSliceAll)) - return; wxPostEvent(m_plater, SimpleEvent(EVT_GLVIEWTOOLBAR_PREVIEW)); m_param_panel->OnActivate(); } @@ -1862,6 +1872,10 @@ wxBoxSizer* MainFrame::create_side_tools() m_slice_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent& event) { + m_plater->reset_check_status(); + if (!m_plater->check_ams_status(m_slice_select == eSliceAll)) + return; + m_plater->set_slice_from_slice_btn(true); //this->m_plater->select_view_3D("Preview"); m_plater->exit_gizmo(); @@ -1878,9 +1892,10 @@ wxBoxSizer* MainFrame::create_side_tools() if (slice) { std::string printer_model = wxGetApp().preset_bundle->printers.get_edited_preset().config.opt_string("printer_model"); - if (printer_model == "H2D") { + int extruder_count = wxGetApp().preset_bundle->get_printer_extruder_count(); + if (extruder_count > 1) { if (wxGetApp().app_config->get("play_slicing_video") == "true") { - MessageDialog dlg(this, _L("This is your first time slicing with the H2D machine.\nWould you like to watch a quick tutorial video?"), _L("First Guide"), wxYES_NO); + MessageDialog dlg(this, _L("This is your first time slicing with the dual extruder machine.\nWould you like to watch a quick tutorial video?"), _L("First Guide"), wxYES_NO); auto res = dlg.ShowModal(); if (res == wxID_YES) { play_dual_extruder_slice_video(); @@ -1889,13 +1904,13 @@ wxBoxSizer* MainFrame::create_side_tools() wxGetApp().app_config->set("play_slicing_video", "false"); } - if ((wxGetApp().app_config->get("play_tpu_printing_video") == "true") ) { + if ((wxGetApp().app_config->get("play_tpu_printing_video") == "true")) { auto used_filaments = curr_plate->get_extruders(); std::transform(used_filaments.begin(), used_filaments.end(), used_filaments.begin(), [](auto i) {return i - 1; }); auto full_config = wxGetApp().preset_bundle->full_config(); auto filament_types = full_config.option("filament_type")->values; if (std::any_of(used_filaments.begin(), used_filaments.end(), [filament_types](int idx) { return filament_types[idx] == "TPU"; })) { - MessageDialog dlg(this, _L("This is your first time printing tpu filaments with the H2D machine.\nWould you like to watch a quick tutorial video?"), _L("First Guide"), wxYES_NO); + MessageDialog dlg(this, _L("This is your first time printing tpu filaments with the dual extruder machine.\nWould you like to watch a quick tutorial video?"), _L("First Guide"), wxYES_NO); auto res = dlg.ShowModal(); if (res == wxID_YES) { play_dual_extruder_print_tpu_video(); diff --git a/src/slic3r/GUI/MediaPlayCtrl.cpp b/src/slic3r/GUI/MediaPlayCtrl.cpp index 5ce6b93..29a59c8 100644 --- a/src/slic3r/GUI/MediaPlayCtrl.cpp +++ b/src/slic3r/GUI/MediaPlayCtrl.cpp @@ -28,6 +28,7 @@ #include #include "wx/evtloop.h" +#include static std::map error_messages = { {1, L("The device cannot handle more conversations. Please retry later.")}, @@ -620,7 +621,9 @@ void MediaPlayCtrl::ToggleStream() NetworkAgent *agent = wxGetApp().getAgent(); if (!agent) return; std::string protocols[] = {"", "\"tutk\"", "\"agora\"", "\"tutk\",\"agora\""}; - agent->get_camera_url(m_machine + "|" + m_dev_ver + "|" + protocols[m_remote_proto], + + agent->get_camera_url_for_golive(m_machine + "|" + m_dev_ver + "|" + protocols[m_remote_proto], + wxGetApp().app_config->get("slicer_uuid") + "-golive", [this, m = m_machine, v = agent->get_version(), dv = m_dev_ver](std::string url) { if (boost::algorithm::starts_with(url, "qidi:///")) { url += "&device=" + m; @@ -643,9 +646,23 @@ void MediaPlayCtrl::ToggleStream() .ShowModal(); return; } + std::string url_alt = url; + if (url.find("agora") != std::string::npos) { + wxGetApp().start_http_server(); + url_alt += "&auxiliary_enable=1"; + if (wxGetApp().app_config->get("not_show_agora_tips") != "1") { + MessageDialog msg_dlg(this->GetParent(), + _L("The live streaming feature relies on QIDI Studio to run,and the stream will end some time after the app is closed."), + _L("Information"), wxOK | wxICON_INFORMATION); + msg_dlg.show_dsa_button(); + msg_dlg.ShowModal(); + if (msg_dlg.get_checkbox_state()) wxGetApp().app_config->set("not_show_agora_tips", "1"); + } + } + std::string file_url = data_dir() + "/cameratools/url.txt"; boost::nowide::ofstream file(file_url); - auto url2 = encode_path(url.c_str()); + auto url2 = encode_path(url_alt.c_str()); file.write(url2.c_str(), url2.size()); file.close(); m_streaming = true; @@ -749,8 +766,22 @@ void MediaPlayCtrl::SetStatus(wxString const &msg2, bool hyperlink) OutputDebugStringA(msg.ToUTF8().data()); OutputDebugStringA("\n"); #endif // __WXMSW__ - m_label_status->SetLabel(msg); - m_label_status->Wrap(GetSize().GetWidth() - 120 - m_label_stat->GetSize().GetWidth()); + + wxGCDC dc; + wxSize msg_size = dc.GetTextExtent(msg); + int blank_width = FromDIP(GetSize().GetWidth() - 120 - m_label_stat->GetSize().GetWidth() - m_button_play->GetSize().GetWidth()); + int max_status_width = std::min(blank_width, FromDIP(600)); + + wxString display_text = msg; + if (msg_size.x > max_status_width) { + display_text = wxControl::Ellipsize(msg, dc, wxELLIPSIZE_END, max_status_width); + m_label_status->SetToolTip(msg); + } else { + m_label_status->SetToolTip(wxEmptyString); + } + + m_label_status->SetLabel(display_text); + long style = m_label_status->GetWindowStyle() & ~LB_HYPERLINK; if (hyperlink) { style |= LB_HYPERLINK; @@ -870,7 +901,7 @@ bool MediaPlayCtrl::start_stream_service(bool *need_install) boost::filesystem::path start_dir(boost::filesystem::path(data_dir()) / "plugins"); #ifdef __WXMSW__ auto plugins_dir = boost::nowide::widen(data_dir()) + L"\\plugins\\"; - for (auto dll : {L"QIDISource.dll", L"live555.dll"}) { + for (auto dll : {L"QIDISource.dll", L"live555.dll", L"agora_rtc_sdk.dll", L"libagora-core.dll", L"libagora-ffmpeg.dll", L"libagora-soundtouch.dll"}) { auto file_dll = tools_dir + dll; auto file_dll2 = plugins_dir + dll; if (!boost::filesystem::exists(file_dll) || boost::filesystem::last_write_time(file_dll) != boost::filesystem::last_write_time(file_dll2)) diff --git a/src/slic3r/GUI/Monitor.cpp b/src/slic3r/GUI/Monitor.cpp index e203d65..09dba85 100644 --- a/src/slic3r/GUI/Monitor.cpp +++ b/src/slic3r/GUI/Monitor.cpp @@ -383,9 +383,11 @@ void MonitorPanel::update_all() auto current_page = m_tabpanel->GetCurrentPage(); if (current_page == m_status_info_panel) { - m_status_info_panel->obj = obj; - m_status_info_panel->m_media_play_ctrl->SetMachineObject(obj); - m_status_info_panel->update(obj); + if (m_status_info_panel->IsShown()) { + m_status_info_panel->obj = obj; + m_status_info_panel->m_media_play_ctrl->SetMachineObject(obj); + m_status_info_panel->update(obj); + } } else if (current_page == m_upgrade_panel) { m_upgrade_panel->update(obj); } else if (current_page == m_media_file_panel) { @@ -531,6 +533,16 @@ void MonitorPanel::jump_to_HMS() m_tabpanel->SetSelection(PT_HMS); } +void MonitorPanel::jump_to_Upgrade() +{ + if (this->IsShown()) { + auto page = m_tabpanel->GetCurrentPage(); + if (page && page != m_upgrade_panel) { + m_tabpanel->SetSelection(PT_UPDATE); + } + } +} + void MonitorPanel::jump_to_LiveView() { if (!this->IsShown()) { return; } @@ -544,5 +556,19 @@ void MonitorPanel::jump_to_LiveView() m_status_info_panel->get_media_play_ctrl()->jump_to_play(); } +void MonitorPanel::jump_to_Rack() +{ + if (!this->IsShown()) { + return; + } + + auto page = m_tabpanel->GetCurrentPage(); + if (page && page != m_status_info_panel) { + m_tabpanel->SetSelection(PT_STATUS); + } + + m_status_info_panel->jump_to_Rack(); +} + } // GUI } // Slic3r diff --git a/src/slic3r/GUI/Monitor.hpp b/src/slic3r/GUI/Monitor.hpp index a7f7126..d3032a8 100644 --- a/src/slic3r/GUI/Monitor.hpp +++ b/src/slic3r/GUI/Monitor.hpp @@ -156,7 +156,9 @@ public: void jump_to_HMS(); + void jump_to_Upgrade(); void jump_to_LiveView(); + void jump_to_Rack(); }; diff --git a/src/slic3r/GUI/MsgDialog.hpp b/src/slic3r/GUI/MsgDialog.hpp index 0b6acee..b00f239 100644 --- a/src/slic3r/GUI/MsgDialog.hpp +++ b/src/slic3r/GUI/MsgDialog.hpp @@ -64,6 +64,8 @@ struct MsgDialog : DPIDialog void show_dsa_button(wxString const & title = {}); bool get_checkbox_state(); virtual void on_dpi_changed(const wxRect& suggested_rect); + + void AddButton(wxWindowID btn_id, const wxString& label, bool set_focus = false) { add_button(btn_id, set_focus, label); }; void SetButtonLabel(wxWindowID btn_id, const wxString& label, bool set_focus = false); protected: diff --git a/src/slic3r/GUI/NetworkTestDialog.cpp b/src/slic3r/GUI/NetworkTestDialog.cpp index c6dbaa6..ceaf6ad 100644 --- a/src/slic3r/GUI/NetworkTestDialog.cpp +++ b/src/slic3r/GUI/NetworkTestDialog.cpp @@ -1,3 +1,7 @@ +#ifdef __WINDOWS__ +#include +#endif + #include "NetworkTestDialog.hpp" #include "I18N.hpp" @@ -13,6 +17,188 @@ namespace Slic3r { namespace GUI { +#ifdef __WINDOWS__ +namespace WindowsUtils { + +static const int WINDOWS_SERVER2016_BUILD = 14393; +static const int WINDOWS_SERVER2019_BUILD = 17763; +static const int WINDOWS_SERVER2022_BUILD = 20348; +static const int WINDOWS_SERVER2025_BUILD = 26100; +static const int FIRST_WINDOWS11_BUILD = 22000; +static OSVERSIONINFOEXW getWindowsVersionInfo() +{ + OSVERSIONINFOEXW osvi; + ::ZeroMemory(&osvi, sizeof(osvi)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); + + HMODULE hNtdll = ::GetModuleHandleW(L"ntdll.dll"); + if (hNtdll) + { + typedef LONG (WINAPI *RtlGetVersion_t)(OSVERSIONINFOEXW*); + RtlGetVersion_t pRtlGetVersion = reinterpret_cast( + ::GetProcAddress(hNtdll, "RtlGetVersion") + ); + + if (pRtlGetVersion + && pRtlGetVersion(&osvi) == 0) + { + return osvi; + } + } + + ::GetVersionExW(reinterpret_cast(&osvi)); + return osvi; +} + +static int isWindowsServer() +{ +#ifdef VER_NT_WORKSTATION + switch ( getWindowsVersionInfo().wProductType ) + { + case VER_NT_WORKSTATION: + return 0; + + case VER_NT_SERVER: + case VER_NT_DOMAIN_CONTROLLER: + return 1; + } +#endif // VER_NT_WORKSTATION + return -1; +} + +static bool isPlatform64Bit() +{ +#if defined(__WIN64__) + return true; +#else // Win32 + typedef BOOL (WINAPI *IsWow64Process_t)(HANDLE, PBOOL); + + HMODULE hKernel32 = GetModuleHandleA("kernel32.dll"); + + IsWow64Process_t pIsWow64Process = reinterpret_cast( + GetProcAddress(hKernel32, "IsWow64Process") + ); + + BOOL isWow64 = FALSE; + if (pIsWow64Process) + { + pfnIsWow64Process(::GetCurrentProcess(), &isWow64); + } + return isWow64 != FALSE; +#endif // Win64/Win32 +} + +static wxString getWinOsDescription() +{ + wxString str; + const OSVERSIONINFOEXW info = getWindowsVersionInfo(); + switch ( info.dwPlatformId ) + { + case VER_PLATFORM_WIN32_NT: + switch ( info.dwMajorVersion ) + { + case 5: + switch ( info.dwMinorVersion ) + { + case 2: + // we can't distinguish between XP 64 and 2003 + // as they both are 5.2, so examine the product + // type to resolve this ambiguity + if ( isWindowsServer() == 1 ) + { + str = "Windows Server 2003"; + break; + } + //else: must be XP, fall through + case 1: + str = "Windows XP"; + break; + } + break; + + case 6: + switch ( info.dwMinorVersion ) + { + case 0: + str = isWindowsServer() == 1 + ? "Windows Server 2008" + : "Windows Vista"; + break; + + case 1: + str = isWindowsServer() == 1 + ? "Windows Server 2008 R2" + : "Windows 7"; + break; + + case 2: + str = isWindowsServer() == 1 + ? "Windows Server 2012" + : "Windows 8"; + break; + + case 3: + str = isWindowsServer() == 1 + ? "Windows Server 2012 R2" + : "Windows 8.1"; + break; + } + break; + + case 10: + if ( isWindowsServer() == 1 ) + { + switch ( info.dwBuildNumber ) + { + case WINDOWS_SERVER2016_BUILD: + str = "Windows Server 2016"; + break; + case WINDOWS_SERVER2019_BUILD: + str = "Windows Server 2019"; + break; + case WINDOWS_SERVER2022_BUILD: + str = "Windows Server 2022"; + break; + case WINDOWS_SERVER2025_BUILD: + str = "Windows Server 2025"; + break; + } + } + else + { + str = info.dwBuildNumber >= FIRST_WINDOWS11_BUILD + ? "Windows 11" + : "Windows 10"; + } + break; + } + + if ( str.empty() ) + { + str.Printf("Windows %s%lu.%lu", + isWindowsServer() == 1 ? "Server " : "", + info.dwMajorVersion, + info.dwMinorVersion); + } + + str << wxT(" (") + << wxString::Format("build %lu", info.dwBuildNumber); + if ( !wxIsEmpty(info.szCSDVersion) ) + { + str << wxT(", ") << info.szCSDVersion; + } + str << wxT(')'); + + if ( isPlatform64Bit() ) + str << wxT(", 64-bit edition"); + break; + } + + return str; +} +} +#endif // __WINDOWS__ + wxDECLARE_EVENT(EVT_UPDATE_RESULT, wxCommandEvent); wxDEFINE_EVENT(EVT_UPDATE_RESULT, wxCommandEvent); @@ -336,10 +522,16 @@ void NetworkTestDialog::init_bind() wxString NetworkTestDialog::get_os_info() { + wxString text_sys_version; int major = 0, minor = 0, micro = 0; wxGetOsVersion(&major, &minor, µ); std::string os_version = (boost::format("%1%.%2%.%3%") % major % minor % micro).str(); - wxString text_sys_version = wxGetOsDescription() + wxString::Format("%d.%d.%d", major, minor, micro); +#ifdef __WINDOWS__ + text_sys_version = WindowsUtils::getWinOsDescription(); +#else + text_sys_version = wxGetOsDescription(); +#endif + text_sys_version += wxString::Format(" %d.%d.%d", major, minor, micro); return text_sys_version; } diff --git a/src/slic3r/GUI/ObjColorDialog.cpp b/src/slic3r/GUI/ObjColorDialog.cpp index 4ff59c5..aa14475 100644 --- a/src/slic3r/GUI/ObjColorDialog.cpp +++ b/src/slic3r/GUI/ObjColorDialog.cpp @@ -847,7 +847,27 @@ void ObjColorPanel::deal_algo(char cluster_number, bool redraw_ui) } wxBusyCursor cursor; m_last_cluster_number = cluster_number; - obj_color_deal_algo(m_input_colors, m_cluster_colors_from_algo, m_cluster_labels_from_algo, cluster_number,g_max_color); + if (m_obj_in_out.first_time_using_makerlab && m_obj_in_out.mtl_colors.size() <= g_max_color) { + m_obj_in_out.first_time_using_makerlab = false; + m_cluster_colors_from_algo = m_obj_in_out.mtl_colors; + m_cluster_labels_from_algo.clear(); + m_cluster_labels_from_algo.reserve(m_input_colors.size()); + for (int i = 0; i < m_input_colors.size(); i++) { + bool can_find = false; + for (int j = 0; j < m_cluster_colors_from_algo.size(); j++) { + if (Slic3r::color_is_equal(m_input_colors[i],m_cluster_colors_from_algo[j])) { + m_cluster_labels_from_algo.emplace_back(j); + can_find = true; + } + } + if (!can_find) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " error,not matched: " << i; + m_cluster_labels_from_algo.emplace_back(0); + } + } + } else { + obj_color_deal_algo(m_input_colors, m_cluster_colors_from_algo, m_cluster_labels_from_algo, cluster_number, g_max_color); + } m_cluster_colours.clear(); m_cluster_colours.reserve(m_cluster_colors_from_algo.size()); diff --git a/src/slic3r/GUI/PartPlate.cpp b/src/slic3r/GUI/PartPlate.cpp index c51ba6b..29f522d 100644 --- a/src/slic3r/GUI/PartPlate.cpp +++ b/src/slic3r/GUI/PartPlate.cpp @@ -283,6 +283,17 @@ FilamentMapMode PartPlate::get_real_filament_map_mode(const DynamicConfig& g_con return g_mode; } +std::vector PartPlate::get_real_filament_volume_maps(const DynamicConfig& g_config, bool* use_global_param) const +{ + auto maps = get_filament_volume_maps(); + if (!maps.empty()) { + if (use_global_param) { *use_global_param = false; } + return maps; + } + auto g_maps = g_config.option("filament_volume_map")->values; + if (use_global_param) { *use_global_param = true; } + return g_maps; +} bool PartPlate::has_spiral_mode_config() const { @@ -705,6 +716,21 @@ void PartPlate::render_icon_texture(GLModel &icon, GLTexture &texture) glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); } +void PartPlate::show_tooltip(const std::string tooltip) +{ + const auto scale = m_plater->get_current_canvas3D()->get_scale(); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, {6 * scale, 3 * scale}); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, {3 * scale}); + ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BACKGROUND); + ImGui::PushStyleColor(ImGuiCol_Border, {0, 0, 0, 0}); + ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.00f, 1.00f, 1.00f, 1.00f)); + ImGui::BeginTooltip(); + ImGui::TextUnformatted(tooltip.c_str()); + ImGui::EndTooltip(); + ImGui::PopStyleColor(3); + ImGui::PopStyleVar(2); +} + void PartPlate::render_plate_name_texture() { if (m_name_change) { @@ -726,18 +752,24 @@ void PartPlate::render_plate_name_texture() void PartPlate::render_icons(bool bottom, bool only_body, int hover_id, bool render_name_edit_icon) { if (!only_body) { - if (hover_id == 1) + if (hover_id == 1) { render_icon_texture(m_partplate_list->m_del_icon, m_partplate_list->m_del_hovered_texture); + show_tooltip(_u8L("Remove current plate (if not last one)")); + } else render_icon_texture(m_partplate_list->m_del_icon, m_partplate_list->m_del_texture); - if (hover_id == 2) + if (hover_id == 2){ render_icon_texture(m_partplate_list->m_orient_icon, m_partplate_list->m_orient_hovered_texture); + show_tooltip(_u8L("Auto orient objects on current plate")); + } else render_icon_texture(m_partplate_list->m_orient_icon, m_partplate_list->m_orient_texture); - if (hover_id == 3) + if (hover_id == 3){ render_icon_texture(m_partplate_list->m_arrange_icon, m_partplate_list->m_arrange_hovered_texture); + show_tooltip(_u8L("Arrange objects on current plate")); + } else render_icon_texture(m_partplate_list->m_arrange_icon, m_partplate_list->m_arrange_texture); @@ -746,6 +778,7 @@ void PartPlate::render_icons(bool bottom, bool only_body, int hover_id, bool ren render_icon_texture(m_partplate_list->m_lock_icon, m_partplate_list->m_locked_hovered_texture); else render_icon_texture(m_partplate_list->m_lock_icon, m_partplate_list->m_lockopen_hovered_texture); + show_tooltip(_u8L("Unlock current plate")); } else { if (this->is_locked()) render_icon_texture(m_partplate_list->m_lock_icon, m_partplate_list->m_locked_texture); @@ -755,18 +788,23 @@ void PartPlate::render_icons(bool bottom, bool only_body, int hover_id, bool ren int extruder_count = wxGetApp().preset_bundle->get_printer_extruder_count(); if (extruder_count == 2) { - if (hover_id == PLATE_FILAMENT_MAP_ID) + if (hover_id == PLATE_FILAMENT_MAP_ID){ render_icon_texture(m_partplate_list->m_plate_filament_map_icon, m_partplate_list->m_plate_set_filament_map_hovered_texture); + show_tooltip(_u8L("Edit filament grouping")); + } else render_icon_texture(m_partplate_list->m_plate_filament_map_icon, m_partplate_list->m_plate_set_filament_map_texture); + m_partplate_list->m_plate_filament_map_icon.set_visible(true); } else { m_partplate_list->m_plate_filament_map_icon.set_visible(false); } if (render_name_edit_icon) { - if (hover_id == PLATE_NAME_ID) + if (hover_id == PLATE_NAME_ID) { render_icon_texture(m_plate_name_edit_icon, m_partplate_list->m_plate_name_edit_hovered_texture); + show_tooltip(_u8L("Edit current plate name")); + } else render_icon_texture(m_plate_name_edit_icon, m_partplate_list->m_plate_name_edit_texture); } @@ -779,6 +817,7 @@ void PartPlate::render_icons(bool bottom, bool only_body, int hover_id, bool ren render_icon_texture(m_partplate_list->m_plate_settings_icon, m_partplate_list->m_plate_settings_hovered_texture); else render_icon_texture(m_partplate_list->m_plate_settings_icon, m_partplate_list->m_plate_settings_changed_hovered_texture); + show_tooltip(_u8L("Customize current plate")); } else { if (!has_plate_settings) render_icon_texture(m_partplate_list->m_plate_settings_icon, m_partplate_list->m_plate_settings_texture); @@ -1049,7 +1088,8 @@ void PartPlate::on_render_for_picking() { std::vector gl_models = {&m_partplate_list->m_triangles, &m_partplate_list->m_del_icon, &m_partplate_list->m_orient_icon, &m_partplate_list->m_arrange_icon, &m_partplate_list->m_lock_icon, &m_partplate_list->m_plate_settings_icon, - &m_partplate_list->m_plate_filament_map_icon, &m_plate_name_edit_icon}; + &m_partplate_list->m_plate_filament_map_icon,//some case not show + &m_plate_name_edit_icon}; for (size_t i = 0; i < gl_models.size(); i++) { if (!gl_models[i]->get_visible()) { continue; @@ -1091,6 +1131,9 @@ std::vector PartPlate::get_extruders(bool conside_custom_gcode) const const DynamicPrintConfig& glb_config = wxGetApp().preset_bundle->prints.get_edited_preset().config; int glb_support_intf_extr = glb_config.opt_int("support_interface_filament"); int glb_support_extr = glb_config.opt_int("support_filament"); + int glb_wall_extr = glb_config.opt_int("wall_filament"); + int glb_sparse_infill_extr = glb_config.opt_int("sparse_infill_filament"); + int glb_solid_infill_extr = glb_config.opt_int("solid_infill_filament"); bool glb_support = glb_config.opt_bool("enable_support"); glb_support |= glb_config.opt_int("raft_layers") > 0; @@ -1112,6 +1155,46 @@ std::vector PartPlate::get_extruders(bool conside_custom_gcode) const } } + // filaments for features + for (ModelVolume *mv : mo->volumes) { // go throught all volumes + ModelConfigObject &mv_config = mv->config; + int obj_wall_extr = 0, vol_wall_extr = 0; + const ConfigOption *wall_opt = mo->config.option("wall_filament"); + if (wall_opt != nullptr) obj_wall_extr = wall_opt->getInt(); + const ConfigOption *vol_wall_opt = mv_config.option("wall_filament"); + if (vol_wall_opt != nullptr) vol_wall_extr = vol_wall_opt->getInt(); + if (vol_wall_extr != 0) + plate_extruders.push_back(vol_wall_extr); + else if (obj_wall_extr != 0) + plate_extruders.push_back(obj_wall_extr); + else if (glb_wall_extr != 0) + plate_extruders.push_back(glb_wall_extr); + + int obj_sparse_infill_extr = 0, vol_spare_infill_extr = 0; + const ConfigOption *sparse_infill_opt = mo->config.option("sparse_infill_filament"); + if (sparse_infill_opt != nullptr) obj_sparse_infill_extr = sparse_infill_opt->getInt(); + const ConfigOption *vol_infill_opt = mv_config.option("sparse_infill_filament"); + if (vol_infill_opt != nullptr) vol_spare_infill_extr = vol_infill_opt->getInt(); + if (vol_spare_infill_extr != 0) + plate_extruders.push_back(vol_spare_infill_extr); + else if (obj_sparse_infill_extr != 0) + plate_extruders.push_back(obj_sparse_infill_extr); + else if (glb_sparse_infill_extr != 0) + plate_extruders.push_back(glb_sparse_infill_extr); + + int obj_solid_infill_extr = 0, vol_solid_infill_extr = 0; + const ConfigOption *solid_infill_opt = mo->config.option("solid_infill_filament"); + if (solid_infill_opt != nullptr) obj_solid_infill_extr = solid_infill_opt->getInt(); + const ConfigOption *vol_solid_infill_opt = mv_config.option("solid_infill_filament"); + if (vol_solid_infill_opt != nullptr) vol_solid_infill_extr = vol_solid_infill_opt->getInt(); + if (vol_solid_infill_extr != 0) + plate_extruders.push_back(vol_solid_infill_extr); + else if (obj_solid_infill_extr != 0) + plate_extruders.push_back(obj_solid_infill_extr); + else if (glb_solid_infill_extr != 0) + plate_extruders.push_back(glb_solid_infill_extr); + } + bool obj_support = false; const ConfigOption* obj_support_opt = mo->config.option("enable_support"); const ConfigOption *obj_raft_opt = mo->config.option("raft_layers"); @@ -1172,7 +1255,10 @@ std::vector PartPlate::get_extruders_under_cli(bool conside_custom_gcode, D // if 3mf file int glb_support_intf_extr = full_config.opt_int("support_interface_filament"); - int glb_support_extr = full_config.opt_int("support_filament"); + int glb_support_extr = full_config.opt_int("support_filament"); + int glb_wall_extr = full_config.opt_int("wall_filament"); + int glb_sparse_infill_extr = full_config.opt_int("sparse_infill_filament"); + int glb_solid_infill_extr = full_config.opt_int("solid_infill_filament"); bool glb_support = full_config.opt_bool("enable_support"); glb_support |= full_config.opt_int("raft_layers") > 0; @@ -1202,6 +1288,53 @@ std::vector PartPlate::get_extruders_under_cli(bool conside_custom_gcode, D } } + // filaments for features + bool enabled_SFFF_mo = object->config.option("separate_filaments_for_features") && object->config.option("separate_filaments_for_features")->getBool(), + mo_not_override = object->config.option("separate_filaments_for_features") == nullptr; + for (ModelVolume *mv : object->volumes) { // go throught all volumes + ModelConfigObject &mv_config = mv->config; + // volume not overrided,or overrided with enabled + bool enabled_SFFF_mv = mv_config.option("separate_filaments_for_features") && mv_config.option("separate_filaments_for_features")->getBool(), + mv_not_override = mv_config.option("separate_filaments_for_features") == nullptr; + if (enabled_SFFF_mv || (mv_not_override && enabled_SFFF_mo)) { + int obj_wall_extr = 0, vol_wall_extr = 0; + const ConfigOption *wall_opt = object->config.option("wall_filament"); + if (wall_opt != nullptr) obj_wall_extr = wall_opt->getInt(); + const ConfigOption *vol_wall_opt = mv_config.option("wall_filament"); + if (vol_wall_opt != nullptr) vol_wall_extr = vol_wall_opt->getInt(); + if (vol_wall_extr != 0) + plate_extruders.push_back(vol_wall_extr); + else if (obj_wall_extr != 0 && (mv_not_override && enabled_SFFF_mo)) + plate_extruders.push_back(obj_wall_extr); + else if (glb_wall_extr != 0 && (mv_not_override && mo_not_override && glb_sparse_infill_extr)) + plate_extruders.push_back(glb_wall_extr); + + int obj_sparse_infill_extr = 0, vol_spare_infill_extr = 0; + const ConfigOption *sparse_infill_opt = object->config.option("sparse_infill_filament"); + if (sparse_infill_opt != nullptr) obj_sparse_infill_extr = sparse_infill_opt->getInt(); + const ConfigOption *vol_infill_opt = mv_config.option("sparse_infill_filament"); + if (vol_infill_opt != nullptr) vol_spare_infill_extr = vol_infill_opt->getInt(); + if (vol_spare_infill_extr != 0) + plate_extruders.push_back(vol_spare_infill_extr); + else if (obj_sparse_infill_extr != 0 && (mv_not_override && enabled_SFFF_mo)) + plate_extruders.push_back(obj_sparse_infill_extr); + else if (glb_sparse_infill_extr != 0 && (mv_not_override && mo_not_override && glb_sparse_infill_extr)) + plate_extruders.push_back(glb_sparse_infill_extr); + + int obj_solid_infill_extr = 0, vol_solid_infill_extr = 0; + const ConfigOption *solid_infill_opt = object->config.option("solid_infill_filament"); + if (solid_infill_opt != nullptr) obj_solid_infill_extr = solid_infill_opt->getInt(); + const ConfigOption *vol_solid_infill_opt = mv_config.option("solid_infill_filament"); + if (vol_solid_infill_opt != nullptr) vol_solid_infill_extr = vol_solid_infill_opt->getInt(); + if (vol_solid_infill_extr != 0) + plate_extruders.push_back(vol_solid_infill_extr); + else if (obj_solid_infill_extr != 0 && (mv_not_override && enabled_SFFF_mo)) + plate_extruders.push_back(obj_solid_infill_extr); + else if (glb_solid_infill_extr != 0 && (mv_not_override && mo_not_override && glb_sparse_infill_extr)) + plate_extruders.push_back(glb_solid_infill_extr); + } + } + bool obj_support = false; const ConfigOption* obj_support_opt = object->config.option("enable_support"); const ConfigOption *obj_raft_opt = object->config.option("raft_layers"); @@ -1313,12 +1446,23 @@ std::vector PartPlate::get_extruders_without_support(bool conside_custom_gc /* -1 is invalid, return physical extruder idx*/ /* machine has 1 extruder*/ -/* logical extruder: 1-unique*/ +/* logical extruder: 0-unique*/ /* physical extruder: 0-unique*/ /* machine have 2 extruders*/ -/* logical extruder: 1-left, 2-right*/ +/* logical extruder: 0-left, 1-right*/ /* physical extruder: 0-right, 1-left*/ +int PartPlate::get_physical_extruder_by_logical_extruder(const DynamicConfig& g_config, int logical_extruder) const +{ + const auto the_map = g_config.option("physical_extruder_map"); + if (!the_map) { + return -1; + } + + return the_map->values[logical_extruder]; +} + +/* logical extruder: 0-default 1-left, 2-right in filament_map*/ int PartPlate::get_physical_extruder_by_filament_id(const DynamicConfig& g_config, int idx) const { const std::vector& filament_map = get_real_filament_maps(g_config); @@ -1337,6 +1481,17 @@ int PartPlate::get_physical_extruder_by_filament_id(const DynamicConfig& g_confi return the_map->values[zero_base_logical_idx]; } +int PartPlate::get_logical_extruder_by_filament_id(const DynamicConfig& g_config, int idx) const +{ + const std::vector& filament_map = get_real_filament_maps(g_config); + if (filament_map.size() < idx) { + return -1; + } + + return filament_map[idx - 1] - 1; +} + + std::vector PartPlate::get_used_filaments() { std::vector used_filaments; @@ -1431,7 +1586,11 @@ bool PartPlate::check_mixture_filament_compatible(const DynamicPrintConfig &conf auto filament_type_opt = config.option("filament_type"); for (auto filament : used_filaments) { int filament_idx = filament - 1; - filament_types.push_back(filament_type_opt->values[filament_idx]); + if (filament_idx >= 0 && filament_type_opt && filament_idx < filament_type_opt->values.size()) { + filament_types.push_back(filament_type_opt->values[filament_idx]); + } else { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "todo:check this case" ; + } }; { @@ -1657,6 +1816,10 @@ arrangement::ArrangePolygon PartPlate::estimate_wipe_tower_polygon(const Dynamic float w = dynamic_cast(config.option("prime_tower_width"))->value; //float a = dynamic_cast(config.option("wipe_tower_rotation_angle"))->value; std::vector v = dynamic_cast(config.option("filament_prime_volume"))->values; + if (config.option>("prime_volume_mode")->value == pvmSaving) { + for (auto& val : v) + val = 15.f; + } const ConfigOptionBool * wrapping_opt = dynamic_cast(config.option("enable_wrapping_detection")); bool enable_wrapping = (wrapping_opt != nullptr) && wrapping_opt->value; wt_size = estimate_wipe_tower_size(config, w, get_max_element(v), extruder_count, plate_extruder_size, use_global_objects, enable_wrapping); @@ -2866,7 +3029,11 @@ int PartPlate::load_gcode_from_file(const std::string& filename) auto& preset_bundle = wxGetApp().preset_bundle; // process gcode std::vector filament_maps = this->get_real_filament_maps(preset_bundle->project_config); - DynamicPrintConfig full_config = wxGetApp().preset_bundle->full_config(false, filament_maps); + std::vector f_volume_maps = this->get_filament_volume_maps(); + if (f_volume_maps.empty()) { + f_volume_maps = preset_bundle->get_default_nozzle_volume_types_for_filaments(filament_maps); + } + DynamicPrintConfig full_config = preset_bundle->full_config(false, filament_maps, f_volume_maps); full_config.apply(m_config, true); m_print->apply(*m_model, full_config, false); //QDS: need to apply two times, for after the first apply, the m_print got its object, @@ -3178,8 +3345,11 @@ void PartPlate::set_filament_map_mode(const FilamentMapMode& mode) FilamentMapMode old_real_mode = old_mode == fmmDefault ? global_mode : old_mode; FilamentMapMode new_real_mode = mode == fmmDefault ? global_mode : mode; - if (old_real_mode != new_real_mode) + if (old_real_mode != new_real_mode){ clear_filament_map(); + clear_filament_nozzle_map(); + clear_filament_volume_map(); + } if (mode == fmmDefault) clear_filament_map_mode(); else @@ -3200,6 +3370,42 @@ void PartPlate::set_filament_maps(const std::vector& f_maps) m_config.option("filament_map", true)->values = f_maps; } +std::vector PartPlate::get_filament_nozzle_maps() const +{ + if(m_config.has("filament_nozzle_map")) + return m_config.option("filament_nozzle_map")->values; + return {}; +} + +void PartPlate::set_filament_nozzle_maps(const std::vector& f_maps) +{ + m_config.option("filament_nozzle_map", true)->values = f_maps; +} + +std::vector PartPlate::get_filament_volume_maps() const +{ + if(m_config.has("filament_volume_map")) + return m_config.option("filament_volume_map")->values; + return {}; +} + +void PartPlate::set_filament_volume_maps(const std::vector& f_maps) +{ + m_config.option("filament_volume_map", true)->values = f_maps; +} + +void PartPlate::clear_filament_nozzle_map() +{ + if(m_config.has("filament_nozzle_map")) + m_config.erase("filament_nozzle_map"); +} + +void PartPlate::clear_filament_volume_map() +{ + if(m_config.has("filament_volume_map")) + m_config.erase("filament_volume_map"); +} + void PartPlate::clear_filament_map() { if (m_config.has("filament_map")) @@ -3220,6 +3426,8 @@ void PartPlate::on_extruder_count_changed(int extruder_count) wxGetApp().plater()->set_global_filament_map(f_map); // clear filament map and mode in single extruder mode clear_filament_map(); + clear_filament_nozzle_map(); + clear_filament_volume_map(); //clear_filament_map_mode(); // do not clear mode now, reset to default mode m_config.option>("filament_map_mode", true)->value = FilamentMapMode::fmmAutoForFlush; @@ -3232,6 +3440,16 @@ void PartPlate::set_filament_count(int filament_count) std::vector& filament_maps = m_config.option("filament_map")->values; filament_maps.resize(filament_count, 1); } + + if(m_config.has("filament_nozzle_map")){ + std::vector& filament_nozzle_map = m_config.option("filament_nozzle_map")->values; + filament_nozzle_map.resize(filament_count, 0); + } + if (m_config.has("filament_volume_map")) { + std::vector& filament_volume_map = m_config.option("filament_volume_map")->values; + filament_volume_map.resize(filament_count, static_cast(NozzleVolumeType::nvtStandard)); + } + } void PartPlate::on_filament_added() @@ -3240,6 +3458,21 @@ void PartPlate::on_filament_added() std::vector& filament_maps = m_config.option("filament_map")->values; filament_maps.push_back(1); } + + if(m_config.has("filament_nozzle_map")){ + std::vector& filament_nozzle_map = m_config.option("filament_nozzle_map")->values; + filament_nozzle_map.push_back(0); + } + + if(m_config.has("filament_volume_map")){ + std::vector& filament_volume_map = m_config.option("filament_volume_map")->values; + int volume_type = 0; + auto nozzle_volumes = wxGetApp().preset_bundle->project_config.option("nozzle_volume_type"); + if (nozzle_volumes && !nozzle_volumes->values.empty()) + volume_type = nozzle_volumes->values[0]; + + filament_volume_map.push_back(volume_type); + } } void PartPlate::on_filament_deleted(int filament_count, int filament_id) @@ -3248,10 +3481,20 @@ void PartPlate::on_filament_deleted(int filament_count, int filament_id) std::vector& filament_maps = m_config.option("filament_map")->values; filament_maps.erase(filament_maps.begin() + filament_id); } + + if(m_config.has("filament_nozzle_map")){ + std::vector& filament_nozzle_map = m_config.option("filament_nozzle_map")->values; + filament_nozzle_map.erase(filament_nozzle_map.begin() + filament_id); + } + + if(m_config.has("filament_volume_map")){ + std::vector& filament_volume_map = m_config.option("filament_volume_map")->values; + filament_volume_map.erase(filament_volume_map.begin() + filament_id); + } + update_first_layer_print_sequence_when_delete_filament(filament_id); } - /* PartPlate List related functions*/ PartPlateList::PartPlateList(int width, int depth, int height, Plater* platerObj, Model* modelObj, PrinterTechnology tech) :m_plate_width(width), m_plate_depth(depth), m_plate_height(height), m_plater(platerObj), m_model(modelObj), printer_technology(tech), @@ -3902,10 +4145,18 @@ void PartPlateList::set_default_wipe_tower_pos_for_plate(int plate_idx, bool ini coordf_t plate_bbox_y_max_local_coord = plate_bbox_2d.max(1) - plate_origin(1); std::vector filament_maps = part_plate->get_real_filament_maps(proj_cfg); - DynamicPrintConfig full_config = wxGetApp().preset_bundle->full_config(false, filament_maps); + std::vector f_volume_maps = part_plate->get_filament_volume_maps(); + if (f_volume_maps.empty()) { + f_volume_maps = wxGetApp().preset_bundle->get_default_nozzle_volume_types_for_filaments(filament_maps); + } + DynamicPrintConfig full_config = wxGetApp().preset_bundle->full_config(false, filament_maps, f_volume_maps); const DynamicPrintConfig &print_cfg = wxGetApp().preset_bundle->prints.get_edited_preset().config; float w = dynamic_cast(print_cfg.option("prime_tower_width"))->value; std::vector v = dynamic_cast(full_config.option("filament_prime_volume"))->values; + if (full_config.option>("prime_volume_mode")->value == pvmSaving) { + for (auto& val : v) + val = 15.f; + } bool enable_wrapping = dynamic_cast(full_config.option("enable_wrapping_detection"))->value; int nozzle_nums = wxGetApp().preset_bundle->get_printer_extruder_count(); double wipe_vol = get_max_element(v); @@ -3931,9 +4182,9 @@ void PartPlateList::set_default_wipe_tower_pos_for_plate(int plate_idx, bool ini } } - //y74 - if( x < plate_bbox_x_max_local_coord * 0.8) - x = plate_bbox_x_max_local_coord * 0.8; + //y74 y75 + if( x < plate_bbox_x_max_local_coord * 0.6) + x = plate_bbox_x_max_local_coord * 0.6; if ( y < plate_bbox_y_max_local_coord * 0.8) y = plate_bbox_y_max_local_coord * 0.8; @@ -4062,6 +4313,13 @@ void PartPlateList::set_bed3d(Bed3D *_bed3d) { m_bed3d = _bed3d; } +void PartPlateList::update_plates() +{ + update_all_plates_pos_and_size(true, false); + //set_shapes(m_shape, m_exclude_areas, m_logo_texture_filename, m_height_to_lid, m_height_to_rod); + set_shapes(m_shape, m_exclude_areas, m_wrapping_exclude_areas, m_extruder_areas, m_extruder_heights, m_logo_texture_filename, m_height_to_lid, m_height_to_rod); +} + /*basic plate operations*/ //create an empty plate, and return its index //these model instances which are not in any plates should not be affected also @@ -6094,6 +6352,7 @@ int PartPlateList::store_to_3mf_structure(PlateDataPtrs& plate_data_list, bool w plate_data_item->limit_filament_maps = m_plate_list[i]->m_gcode_result->limit_filament_maps; plate_data_item->layer_filaments = m_plate_list[i]->m_gcode_result->layer_filaments; plate_data_item->first_layer_time = std::to_string(m_plate_list[i]->cali_bboxes_data.first_layer_time); + plate_data_item->filament_change_sequence = m_plate_list[i]->m_gcode_result->filament_change_sequence; Print *print = nullptr; m_plate_list[i]->get_print((PrintBase **) &print, nullptr, nullptr); if (print) { @@ -6158,6 +6417,7 @@ int PartPlateList::load_from_3mf_structure(PlateDataPtrs& plate_data_list, int f m_plate_list[index]->get_print(&fff_print, &gcode_result, nullptr); PrintStatistics& ps = (dynamic_cast(fff_print))->print_statistics(); gcode_result->print_statistics.modes[static_cast(PrintEstimatedStatistics::ETimeMode::Normal)].time = atoi(plate_data_list[i]->gcode_prediction.c_str()); + gcode_result->nozzle_group_result = MultiNozzleUtils::MultiNozzleGroupResult::init_from_slice_filament(plate_data_list[i]->filament_maps, plate_data_list[i]->slice_filaments_info); ps.total_weight = atof(plate_data_list[i]->gcode_weight.c_str()); ps.total_used_filament = 0.f; for (auto filament_item: plate_data_list[i]->slice_filaments_info) @@ -6168,6 +6428,7 @@ int PartPlateList::load_from_3mf_structure(PlateDataPtrs& plate_data_list, int f gcode_result->toolpath_outside = plate_data_list[i]->toolpath_outside; gcode_result->label_object_enabled = plate_data_list[i]->is_label_object_enabled; gcode_result->timelapse_warning_code = plate_data_list[i]->timelapse_warning_code; + gcode_result->filament_change_sequence = plate_data_list[i]->filament_change_sequence; m_plate_list[index]->set_timelapse_warning_code(plate_data_list[i]->timelapse_warning_code); m_plate_list[index]->slice_filaments_info = plate_data_list[i]->slice_filaments_info; gcode_result->warnings = plate_data_list[i]->warnings; diff --git a/src/slic3r/GUI/PartPlate.hpp b/src/slic3r/GUI/PartPlate.hpp index 63cd5ef..1f7d5ee 100644 --- a/src/slic3r/GUI/PartPlate.hpp +++ b/src/slic3r/GUI/PartPlate.hpp @@ -134,7 +134,6 @@ private: GLModel m_height_limit_bottom; GLModel m_height_limit_top; GLModel m_plate_name_edit_icon; - float m_scale_factor{ 1.0f }; GLUquadricObject* m_quadric; int m_hover_id; @@ -175,6 +174,7 @@ private: void render_right_arrow(const float* render_color, bool use_lighting) const; void render_icon_texture(GLModel &buffer, GLTexture &texture); + void show_tooltip(const std::string tooltip); void render_plate_name_texture(); void render_icons(bool bottom, bool only_body = false, int hover_id = -1,bool render_name_edit_icon = true); void render_plate_name_icon_and_texture(bool only_body = false, int hover_id = -1); @@ -188,8 +188,8 @@ private: public: static const unsigned int PLATE_BASE_ID = 255 * 255 * 253; - static const unsigned int PLATE_FILAMENT_MAP_ID = 6; - static const unsigned int GRABBER_COUNT = 8; + static const unsigned int GRABBER_COUNT = 8; + static const unsigned int PLATE_FILAMENT_MAP_ID = GRABBER_COUNT - 2; static const unsigned int PLATE_NAME_ID = GRABBER_COUNT-1; static std::array SELECT_COLOR; @@ -233,6 +233,7 @@ public: std::vector get_real_filament_maps(const DynamicConfig& g_config, bool* use_global_param = nullptr)const; FilamentMapMode get_real_filament_map_mode(const DynamicConfig& g_config,bool * use_global_param = nullptr) const; + std::vector get_real_filament_volume_maps(const DynamicConfig &g_config, bool *use_global_param = nullptr) const; FilamentMapMode get_filament_map_mode() const; void set_filament_map_mode(const FilamentMapMode& mode); @@ -241,8 +242,16 @@ public: std::vector get_filament_maps() const; void set_filament_maps(const std::vector& f_maps); + std::vector get_filament_nozzle_maps() const; + void set_filament_nozzle_maps(const std::vector& f_maps); + + std::vector get_filament_volume_maps() const; + void set_filament_volume_maps(const std::vector& f_maps); + void clear_filament_map(); void clear_filament_map_mode(); + void clear_filament_nozzle_map(); + void clear_filament_volume_map(); bool has_spiral_mode_config() const; bool get_spiral_vase_mode() const; @@ -318,7 +327,9 @@ public: std::vector get_extruders_without_support(bool conside_custom_gcode = false) const; // get used filaments from gcode result, 1 based idx std::vector get_used_filaments(); + int get_logical_extruder_by_filament_id(const DynamicConfig& g_config, int idx) const; int get_physical_extruder_by_filament_id(const DynamicConfig& g_config, int idx) const; + int get_physical_extruder_by_logical_extruder(const DynamicConfig& g_config, int logical_extruder) const; bool check_filament_printable(const DynamicPrintConfig & config, wxString& error_message); bool check_tpu_printable_status(const DynamicPrintConfig & config, const std::vector &tpu_filaments); bool check_mixture_of_pla_and_petg(const DynamicPrintConfig & config); @@ -755,7 +766,8 @@ public: depth = m_plate_depth; height = m_plate_height; } - + // Pantheon: update plates after moving plate to the front + void update_plates(); /*basic plate operations*/ //create an empty plate and return its index int create_plate(bool adjust_position = true); diff --git a/src/slic3r/GUI/PlateMoveDialog.cpp b/src/slic3r/GUI/PlateMoveDialog.cpp new file mode 100644 index 0000000..cf62080 --- /dev/null +++ b/src/slic3r/GUI/PlateMoveDialog.cpp @@ -0,0 +1,322 @@ +#include "PlateMoveDialog.hpp" +#include "MsgDialog.hpp" + +namespace Slic3r { namespace GUI { + +const StateColor btn_bg_green_in_plate_swap(std::pair(wxColour(0, 66, 255), StateColor::Pressed), + std::pair(wxColour(116, 168, 255), StateColor::Hovered), + std::pair(wxColour(68, 121, 251), StateColor::Normal)); +const StateColor btn_bg_disable_bg_in_plate_swap(std::pair(wxColour(205, 201, 201), StateColor::Pressed), + std::pair(wxColour(205, 201, 201), StateColor::Hovered), + std::pair(wxColour(205, 201, 201), StateColor::Normal)); +PlateMoveDialog::PlateMoveDialog(wxWindow *parent, wxWindowID id, const wxString &title, const wxPoint &pos, const wxSize &size, long style) + : DPIDialog(parent, id, title, pos, size, style) +{ + std::string icon_path = (boost::format("%1%/images/QIDIStudioTitle.ico") % resources_dir()).str(); + SetIcon(wxIcon(encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO)); + + SetBackgroundColour(*wxWHITE); + wxBoxSizer *m_sizer_main = new wxBoxSizer(wxVERTICAL); + auto m_line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(305), -1)); + m_line_top->SetBackgroundColour(wxColour(166, 169, 170)); + m_sizer_main->Add(m_line_top, 0, wxEXPAND, 0); + m_sizer_main->Add(0, 0, 0, wxTOP, FromDIP(5)); + + m_sizer_main->AddSpacer(FromDIP(15)); + + init_bitmaps(); + auto layout_sizer = new wxBoxSizer(wxHORIZONTAL); + auto content_sizer = new wxBoxSizer(wxVERTICAL); + auto text_sizer = new wxBoxSizer(wxHORIZONTAL); + auto combox_sizer = new wxBoxSizer(wxHORIZONTAL); + { + wxStaticText *temp_title = new wxStaticText(this, wxID_ANY, _L("Move the current plate to")); + text_sizer->Add(temp_title, 0, wxEXPAND | wxALL, FromDIP(5)); + } + {// icon_name + m_swipe_left_button = new ScalableButton(this, wxID_ANY, wxGetApp().dark_mode() ? "previous_normal_dark" : "previous_normal", wxEmptyString, wxDefaultSize, wxDefaultPosition, + wxBU_EXACTFIT | wxNO_BORDER, true, m_bmp_pix_cont); + m_swipe_left_button->SetToolTip(_L("Move forward")); + m_swipe_left_button->Bind(wxEVT_ENTER_WINDOW, [this](auto &e) { + if (!m_swipe_left_button_enable) { return; } + m_swipe_left_button->SetBitmap(m_swipe_left_bmp_hover.bmp()); + }); + m_swipe_left_button->Bind(wxEVT_LEAVE_WINDOW, [this](auto &e) { + if (!m_swipe_left_button_enable) { return; } + m_swipe_left_button->SetBitmap(m_swipe_left_bmp_normal.bmp()); + }); + m_swipe_left_button->Bind(wxEVT_BUTTON, &PlateMoveDialog::on_previous_plate, this); + } + { + m_swipe_right_button = new ScalableButton(this, wxID_ANY, wxGetApp().dark_mode() ? "next_normal_dark" : "next_normal", wxEmptyString, wxDefaultSize, wxDefaultPosition, + wxBU_EXACTFIT | wxNO_BORDER, true, m_bmp_pix_cont); + m_swipe_right_button->SetToolTip(_L("Move backwards")); + m_swipe_right_button->Bind(wxEVT_ENTER_WINDOW, [this](auto &e) { + if (!m_swipe_right_button_enable) { return; } + m_swipe_right_button->SetBitmap(m_swipe_right_bmp_hover.bmp()); + }); + m_swipe_right_button->Bind(wxEVT_LEAVE_WINDOW, [this](auto &e) { + if (!m_swipe_right_button_enable) { return; } + m_swipe_right_button->SetBitmap(m_swipe_right_bmp_normal.bmp()); + }); + m_swipe_right_button->Bind(wxEVT_BUTTON, &PlateMoveDialog::on_next_plate, this); + } + { + m_swipe_frontmost_button = new ScalableButton(this, wxID_ANY, wxGetApp().dark_mode() ? "frontmost_normal_dark" : "frontmost_normal", wxEmptyString, wxDefaultSize, + wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, true, m_bmp_pix_cont); + m_swipe_frontmost_button->SetToolTip(_L("Move to the front")); + m_swipe_frontmost_button->Bind(wxEVT_ENTER_WINDOW, [this](auto &e) { + if (!m_swipe_frontmost_button_enable) { return; } + m_swipe_frontmost_button->SetBitmap(m_swipe_frontmost_bmp_hover.bmp()); + }); + m_swipe_frontmost_button->Bind(wxEVT_LEAVE_WINDOW, [this](auto &e) { + if (!m_swipe_frontmost_button_enable) { return; } + m_swipe_frontmost_button->SetBitmap(m_swipe_frontmost_bmp_normal.bmp()); + }); + m_swipe_frontmost_button->Bind(wxEVT_BUTTON, &PlateMoveDialog::on_frontmost_plate, this); + } + { + m_swipe_backmost_button = new ScalableButton(this, wxID_ANY, wxGetApp().dark_mode() ? "backmost_normal_dark":"backmost_normal", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, true,m_bmp_pix_cont); + m_swipe_backmost_button->SetToolTip(_L("Move to the back")); + m_swipe_backmost_button->Bind(wxEVT_ENTER_WINDOW, [this](auto &e) { + if (!m_swipe_backmost_button_enable) { return; } + m_swipe_backmost_button->SetBitmap(m_swipe_backmost_bmp_hover.bmp()); + }); + m_swipe_backmost_button->Bind(wxEVT_LEAVE_WINDOW, [this](auto &e) { + if (!m_swipe_backmost_button_enable) { return; } + m_swipe_backmost_button->SetBitmap(m_swipe_backmost_bmp_normal.bmp()); + }); + m_swipe_backmost_button->Bind(wxEVT_BUTTON, &PlateMoveDialog::on_backmost_plate, this); + } + + GUI::PartPlateList &plate_list = wxGetApp().plater()->get_partplate_list(); + GUI::PartPlate * curr_plate = GUI::wxGetApp().plater()->get_partplate_list().get_selected_plate(); + m_specify_plate_idx = GUI::wxGetApp().plater()->get_partplate_list().get_curr_plate_index(); + for (size_t i = 0; i < plate_list.get_plate_count(); i++) { + if (i < 9) { + m_plate_number_choices_str.Add("0" + std::to_wstring(i + 1)); + } else if (i == 9) { + m_plate_number_choices_str.Add("10"); + } else { + m_plate_number_choices_str.Add(std::to_wstring(i + 1)); + } + m_plate_choices.emplace_back(i); + } + + m_combobox_plate = new ComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(FromDIP(100), -1), 0, NULL, wxCB_READONLY); + m_combobox_plate->Bind(wxEVT_COMBOBOX, [this](auto &e) { + if (e.GetSelection() < m_plate_choices.size()) { + m_specify_plate_idx = e.GetSelection(); + update_swipe_button_state(); + } + }); + combox_sizer->Add(m_swipe_frontmost_button, 0, wxALIGN_LEFT | wxEXPAND | wxALIGN_CENTER_VERTICAL | wxALL, FromDIP(5)); + combox_sizer->Add(m_swipe_left_button, 0, wxALIGN_LEFT | wxEXPAND | wxALIGN_CENTER_VERTICAL | wxALL, FromDIP(5)); + combox_sizer->Add(m_combobox_plate, 0, wxALIGN_LEFT | wxEXPAND | wxALIGN_CENTER_VERTICAL | wxALL, FromDIP(5)); + combox_sizer->Add(m_swipe_right_button, 0, wxALIGN_LEFT | wxEXPAND | wxALIGN_CENTER_VERTICAL | wxALL, FromDIP(5)); + combox_sizer->Add(m_swipe_backmost_button, 0, wxALIGN_LEFT | wxEXPAND | wxALIGN_CENTER_VERTICAL | wxALL, FromDIP(5)); + + content_sizer->Add(text_sizer, 0, wxEXPAND | wxLEFT, FromDIP(0)); + content_sizer->Add(combox_sizer, 0, wxEXPAND | wxLEFT, FromDIP(0)); + + layout_sizer->AddStretchSpacer(); + layout_sizer->Add(content_sizer, 0, wxEXPAND | wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(0)); + layout_sizer->AddStretchSpacer(); + m_sizer_main->Add(layout_sizer, 0, wxEXPAND); + m_sizer_main->AddSpacer(FromDIP(10)); + + auto sizer_button = new wxBoxSizer(wxHORIZONTAL); + + StateColor btn_bg_white(std::pair(wxColour(0, 66, 255), StateColor::Pressed), std::pair(wxColour(116, 168, 255), StateColor::Hovered), + std::pair(*wxWHITE, StateColor::Normal)); + + m_button_ok = new Button(this, _L("OK")); + //m_button_ok->SetBackgroundColor(btn_bg_green_in_plate_swap); + m_button_ok->SetBorderColor(*wxWHITE); + m_button_ok->SetTextColor(wxColour("#FFFFFE")); + m_button_ok->SetFont(Label::Body_12); + m_button_ok->SetSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_ok->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_ok->SetCornerRadius(FromDIP(12)); + m_button_ok->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [this](wxCommandEvent &e) { + if (this->IsModal()) + EndModal(wxID_YES); + else + this->Close(); + }); + + m_button_cancel = new Button(this, _L("Cancel")); + m_button_cancel->SetBackgroundColor(btn_bg_white); + m_button_cancel->SetBorderColor(wxColour(38, 46, 48)); + m_button_cancel->SetFont(Label::Body_12); + m_button_cancel->SetSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_cancel->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_cancel->SetCornerRadius(FromDIP(12)); + m_button_cancel->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [this](wxCommandEvent &e) { + if (this->IsModal()) + EndModal(wxID_NO); + else + this->Close(); + }); + + sizer_button->AddStretchSpacer(); + sizer_button->Add(m_button_ok, 0, wxALL, FromDIP(5)); + sizer_button->Add(m_button_cancel, 0, wxALL, FromDIP(5)); + sizer_button->Add(FromDIP(15), 0, 0, 0); + + m_sizer_main->AddSpacer(FromDIP(15)); + m_sizer_main->Add(sizer_button, 0, wxEXPAND, FromDIP(20)); + m_sizer_main->AddSpacer(FromDIP(15)); + + SetSizer(m_sizer_main); + //update ui + update_plate_combox(); + update_swipe_button_state(); + + m_swipe_left_button->SetBitmapDisabled_(m_swipe_left_bmp_disable); + m_swipe_right_button->SetBitmapDisabled_(m_swipe_right_bmp_disable); + m_swipe_frontmost_button->SetBitmapDisabled_(m_swipe_frontmost_bmp_disable); + m_swipe_backmost_button->SetBitmapDisabled_(m_swipe_backmost_bmp_disable); + + Layout(); + Fit(); + //m_sizer_main->Fit(this); + + CenterOnParent(); + + wxGetApp().UpdateDlgDarkUI(this); +} + +PlateMoveDialog::~PlateMoveDialog() { +} + +void PlateMoveDialog::update_ok_button_enable() { + auto disable = m_specify_plate_idx == GUI::wxGetApp().plater()->get_partplate_list().get_curr_plate_index(); + m_button_ok->Enable(!disable); + m_button_ok->SetBackgroundColor(disable ? btn_bg_disable_bg_in_plate_swap :btn_bg_green_in_plate_swap); +} + +void PlateMoveDialog::on_dpi_changed(const wxRect &suggested_rect) +{ + m_button_ok->Rescale(); + m_button_cancel->Rescale(); + m_swipe_left_button->msw_rescale(); +} + +void PlateMoveDialog::init_bitmaps() { + if (wxGetApp().dark_mode()) { + m_swipe_left_bmp_normal = ScalableBitmap(this, "previous_normal_dark", m_bmp_pix_cont); + m_swipe_left_bmp_hover = ScalableBitmap(this, "previous_hover_dark", m_bmp_pix_cont); + m_swipe_left_bmp_disable = ScalableBitmap(this, "previous_disable_dark", m_bmp_pix_cont); + m_swipe_right_bmp_normal = ScalableBitmap(this, "next_normal_dark", m_bmp_pix_cont); + m_swipe_right_bmp_hover = ScalableBitmap(this, "next_hover_dark", m_bmp_pix_cont); + m_swipe_right_bmp_disable = ScalableBitmap(this, "next_disable_dark", m_bmp_pix_cont); + + m_swipe_frontmost_bmp_normal = ScalableBitmap(this, "frontmost_normal_dark", m_bmp_pix_cont); + m_swipe_frontmost_bmp_hover = ScalableBitmap(this, "frontmost_hover_dark", m_bmp_pix_cont); + m_swipe_frontmost_bmp_disable = ScalableBitmap(this, "frontmost_disable_dark", m_bmp_pix_cont); + m_swipe_backmost_bmp_normal = ScalableBitmap(this, "backmost_normal_dark", m_bmp_pix_cont); + m_swipe_backmost_bmp_hover = ScalableBitmap(this, "backmost_hover_dark", m_bmp_pix_cont); + m_swipe_backmost_bmp_disable = ScalableBitmap(this, "backmost_disable_dark", m_bmp_pix_cont); + } else { + m_swipe_left_bmp_normal = ScalableBitmap(this, "previous_normal", m_bmp_pix_cont); + m_swipe_left_bmp_hover = ScalableBitmap(this, "previous_hover", m_bmp_pix_cont); + m_swipe_left_bmp_disable = ScalableBitmap(this, "previous_disable", m_bmp_pix_cont); + m_swipe_right_bmp_normal = ScalableBitmap(this, "next_normal", m_bmp_pix_cont); + m_swipe_right_bmp_hover = ScalableBitmap(this, "next_hover", m_bmp_pix_cont); + m_swipe_right_bmp_disable = ScalableBitmap(this, "next_disable", m_bmp_pix_cont); + + m_swipe_frontmost_bmp_normal = ScalableBitmap(this, "frontmost_normal", m_bmp_pix_cont); + m_swipe_frontmost_bmp_hover = ScalableBitmap(this, "frontmost_hover", m_bmp_pix_cont); + m_swipe_frontmost_bmp_disable = ScalableBitmap(this, "frontmost_disable", m_bmp_pix_cont); + m_swipe_backmost_bmp_normal = ScalableBitmap(this, "backmost_normal", m_bmp_pix_cont); + m_swipe_backmost_bmp_hover = ScalableBitmap(this, "backmost_hover", m_bmp_pix_cont); + m_swipe_backmost_bmp_disable = ScalableBitmap(this, "backmost_disable", m_bmp_pix_cont); + } +} +void PlateMoveDialog::update_swipe_button_state() { + m_swipe_left_button_enable = true; + m_swipe_left_button->Enable(); + m_swipe_left_button->SetBitmap(m_swipe_left_bmp_normal.bmp()); + m_swipe_right_button_enable = true; + m_swipe_right_button->Enable(); + m_swipe_right_button->SetBitmap(m_swipe_right_bmp_normal.bmp()); + if (m_combobox_plate->GetSelection() == 0) { // auto plate_index = m_plate_choices[m_combobox_plate->GetSelection()]; + m_swipe_left_button->Disable(); // SetBitmap(m_swipe_left_bmp_disable.bmp()); + m_swipe_left_button_enable = false; + } + if (m_combobox_plate->GetSelection() == m_combobox_plate->GetCount() - 1) { + m_swipe_right_button->Disable(); // m_swipe_right_button->set(m_swipe_right_bmp_disable.bmp()); + m_swipe_right_button_enable = false; + } + + m_swipe_frontmost_button_enable = true; + m_swipe_frontmost_button->Enable(); + m_swipe_frontmost_button->SetBitmap(m_swipe_frontmost_bmp_normal.bmp()); + m_swipe_backmost_button_enable = true; + m_swipe_backmost_button->Enable(); + m_swipe_backmost_button->SetBitmap(m_swipe_backmost_bmp_normal.bmp()); + if (m_combobox_plate->GetSelection() == 0) { // auto plate_index = m_plate_choices[m_combobox_plate->GetSelection()]; + m_swipe_frontmost_button->Disable(); // SetBitmap(m_swipe_frontmost_bmp_disable.bmp()); + m_swipe_frontmost_button_enable = false; + } + if (m_combobox_plate->GetSelection() == m_combobox_plate->GetCount() - 1) { + m_swipe_backmost_button->Disable(); // m_swipe_backmost_button->SetBitmap(m_swipe_backmost_bmp_disable.bmp()); + m_swipe_backmost_button_enable = false; + } + + update_ok_button_enable(); +} + +void PlateMoveDialog::on_previous_plate(wxCommandEvent &event) +{ + auto cobox_idx = m_combobox_plate->GetSelection(); + cobox_idx--; + if (cobox_idx < 0) { return; } + m_combobox_plate->SetSelection(cobox_idx); + m_specify_plate_idx = cobox_idx; + + update_swipe_button_state(); +} + +void PlateMoveDialog::on_next_plate(wxCommandEvent &event) { + auto cobox_idx = m_combobox_plate->GetSelection(); + cobox_idx++; + if (cobox_idx >= (int) m_combobox_plate->GetCount()) { return; } + m_combobox_plate->SetSelection(cobox_idx); + m_specify_plate_idx = cobox_idx; + + update_swipe_button_state(); +} + +void PlateMoveDialog::on_frontmost_plate(wxCommandEvent &event) { + m_combobox_plate->SetSelection(0); + m_specify_plate_idx = 0; + + update_swipe_button_state(); +} + +void PlateMoveDialog::on_backmost_plate(wxCommandEvent &event) { + auto cobox_idx = m_combobox_plate->GetCount() - 1; + m_combobox_plate->SetSelection(cobox_idx); + m_specify_plate_idx = cobox_idx; + + update_swipe_button_state(); +} + +void PlateMoveDialog::update_plate_combox() +{ + if (m_combobox_plate) { + m_combobox_plate->Clear(); + for (size_t i = 0; i < m_plate_number_choices_str.size(); i++) { + m_combobox_plate->Append(m_plate_number_choices_str[i]); + } + auto iter = std::find(m_plate_choices.begin(), m_plate_choices.end(), m_specify_plate_idx); + if (iter != m_plate_choices.end()) { + auto index = iter - m_plate_choices.begin(); + m_combobox_plate->SetSelection(index); + } + } +} +} +} // namespace Slic3r::GUI \ No newline at end of file diff --git a/src/slic3r/GUI/PlateMoveDialog.hpp b/src/slic3r/GUI/PlateMoveDialog.hpp new file mode 100644 index 0000000..4bbc4c0 --- /dev/null +++ b/src/slic3r/GUI/PlateMoveDialog.hpp @@ -0,0 +1,76 @@ +#ifndef slic3r_GUI_PlateMoveDialog_hpp_ +#define slic3r_GUI_PlateMoveDialog_hpp_ + +#include "Plater.hpp" +#include "PartPlate.hpp" +#include "Widgets/Button.hpp" +#include "Widgets/RadioBox.hpp" +#include "Widgets/ComboBox.hpp" +#include "DragCanvas.hpp" +#include "wxExtensions.hpp" +namespace Slic3r { namespace GUI { + +class PlateMoveDialog : public DPIDialog +{ +public: + enum ButtonStyle { ONLY_CONFIRM = 0, CONFIRM_AND_CANCEL = 1, MAX_STYLE_NUM = 2 }; + PlateMoveDialog(wxWindow * parent, + wxWindowID id = wxID_ANY, + const wxString &title = wxEmptyString, + const wxPoint & pos = wxDefaultPosition, + const wxSize & size = wxDefaultSize, + long style = wxCLOSE_BOX | wxCAPTION); + + ~PlateMoveDialog(); + void on_dpi_changed(const wxRect &suggested_rect) override; + int get_swap_index() { return m_specify_plate_idx; } + +private: + void init_bitmaps(); + void update_swipe_button_state(); + void on_previous_plate(wxCommandEvent &event); + void on_next_plate(wxCommandEvent &event); + void on_frontmost_plate(wxCommandEvent &event); + void on_backmost_plate(wxCommandEvent &event); + void update_plate_combox(); + void update_ok_button_enable(); + +private: + Button * m_button_ok{nullptr}; + Button * m_button_cancel{nullptr}; + + wxArrayString m_plate_number_choices_str; + std::vector m_plate_choices; + int m_specify_plate_idx{0}; + ComboBox * m_combobox_plate{nullptr}; + + ScalableButton *m_swipe_left_button{nullptr}; + ScalableButton *m_swipe_right_button{nullptr}; + bool m_swipe_left_button_enable; + bool m_swipe_right_button_enable; + + ScalableButton *m_swipe_frontmost_button{nullptr}; + ScalableButton *m_swipe_backmost_button{nullptr}; + bool m_swipe_frontmost_button_enable; + bool m_swipe_backmost_button_enable; + + int m_bmp_pix_cont = 32; + ScalableBitmap m_swipe_left_bmp_disable; + ScalableBitmap m_swipe_left_bmp_normal; + ScalableBitmap m_swipe_left_bmp_hover; + ScalableBitmap m_swipe_right_bmp_disable; + ScalableBitmap m_swipe_right_bmp_normal; + ScalableBitmap m_swipe_right_bmp_hover; + + ScalableBitmap m_swipe_frontmost_bmp_disable; + ScalableBitmap m_swipe_frontmost_bmp_normal; + ScalableBitmap m_swipe_frontmost_bmp_hover; + ScalableBitmap m_swipe_backmost_bmp_disable; + ScalableBitmap m_swipe_backmost_bmp_normal; + ScalableBitmap m_swipe_backmost_bmp_hover; + + +}; +}} // namespace Slic3r::GUI + +#endif \ No newline at end of file diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 04c75c5..43f033d 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -48,6 +48,7 @@ //#include "libslic3r/Format/3mf.hpp" #include "libslic3r/Format/qds_3mf.hpp" #include "libslic3r/GCode/ThumbnailData.hpp" +#include "Gizmos/GLGizmoAlignment.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/SLA/Hollowing.hpp" #include "libslic3r/SLA/SupportPoint.hpp" @@ -129,6 +130,7 @@ #include "Widgets/CheckBox.hpp" #include "Widgets/Button.hpp" #include "Widgets/StaticGroup.hpp" +#include "Widgets/MultiNozzleSync.hpp" #include "GUI_ObjectTable.hpp" #include "libslic3r/Thread.hpp" @@ -148,9 +150,11 @@ #include "PhysicalPrinterDialog.hpp" #include "PrintHostDialogs.hpp" #include "PlateSettingsDialog.hpp" +#include "PlateMoveDialog.hpp" #include "DailyTips.hpp" #include "CreatePresetsDialog.hpp" #include "StepMeshDialog.hpp" +#include "PurgeModeDialog.hpp" #include "FilamentMapDialog.hpp" #include "DeviceCore/DevFilaSystem.h" @@ -214,6 +218,7 @@ wxDEFINE_EVENT(EVT_DEL_FILAMENT, SimpleEvent); wxDEFINE_EVENT(EVT_ADD_CUSTOM_FILAMENT, ColorEvent); wxDEFINE_EVENT(EVT_NOTICE_CHILDE_SIZE_CHANGED, SimpleEvent); wxDEFINE_EVENT(EVT_NOTICE_FULL_SCREEN_CHANGED, IntEvent); +wxDEFINE_EVENT(EVT_SWITCH_TO_PREPARE_TAB, wxCommandEvent); // helio wxDEFINE_EVENT(EVT_HELIO_PROCESSING_COMPLETED, HelioCompletionEvent); @@ -390,10 +395,131 @@ enum class ActionButtonType : int { abSendGCode }; +class HoverLabel : public wxPanel +{ +public: + HoverLabel(wxWindow *parent, const wxString &label) : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE) + { + SetBackgroundColour(*wxWHITE); + + auto sizer = new wxBoxSizer(wxHORIZONTAL); + + m_label = new wxStaticText(this, wxID_ANY, label, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); + m_label->SetFont(Label::Body_13); + m_label->SetForegroundColour("#6B6B6B"); + m_count = new wxStaticText(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); + m_count->SetFont(Label::Body_13.Bold()); + m_count->SetForegroundColour("#262E30"); + + m_title_type = new wxStaticText(this, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); + m_title_type->SetForegroundColour("#ACACAC"); + + m_count->Hide(); + m_title_type->Hide(); + + m_brace_left = new wxStaticText(this, wxID_ANY, "(", wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); + m_brace_left->SetFont(Label::Body_13); + m_brace_left->SetForegroundColour("#262E30"); + + m_brace_right = new wxStaticText(this, wxID_ANY, ")", wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); + m_brace_right->SetFont(Label::Body_13); + m_brace_right->SetForegroundColour("#262E30"); + + auto hover_icon = create_scaled_bitmap("dot", this, 16); + m_hover_btn = new wxBitmapButton(this, wxID_ANY, hover_icon, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); + m_hover_btn->SetMinSize(wxSize(FromDIP(25), -1)); +#ifdef __WXOSX__ + m_hover_btn->SetBackgroundColour("#F7F7F7"); +#else + m_hover_btn->SetBackgroundColour(*wxWHITE); +#endif + sizer->Add(m_label, 0, wxALIGN_CENTER_VERTICAL); + sizer->Add(m_brace_left, 0, wxALIGN_CENTER_VERTICAL); + sizer->Add(m_title_type, 0, wxALIGN_CENTER_VERTICAL); + sizer->Add(m_count, 0, wxALIGN_CENTER_VERTICAL); + sizer->Add(m_brace_right, 0, wxALIGN_CENTER_VERTICAL); + sizer->Add(m_hover_btn, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 5); + + SetSizerAndFit(sizer); + Layout(); + m_hover_btn->Bind(wxEVT_BUTTON, [this](auto &evt) { + if (m_enabled) m_hover_on_click(); + }); + } + + void EnableEdit(bool enable) + { + m_enabled = enable; + if (enable) { + m_hover_btn->SetBitmap(create_scaled_bitmap("edit")); + } else { + m_hover_btn->SetBitmap(create_scaled_bitmap("dot")); + } + } + void SetOnHoverClick(std::function on_click) { m_hover_on_click = on_click; } + + void SetCount(int count) + { + if (count == -1) { + m_count->Hide(); + if (!m_title_type->IsShown()) { + m_brace_left->Hide(); + m_brace_right->Hide(); + } + } else { + m_count->Show(); + m_brace_left->Show(); + m_brace_right->Show(); + wxString count_str = wxString::Format("%d", count); + m_count->SetLabel(count_str); + } + Layout(); + Fit(); + } + void SetTitleWithType(const int type) + { + if (type != 1) { + m_title_type->Hide(); + if (!m_count->IsShown()) { + m_brace_left->Hide(); + m_brace_right->Hide(); + } + } else { + m_brace_left->Show(); + m_title_type->Show(); + m_brace_right->Show(); + wxString title_str = wxString::Format(_L("Aux")); + m_title_type->SetLabel(title_str); + } + Layout(); + Fit(); + } + wxString GetSuffixStr() const { return m_title_type->IsShown() ? m_title_type->GetLabel() : ""; } + +private: + wxStaticText *m_label; + wxBitmapButton *m_hover_btn; + wxStaticText *m_count; + wxStaticText *m_title_type; + + wxStaticText *m_brace_left; + wxStaticText *m_brace_right; + std::function m_hover_on_click; + bool m_enabled; +}; + +static void PositionLabelOverStaticBox(wxPoint top_left, HoverLabel *label_) +{ + int ox = 10; + int oy = 0; + label_->SetPosition(top_left + wxPoint{ox, oy}); +} + struct ExtruderGroup : StaticGroup { ExtruderGroup(wxWindow * parent, int index, wxString const &title); - wxStaticBoxSizer *sizer = nullptr; + wxBoxSizer *sizer = nullptr; + HoverLabel * hover_label = nullptr; ScalableButton * btn_edit = nullptr; ComboBox * combo_diameter = nullptr; ComboBox * combo_flow = nullptr; @@ -421,6 +547,12 @@ struct ExtruderGroup : StaticGroup } } + void SetEditEnabled(bool enable); + void SetCount(int count); + void SetTitleWithType(const int type); + wxString GetSuffixStr(); + void SetOnHoverClick(std::function on_click); + void update_ams(); void sync_ams(MachineObject const *obj, std::vector const &ams4, std::vector const &ams1); @@ -501,6 +633,7 @@ struct Sidebar::priv wxStaticLine* m_staticline2; wxPanel* m_panel_project_title; ScalableButton* m_filament_icon = nullptr; + Button * m_purge_mode_btn = nullptr; Button * m_flushing_volume_btn = nullptr; wxSearchCtrl* m_search_bar = nullptr; Search::SearchObjectDialog* dia = nullptr; @@ -537,6 +670,7 @@ struct Sidebar::priv bool sync_extruder_list(bool &only_external_material); bool switch_diameter(bool single); void update_sync_status(const MachineObject* obj); + void adjust_filament_title_layout(); #ifdef _WIN32 wxString btn_reslice_tip; @@ -620,6 +754,66 @@ void Sidebar::priv::flush_printer_sync(bool restart) timer_sync_printer->Stop(); } +void Sidebar::priv::adjust_filament_title_layout() +{ + if (!m_panel_filament_title) return; + + wxSize panel_size = m_panel_filament_title->GetSize(); + int available_width = panel_size.GetWidth() - FromDIP(220); + + int button_count = 4; // add, del, ams, set + if (m_purge_mode_btn->IsShown()) button_count++; + if (m_flushing_volume_btn->IsShown()) button_count++; + + if (button_count == 0) return; + + int purge_ideal_width = 0, flush_ideal_width = 0; + if (m_purge_mode_btn->IsShown()) { + purge_ideal_width = m_purge_mode_btn->GetTextRect().width + 10; + } + if (m_flushing_volume_btn->IsShown()) { + flush_ideal_width = m_flushing_volume_btn->GetTextRect().width + 10; + } + + int icon_ideal_size = m_bpButton_add_filament ? m_bpButton_add_filament->GetSize().GetWidth() : FromDIP(24); + int icon_button_count = 4; // add, del, ams, set + + int button_spacing = FromDIP(4); + int total_spacing = button_spacing * (button_count - 1); + int ideal_total_width = purge_ideal_width + flush_ideal_width + (icon_button_count * icon_ideal_size) + total_spacing; + + if (available_width >= ideal_total_width) { + if (m_purge_mode_btn->IsShown()) { + m_purge_mode_btn->SetMinSize(wxSize(purge_ideal_width, -1)); + m_purge_mode_btn->SetMaxSize(wxSize(purge_ideal_width, -1)); + } + + if (m_flushing_volume_btn->IsShown()) { + m_flushing_volume_btn->SetMinSize(wxSize(flush_ideal_width, -1)); + m_flushing_volume_btn->SetMaxSize(wxSize(flush_ideal_width, -1)); + } + + m_panel_filament_title->Layout(); + return; + } + + int button_width = std::max(FromDIP(24), (available_width - total_spacing) / button_count); + button_width = std::min(button_width, FromDIP(80)); + button_width = std::max(button_width, FromDIP(20)); + + if (m_purge_mode_btn->IsShown()) { + m_purge_mode_btn->SetMinSize(wxSize(button_width, -1)); + m_purge_mode_btn->SetMaxSize(wxSize(button_width, -1)); + } + + if (m_flushing_volume_btn->IsShown()) { + m_flushing_volume_btn->SetMinSize(wxSize(button_width, -1)); + m_flushing_volume_btn->SetMaxSize(wxSize(button_width, -1)); + } + + m_panel_filament_title->Layout(); +} + Sidebar::priv::~priv() { // QDS @@ -964,12 +1158,19 @@ public: }; ExtruderGroup::ExtruderGroup(wxWindow * parent, int index, wxString const &title) - : StaticGroup(parent, wxID_ANY, title) + : StaticGroup(parent, wxID_ANY) { SetFont(Label::Body_10); SetForegroundColour(wxColour("#CECECE")); + SetBackgroundColour(*wxWHITE); SetBorderColor(wxColour("#EEEEEE")); ShowBadge(true); + + hover_label = new HoverLabel(this, title); +#if defined(__WXOSX__) + hover_label->SetBackgroundColour("#F7F7F7"); +#endif + // Nozzle wxStaticText *label_diameter = new wxStaticText(this, wxID_ANY, _L("Diameter")); label_diameter->SetFont(Label::Body_14); @@ -985,9 +1186,13 @@ ExtruderGroup::ExtruderGroup(wxWindow * parent, int index, wxString const &title combo_flow->GetDropDown().SetUseContentWidth(true); combo_flow->Bind(wxEVT_COMBOBOX, [this, index, combo_flow](wxCommandEvent &evt) { auto printer_tab = dynamic_cast(wxGetApp().get_tab(Preset::TYPE_PRINTER)); - printer_tab->set_extruder_volume_type(index, NozzleVolumeType(intptr_t(combo_flow->GetClientData(evt.GetInt())))); - if (GUI::wxGetApp().plater()) - GUI::wxGetApp().plater()->update_machine_sync_status(); + NozzleVolumeType volume_type = NozzleVolumeType(intptr_t(combo_flow->GetClientData(evt.GetInt()))); + printer_tab->set_extruder_volume_type(index, volume_type); + auto plater = GUI::wxGetApp().plater(); + if (plater) { + plater->update_filament_volume_map(index, static_cast(volume_type)); + plater->update_machine_sync_status(); + } }); this->combo_flow = combo_flow; @@ -1065,12 +1270,16 @@ ExtruderGroup::ExtruderGroup(wxWindow * parent, int index, wxString const &title if (index < 0) { label_ams->Hide(); ams_not_installed_msg->Hide(); - wxStaticBoxSizer *hsizer = new wxStaticBoxSizer(this, wxHORIZONTAL); + wxStaticBoxSizer *vsizer = new wxStaticBoxSizer(this, wxVERTICAL); + wxBoxSizer *hsizer = new wxBoxSizer(wxHORIZONTAL); hsizer->Add(hsizer_diameter, 1, wxEXPAND | wxTOP| wxBOTTOM, FromDIP(8)); hsizer->Add(hsizer_nozzle, 1, wxEXPAND | wxALL, FromDIP(8)); - this->sizer = hsizer; + vsizer->Add(hover_label, 0, wxLEFT | wxALL, FromDIP(2)); + vsizer->Add(hsizer, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(2)); + this->sizer = vsizer; } else { wxStaticBoxSizer *vsizer = new wxStaticBoxSizer(this, wxVERTICAL); + vsizer->Add(hover_label, 0, wxLEFT | wxALL, FromDIP(2)); vsizer->Add(hsizer_ams, 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, FromDIP(2)); vsizer->Add(hsizer_diameter, 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, FromDIP(2)); vsizer->Add(hsizer_nozzle, 0, wxEXPAND | wxALL, FromDIP(2)); @@ -1079,6 +1288,42 @@ ExtruderGroup::ExtruderGroup(wxWindow * parent, int index, wxString const &title AMSCountPopupWindow::UpdateAMSCount(index < 0 ? 0 : index, this); } +void ExtruderGroup::SetEditEnabled(bool enable) +{ + if (hover_label) { + hover_label->EnableEdit(enable); + } +} + +void ExtruderGroup::SetCount(int count) +{ + if (hover_label) { + hover_label->SetCount(count); + } +} + +void ExtruderGroup::SetTitleWithType(const int type) +{ + if (hover_label) { + hover_label->SetTitleWithType(type); + } +} + +wxString ExtruderGroup::GetSuffixStr() +{ + if (hover_label) { + return hover_label->GetSuffixStr(); + } + return wxString(); +} + +void ExtruderGroup::SetOnHoverClick(std::function on_click) +{ + if (hover_label) { + hover_label->SetOnHoverClick(on_click); + } +} + void ExtruderGroup::update_ams() { static AMSinfo info4; @@ -1164,6 +1409,34 @@ void ExtruderGroup::sync_ams(MachineObject const *obj, std::vector con } if (infos == infos2) return false; + + for (size_t ams_index = 0; ams_index < infos2.size(); ++ams_index) { + auto ams_info = infos2[ams_index]; + BOOST_LOG_TRIVIAL(info) << "ExtruderGroup::sync_ams - AMS Info: " + << "ams_id = \"" << ams_info.ams_id << "\"" + << ", ams_type = " << static_cast(ams_info.ams_type) << ", cans_count = " << ams_info.cans.size(); + + for (size_t can_index = 0; can_index < ams_info.cans.size(); ++can_index) { + const auto &can_info = ams_info.cans[can_index]; + + BOOST_LOG_TRIVIAL(info) << "ExtruderGroup::sync_ams - Can[" << can_index << "]: " + << "can_id = \"" << can_info.can_id << "\"" + << ", material_name = \"" << can_info.material_name.ToStdString() << "\"" + << ", material_colour = " << can_info.material_colour.GetAsString(2).ToStdString() + << ", material_state = " << static_cast(can_info.material_state) << ", multi_colours_count = " << can_info.material_cols.size(); + + if (can_info.material_cols.size() > 1) { + std::string multi_colors_str; + for (size_t i = 0; i < can_info.material_cols.size(); ++i) { + if (i > 0) multi_colors_str += ","; + const auto &color = can_info.material_cols[i]; + multi_colors_str += color.GetAsString(2).ToStdString(); + } + BOOST_LOG_TRIVIAL(info) << "ExtruderGroup::sync_ams - Can[" << can_index << "] MultiColors: " << multi_colors_str; + } + } + } + infos.swap(infos2); return true; }; @@ -1212,18 +1485,13 @@ bool Sidebar::priv::switch_diameter(bool single) static bool is_skip_high_flow_printer(const std::string& printer) { static const std::set invalidate_list = { - "Bambu Lab X1", - "Bambu Lab X1E", - "Bambu Lab X1 Carbon", - "Bambu Lab P1P", - "Bambu Lab P1S" + "Bambu Lab X1E" }; return invalidate_list.count(printer); }; bool Sidebar::priv::sync_extruder_list(bool &only_external_material) { - wxBusyCursor busy; MachineObject *obj = wxGetApp().getDeviceManager()->get_selected_machine(); auto printer_name = plater->get_selected_printer_name_in_combox(); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << __LINE__ << " begin sync_extruder_list"; @@ -1276,14 +1544,28 @@ bool Sidebar::priv::sync_extruder_list(bool &only_external_material) extruder_map = physical_extruder_map->values; } assert(obj->GetExtderSystem()->GetTotalExtderCount() == extruder_nums); + auto extruder_max_nozzle_count = cur_preset.config.option("extruder_max_nozzle_count")->values; + auto nozzle_option = tryPopUpMultiNozzleDialog(obj); + bool support_multi_nozzle = std::any_of(extruder_max_nozzle_count.begin(),extruder_max_nozzle_count.end(),[](int val){return val>1;}); + if (!nozzle_option && support_multi_nozzle) + return false; std::vector nozzle_diameters; nozzle_diameters.resize(extruder_nums); + std::vectortarget_types(extruder_nums, NozzleVolumeType::nvtStandard); + for (size_t index = 0; index < extruder_nums; ++index) { - int extruder_id = extruder_map[index]; - nozzle_diameters[extruder_id] = obj->GetExtderSystem()->GetNozzleDiameter(index); + int extruder_id = extruder_map[index]; //physical extruder id + nozzle_diameters[extruder_id] = nozzle_option ? atof(nozzle_option->diameter.c_str()) : obj->GetExtderSystem()->GetNozzleDiameter(index); + std::optional select_type; NozzleVolumeType target_type = NozzleVolumeType::nvtStandard; - auto printer_tab = dynamic_cast(wxGetApp().get_tab(Preset::TYPE_PRINTER)); + if (nozzle_option && nozzle_option->extruder_nozzle_stats.count(index)) { + if (nozzle_option->extruder_nozzle_stats[index].size() > 1) + select_type = NozzleVolumeType::nvtHybrid; + else + select_type = nozzle_option->extruder_nozzle_stats[index].begin()->first; + } + if (obj->is_nozzle_flow_type_supported()) { if (obj->GetExtderSystem()->GetNozzleFlowType(index) == NozzleFlowType::NONE_FLOWTYPE) { MessageDialog dlg(this->plater, _L("There are unset nozzle types. Please set the nozzle types of all extruders before synchronizing."), @@ -1295,7 +1577,10 @@ bool Sidebar::priv::sync_extruder_list(bool &only_external_material) if (std::fabs(nozzle_diameters[extruder_id] - 0.2) > EPSILON && !is_skip_high_flow_printer(printer_model)) target_type = NozzleVolumeType(obj->GetExtderSystem()->GetNozzleFlowType(extruder_id) - 1); } - printer_tab->set_extruder_volume_type(index, target_type); + if (select_type) + target_type = *select_type; + + target_types[index] = target_type; } int deputy_4 = 0, main_4 = 0, deputy_1 = 0, main_1 = 0; @@ -1339,6 +1624,12 @@ bool Sidebar::priv::sync_extruder_list(bool &only_external_material) is_switching_diameter = false; } + // set nozzle volume type after switching prset, so this value can override the old value stored in conf + auto printer_tab = dynamic_cast(wxGetApp().get_tab(Preset::TYPE_PRINTER)); + for (size_t idx = 0; idx < target_types.size(); ++idx) { + printer_tab->set_extruder_volume_type(idx, target_types[idx]); + } + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << __LINE__ << " finish sync_extruder_list"; return true; } @@ -1407,49 +1698,23 @@ void Sidebar::priv::update_sync_status(const MachineObject *obj) } }; - auto is_same_nozzle_info = [obj](const ExtruderInfo &left, const ExtruderInfo &right) { - bool is_same_nozzle_type = true; - if (obj->is_nozzle_flow_type_supported()) - is_same_nozzle_type = left.nozzle_volue_type == right.nozzle_volue_type; - return abs(left.diameter - right.diameter) < EPSILON && is_same_nozzle_type; - }; - // 2. update extruder status int extruder_nums = preset_bundle->get_printer_extruder_count(); //if (extruder_nums != obj->GetExtderSystem()->GetTotalExtderCount()) // return; - std::vector extruder_infos(extruder_nums); - std::vector nozzle_volume_types = wxGetApp().preset_bundle->project_config.option("nozzle_volume_type")->values; - for (size_t i = 0; i < nozzle_volume_types.size(); ++i) { - extruder_infos[i].nozzle_volue_type = nozzle_volume_types[i]; - } - - std::vector> extruder_ams_counts = wxGetApp().preset_bundle->extruder_ams_counts; - if (extruder_ams_counts.size() >= extruder_nums) { - for (size_t i = 0; i < extruder_nums; ++i) { - for (auto iter = extruder_ams_counts[i].begin(); iter != extruder_ams_counts[i].end(); ++iter) { - if (iter->first == 4) - extruder_infos[i].ams_4 = iter->second; - if (iter->first == 1) - extruder_infos[i].ams_1 = iter->second; - } - } - } + //std::vector extruder_infos(extruder_nums); if (extruder_nums == 1) { double value = 0.0; single_extruder->diameter.ToDouble(&value); - extruder_infos[0].diameter = float(value); } else if(extruder_nums == 2){ double value = 0.0; left_extruder->diameter.ToDouble(&value); - extruder_infos[0].diameter = float(value); value = 0.0; right_extruder->diameter.ToDouble(&value); - extruder_infos[1].diameter = float(value); } //y59 @@ -1478,7 +1743,7 @@ void Sidebar::priv::update_sync_status(const MachineObject *obj) //std::vector extruder_synced(extruder_nums, false); //if (extruder_nums == 1) { - // if (is_same_nozzle_info(extruder_infos[0], machine_extruder_infos[0])) { + // if (this->plater->is_extruder_stat_synced()) { // single_extruder->ShowBadge(true); // single_extruder->sync_ams(obj, machine_extruder_infos[0].ams_v4, machine_extruder_infos[0].ams_v1); // extruder_synced[0] = true; @@ -1489,7 +1754,7 @@ void Sidebar::priv::update_sync_status(const MachineObject *obj) // } //} //else if (extruder_nums == 2) { - // if (extruder_infos[0] == machine_extruder_infos[0]) { + // if (this->plater->is_extruder_stat_synced(0)) { // left_extruder->ShowBadge(true); // left_extruder->sync_ams(obj, machine_extruder_infos[0].ams_v4, machine_extruder_infos[0].ams_v1); // extruder_synced[0] = true; @@ -1499,7 +1764,7 @@ void Sidebar::priv::update_sync_status(const MachineObject *obj) // left_extruder->sync_ams(obj, {}, {}); // } - // if (extruder_infos[1] == machine_extruder_infos[1]) { + // if (this->plater->is_extruder_stat_synced(1)) { // right_extruder->ShowBadge(true); // right_extruder->sync_ams(obj, machine_extruder_infos[1].ams_v4, machine_extruder_infos[1].ams_v1); // extruder_synced[1] = true; @@ -1537,6 +1802,9 @@ Sidebar::Sidebar(Plater *parent) { Choice::register_dynamic_list("support_filament", &dynamic_filament_list); Choice::register_dynamic_list("support_interface_filament", &dynamic_filament_list); + Choice::register_dynamic_list("wall_filament", &dynamic_filament_list); + Choice::register_dynamic_list("sparse_infill_filament", &dynamic_filament_list); + Choice::register_dynamic_list("solid_infill_filament", &dynamic_filament_list); p->scrolled = new wxPanel(this); // p->scrolled->SetScrollbars(0, 100, 1, 2); // ys_DELETE_after_testing. pixelsPerUnitY = 100 @@ -1724,8 +1992,8 @@ Sidebar::Sidebar(Plater *parent) p->combo_printer_bed->Bind(wxEVT_COMBOBOX, [this](auto &e) { bool isDual = static_cast(p->panel_printer_preset->GetSizer())->GetOrientation() == wxVERTICAL; bool exist; - auto image_path = get_cur_select_bed_image(exist); - if (exist) { + auto image_path = get_cur_select_bed_image(exist); + if(exist){ p->image_printer_bed->SetBitmap(create_scaled_bitmap(image_path, this, 48)); if (p->big_bed_image_popup) { p->big_bed_image_popup->set_bitmap(create_scaled_bitmap("big_" + image_path, p->big_bed_image_popup, p->big_bed_image_popup->get_image_px())); @@ -1803,10 +2071,20 @@ Sidebar::Sidebar(Plater *parent) }); p->btn_sync_printer = btn_sync; - p->left_extruder = new ExtruderGroup(p->m_panel_printer_content, 0, _L("Left Nozzle")); + p->left_extruder = new ExtruderGroup(p->m_panel_printer_content, 0, _L("Left Nozzle")); p->right_extruder = new ExtruderGroup(p->m_panel_printer_content, 1, _L("Right Nozzle")); p->single_extruder = new ExtruderGroup(p->m_panel_printer_content, -1, _L("Nozzle")); + p->left_extruder->SetOnHoverClick([this, parent]() { + GUI::manuallySetNozzleCount(0); + wxGetApp().plater()->update(); + }); + p->right_extruder->SetOnHoverClick([this, parent]() { + GUI::manuallySetNozzleCount(1); + wxGetApp().plater()->update(); + }); + p->single_extruder->SetEditEnabled(false); + auto switch_diameter = [this](wxCommandEvent & evt) { auto extruder = dynamic_cast(dynamic_cast(evt.GetEventObject())->GetParent()); p->is_switching_diameter = true; @@ -1846,6 +2124,13 @@ Sidebar::Sidebar(Plater *parent) p->m_panel_filament_content->SetMaxSize({-1, 0}); } m_scrolled_sizer->Layout(); + e.Skip(); + }); + p->m_panel_filament_title->Bind(wxEVT_SIZE, [this](wxSizeEvent &event) { + wxGetApp().CallAfter([this]() { + p->adjust_filament_title_layout(); + }); + event.Skip(); }); wxBoxSizer* bSizer39; @@ -1869,6 +2154,40 @@ Sidebar::Sidebar(Plater *parent) bSizer39->AddStretchSpacer(1); + p->m_purge_mode_btn = new Button(p->m_panel_filament_title, _L("Purge mode")); + p->m_purge_mode_btn->SetFont(Label::Body_10); + p->m_purge_mode_btn->SetPaddingSize(wxSize(FromDIP(6), FromDIP(3))); + p->m_purge_mode_btn->SetCornerRadius(FromDIP(8)); + + StateColor purge_bg_col(std::pair(wxColour(219, 253, 231), StateColor::Pressed), std::pair(wxColour(238, 238, 238), StateColor::Hovered), + std::pair(wxColour(238, 238, 238), StateColor::Normal)); + + StateColor purge_fg_col(std::pair(wxColour(107, 107, 106), StateColor::Pressed), std::pair(wxColour(107, 107, 106), StateColor::Hovered), + std::pair(wxColour(107, 107, 106), StateColor::Normal)); + + StateColor purge_bd_col(std::pair(wxColour(0, 174, 66), StateColor::Pressed), std::pair(wxColour(0, 174, 66), StateColor::Hovered), + std::pair(wxColour(172, 172, 172), StateColor::Normal)); + + p->m_purge_mode_btn->SetBackgroundColor(purge_bg_col); + p->m_purge_mode_btn->SetBorderColor(purge_bd_col); + p->m_purge_mode_btn->SetTextColor(purge_fg_col); + p->m_purge_mode_btn->SetFocus(); + p->m_purge_mode_btn->Rescale(); + + p->m_purge_mode_btn->Bind(wxEVT_BUTTON, ([parent, this](wxCommandEvent &e) { + auto &preset_bundle = *wxGetApp().preset_bundle; + + PurgeModeDialog dlg(static_cast(wxGetApp().mainframe)); + if (dlg.ShowModal() == wxID_OK) { + preset_bundle.project_config.set_key_value("prime_volume_mode", new ConfigOptionEnum(dlg.get_selected_mode())); + wxGetApp().plater()->update(); + } + })); + + bSizer39->Add(p->m_purge_mode_btn, 0, wxALIGN_CENTER_VERTICAL, FromDIP(4)); + //bSizer39->Hide(p->m_purge_mode_btn); + bSizer39->Add(FromDIP(12), 0, 0, 0, 0); + // QDS // add wiping dialog //wiping_dialog_button->SetFont(wxGetApp().normal_font()); @@ -2143,8 +2462,8 @@ void Sidebar::on_enter_image_printer_bed(wxMouseEvent &evt) { if (p->big_bed_image_popup == nullptr) { p->big_bed_image_popup = new ImageDPIFrame(); bool exist; - auto image_path = get_cur_select_bed_image(exist); - if (exist) { + auto image_path = get_cur_select_bed_image(exist); + if(exist){ p->big_bed_image_popup->set_bitmap(create_scaled_bitmap("big_" + image_path, p->big_bed_image_popup, p->big_bed_image_popup->get_image_px())); } } @@ -2521,14 +2840,15 @@ void Sidebar::update_presets(Preset::Type preset_type) auto nozzle_volumes = wxGetApp().preset_bundle->project_config.option("nozzle_volume_type"); auto diameters = wxGetApp().preset_bundle->printers.diameters_of_selected_printer(); auto diameter = printer_preset.config.opt_string("printer_variant"); - auto printer_model = printer_preset.config.opt_string("printer_model"); - auto update_extruder_variant = [&](ExtruderGroup & extruder, int index) { + auto extruder_max_nozzle_count = printer_preset.config.option("extruder_max_nozzle_count"); + auto update_extruder_variant = [extruders_def, extruders, nozzle_volumes_def, nozzle_volumes, extruder_variants,diameter,extruder_max_nozzle_count](ExtruderGroup & extruder, int index) { extruder.combo_flow->Clear(); auto type = extruders_def->enum_labels[extruders->values[index]]; int select = -1; for (size_t i = 0; i < nozzle_volumes_def->enum_labels.size(); ++i) { - if (boost::algorithm::contains(extruder_variants->values[index], type + " " + nozzle_volumes_def->enum_labels[i])) { - if ((diameter == "0.2" || is_skip_high_flow_printer(printer_model))&& nozzle_volumes_def->enum_keys_map->at(nozzle_volumes_def->enum_values[i]) == NozzleVolumeType::nvtHighFlow) + if (boost::algorithm::contains(extruder_variants->values[index], type + " " + nozzle_volumes_def->enum_labels[i]) || + extruder_max_nozzle_count->values[index] > 1 && nozzle_volumes_def->enum_keys_map->at(nozzle_volumes_def->enum_values[i]) == nvtHybrid) { + if (diameter == "0.2" && nozzle_volumes_def->enum_keys_map->at(nozzle_volumes_def->enum_values[i]) == NozzleVolumeType::nvtHighFlow) continue; if (nozzle_volumes->values[index] == i) select = extruder.combo_flow->GetCount(); @@ -2559,16 +2879,16 @@ void Sidebar::update_presets(Preset::Type preset_type) AMSCountPopupWindow::UpdateAMSCount(1, p->right_extruder); update_extruder_variant(*p->left_extruder, 0); update_extruder_variant(*p->right_extruder, 1); - // if (!p->is_switching_diameter) { - update_extruder_diameter(*p->left_extruder); - update_extruder_diameter(*p->right_extruder); + //if (!p->is_switching_diameter) { + update_extruder_diameter(*p->left_extruder); + update_extruder_diameter(*p->right_extruder); //} p->image_printer_bed->SetBitmap(create_scaled_bitmap(image_path, this, 48)); } else { AMSCountPopupWindow::UpdateAMSCount(0, p->single_extruder); update_extruder_variant(*p->single_extruder, 0); - // if (!p->is_switching_diameter) - update_extruder_diameter(*p->single_extruder); + //if (!p->is_switching_diameter) + update_extruder_diameter(*p->single_extruder); p->image_printer_bed->SetBitmap(create_scaled_bitmap(image_path, this, 48)); } } @@ -2791,12 +3111,14 @@ void Sidebar::msw_rescale() p->image_printer_bed->SetBitmap(create_scaled_bitmap(image_path, this, 48)); } + p->adjust_filament_title_layout(); p->m_filament_icon->msw_rescale(); p->m_bpButton_add_filament->msw_rescale(); p->m_bpButton_del_filament->msw_rescale(); p->m_bpButton_ams_filament->msw_rescale(); p->m_bpButton_set_filament->msw_rescale(); p->m_flushing_volume_btn->Rescale(); + p->m_purge_mode_btn->Rescale(); //QDS p->combo_printer_bed->Rescale(); p->combo_printer_bed->SetMinSize({-1, 3 * wxGetApp().em_unit()}); @@ -2876,6 +3198,7 @@ void Sidebar::sys_color_changed() p->m_bpButton_box_filament->msw_rescale(); p->m_bpButton_set_filament->msw_rescale(); p->m_flushing_volume_btn->Rescale(); + p->m_purge_mode_btn->Rescale(); // QDS #if 0 @@ -2975,6 +3298,9 @@ void Sidebar::on_filament_count_change(size_t num_filaments) else sizer->Hide(p->m_flushing_volume_btn); } + wxGetApp().CallAfter([this]() { + p->adjust_filament_title_layout(); + }); auto min_size = p->m_panel_filament_content->GetSizer()->GetMinSize(); if (min_size.y > p->m_panel_filament_content->GetMaxHeight()) @@ -3031,6 +3357,9 @@ void Sidebar::on_filaments_delete(size_t filament_id) else sizer->Hide(p->m_flushing_volume_btn); } + wxGetApp().CallAfter([this]() { + p->adjust_filament_title_layout(); + }); for (size_t idx = filament_id ; idx < p->combos_filament.size(); ++idx) { p->combos_filament[idx]->update(); @@ -3256,6 +3585,30 @@ void Sidebar::get_small_btn_sync_pos_size(wxPoint &pt, wxSize &size) { pt = fila_sync_btn->GetScreenPosition(); } +void Sidebar::set_extruder_nozzle_count(int extruder_id, int nozzle_count) +{ + if (extruder_id == 0) { + p->left_extruder->SetCount(nozzle_count); + p->single_extruder->SetCount(nozzle_count); + } + else if (extruder_id == 1) { + p->right_extruder->SetCount(nozzle_count); + } +} + +void Sidebar::enable_nozzle_count_edit(bool enable){ + p->left_extruder->SetEditEnabled(enable); + p->right_extruder->SetEditEnabled(enable); +} + +void Sidebar::enable_purge_mode_btn(bool enable) +{ + p->m_purge_mode_btn->Show(enable); + wxGetApp().CallAfter([this]() { + p->adjust_filament_title_layout(); + }); +} + void Sidebar::load_ams_list(MachineObject* obj) { std::map filament_ams_list = build_filament_ams_list(obj); @@ -4445,6 +4798,7 @@ public: } m_ui_jobs; int m_job_prepare_state; + bool m_slice_from_slice_button{false}; bool delayed_scene_refresh; std::string delayed_error_message; @@ -4520,6 +4874,7 @@ public: void update(unsigned int flags = 0); void select_view(const std::string& direction); //QDS: add no_slice option + void set_slice_from_slice_btn(bool flag); void select_view_3D(const std::string& name, bool no_slice = true); void select_next_view_3D(); @@ -4553,6 +4908,7 @@ public: void reset_canvas_volumes(); bool check_ams_status_impl(bool is_slice_all); // Check whether the printer and ams status are consistent, for grouping algorithm bool get_machine_sync_status(); // check whether the printer is linked and the printer type is same as selected profile + bool is_extruder_stat_synced(int target_extruder_id = -1); // check whether nozzle staus is synced with printer, extruder = -1 means check both extruder Camera& get_current_camera(); // QDS @@ -4617,6 +4973,18 @@ public: void delete_all_objects_from_model(); void reset(bool apply_presets_change = false); void center_selection(); + void distribute_selection_y(); + void distribute_selection_x(); + void distribute_selection_z(); + void align_selection_y_max(); + void align_selection_y_min(); + void align_selection_y_center(); + void align_selection_x_max(); + void align_selection_x_min(); + void align_selection_x_center(); + void align_selection_z_max(); + void align_selection_z_min(); + void align_selection_z_center(); void mirror(Axis axis); void split_object(); void split_volume(); @@ -4743,6 +5111,7 @@ public: void on_object_select(SimpleEvent&); void show_right_click_menu(Vec2d mouse_position, wxMenu *menu); void on_plate_name_change(SimpleEvent &); + void on_move_plate(SimpleEvent &); void on_right_click(RBtnEvent&); //QDS: add model repair void on_repair_model(wxCommandEvent &event); @@ -4750,6 +5119,7 @@ public: void show_install_plugin_hint(wxCommandEvent &event); void install_network_plugin(wxCommandEvent &event); void show_preview_only_hint(wxCommandEvent &event); + void on_switch_to_prepare_tab(wxCommandEvent &event); //QDS: add part plate related logic void on_plate_right_click(RBtnPlateEvent&); void on_plate_selected(SimpleEvent&); @@ -4942,7 +5312,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) "nozzle_height", "skirt_loops", "skirt_distance", "brim_width", "brim_object_gap", "brim_type", "nozzle_diameter", "single_extruder_multi_material", "enable_prime_tower", "wipe_tower_x", "wipe_tower_y", "prime_tower_width", "prime_tower_brim_width", "prime_tower_skip_points", "prime_tower_enable_framework","prime_tower_max_speed", - "prime_tower_rib_wall","prime_tower_extra_rib_length", "prime_tower_rib_width","prime_tower_fillet_wall", "prime_tower_infill_gap","filament_prime_volume", + "prime_tower_rib_wall","prime_tower_extra_rib_length", "prime_tower_rib_width","prime_tower_fillet_wall", "prime_tower_infill_gap","filament_prime_volume","filament_prime_volume_nc", "extruder_colour", "filament_colour", "filament_type", "material_colour", "printable_height", "extruder_printable_height", "printer_model", "printer_technology", // These values are necessary to construct SlicingParameters by the Canvas3D variable layer height editor. "layer_height", "initial_layer_print_height", "min_layer_height", "max_layer_height", @@ -5021,6 +5391,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) this->q->Bind(EVT_DEL_FILAMENT, &priv::on_delete_filament, this); this->q->Bind(EVT_ADD_CUSTOM_FILAMENT, &priv::on_add_custom_filament, this); this->q->Bind(EVT_GCODE_VIEWER_CHANGED, &priv::on_gcode_viewer_changed, this); + + this->q->Bind(EVT_SWITCH_TO_PREPARE_TAB, &priv::on_switch_to_prepare_tab, this); + main_frame->m_tabpanel->Bind(wxEVT_NOTEBOOK_PAGE_CHANGING, &priv::on_tab_selection_changing, this); auto *panel_3d = new wxPanel(q); @@ -5141,6 +5514,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) }); view3D_canvas->Bind(EVT_GLCANVAS_OBJECT_SELECT, &priv::on_object_select, this); view3D_canvas->Bind(EVT_GLCANVAS_PLATE_NAME_CHANGE, &priv::on_plate_name_change, this); + view3D_canvas->Bind(EVT_GLCANVAS_MOVE_PLATE, &priv::on_move_plate, this); view3D_canvas->Bind(EVT_GLCANVAS_RIGHT_CLICK, &priv::on_right_click, this); //QDS: add part plate related logic view3D_canvas->Bind(EVT_GLCANVAS_PLATE_RIGHT_CLICK, &priv::on_plate_right_click, this); @@ -5566,23 +5940,7 @@ std::map Plater::get_bed_texture_maps() { auto pm = get_curr_printer_model(); if (pm) { - std::map maps; - if (pm->use_double_extruder_default_texture.size() > 0) { - maps["use_double_extruder_default_texture"] = pm->use_double_extruder_default_texture; - } - if (pm->bottom_texture_end_name.size() > 0) { - maps["bottom_texture_end_name"] = pm->bottom_texture_end_name; - } - if (pm->bottom_texture_rect.size() > 0) { - maps["bottom_texture_rect"] = pm->bottom_texture_rect; - } - if (pm->bottom_texture_rect_longer.size() > 0) { - maps["bottom_texture_rect_longer"] = pm->bottom_texture_rect_longer; - } - if (pm->middle_texture_rect.size() > 0) { - maps["middle_texture_rect"] = pm->middle_texture_rect; - } - return maps; + return pm->get_bed_texture_maps(); } return {}; } @@ -5641,7 +5999,8 @@ wxString Plater::get_slice_warning_string(GCodeProcessorResult::SliceWarning& wa } else if (warning.msg == NOZZLE_HRC_CHECKER) { return _L("The nozzle hardness required by the filament is higher than the default nozzle hardness of the printer. Please replace the hardened nozzle or filament, otherwise, the nozzle will be attrited or damaged."); } else if (warning.msg == NOT_SUPPORT_TRADITIONAL_TIMELAPSE) { - return _L("Enabling traditional timelapse photography may cause surface imperfections. It is recommended to change to smooth mode."); + return _L("Enabling traditional timelapse photography may cause surface imperfections. It is recommended to change to smooth mode.") +_L("\n") + + _L("(Path to modify: 'Process'-'Others'-'Special Mode'-'Timelapsed')"); } else if (warning.msg == NOT_GENERATE_TIMELAPSE) { return wxString(); } else if (warning.msg == SMOOTH_TIMELAPSE_WITHOUT_PRIME_TOWER) { @@ -5667,6 +6026,8 @@ void Plater::priv::apply_free_camera_correction(bool apply/* = true*/) camera.recover_from_free_camera(); } +void Plater::priv::set_slice_from_slice_btn(bool flag) { m_slice_from_slice_button = flag; } + //QDS: add no slice option void Plater::priv::select_view_3D(const std::string& name, bool no_slice) { @@ -5678,6 +6039,14 @@ void Plater::priv::select_view_3D(const std::string& name, bool no_slice) set_current_panel(view3D, no_slice); } else if (name == "Preview") { + if (!no_slice && !m_slice_from_slice_button) { + if (!this->partplate_list.get_curr_plate()->is_slice_result_valid() && !q->check_ams_status(false)) + return; + } + if (!no_slice) { + set_slice_from_slice_btn(false); + } + BOOST_LOG_TRIVIAL(info) << "select preview"; //QDS update extruder params and speed table before slicing const Slic3r::DynamicPrintConfig& config = wxGetApp().preset_bundle->full_config(); @@ -6009,6 +6378,14 @@ std::vector Plater::priv::load_files(const std::vector& input_ const float CENTER_AROUND_ORIGIN_RATIO = 0.8; const float LOAD_MODEL_RATIO = 0.9; bool import_obj_or_stl = false; + +//y75 + std::vector qdt_different_keys; + En3mfType _3mf_type; + bool has_different_settings_to_system; + std::string old_preset_name = wxGetApp().preset_bundle->printers.get_edited_preset().name; +//y75 + for (size_t i = 0; i < input_files.size(); ++i) { int file_percent = 0; @@ -6048,9 +6425,6 @@ std::vector Plater::priv::load_files(const std::vector& input_ DynamicPrintConfig config; Semver file_version; En3mfType en_3mf_file_type = En3mfType::From_QDS; - //y71 - std::string old_preset_name = wxGetApp().preset_bundle->printers.get_edited_preset().name; - { DynamicPrintConfig config_loaded; @@ -6311,10 +6685,10 @@ std::vector Plater::priv::load_files(const std::vector& input_ } } - //y71 - bool has_different_settings_to_system = config.option("different_settings_to_system") ? true : false; +//y75 + has_different_settings_to_system = config.option("different_settings_to_system") ? true : false; +//y75 std::string qdt_diff_settings; - std::vector qdt_different_keys; if(has_different_settings_to_system){ std::string qdt_diff_settings = config.option("different_settings_to_system", true)->values[0]; Slic3r::unescape_strings_cstyle(qdt_diff_settings, qdt_different_keys); @@ -6437,6 +6811,8 @@ std::vector Plater::priv::load_files(const std::vector& input_ file_wipe_tower_y = *wipe_tower_y_opt; preset_bundle->load_config_model(filename.string(), std::move(config), file_version); + // QDS: do not change extruder nozzle stat when loading 3mf + preset_bundle->extruder_nozzle_stat.set_force_keep_flag(true); //y71 std::vector qdt_nozzle_sizes = { "0.2 nozzle", "0.4 nozzle", "0.6 nozzle", "0.8 nozzle" }; @@ -6454,17 +6830,6 @@ std::vector Plater::priv::load_files(const std::vector& input_ size_t nozzle_len = old_preset_nozzle_size.size(); old_preset_name.replace(nozzle_pos, nozzle_len, new_preset_nozzle_size); } - if (en_3mf_file_type != En3mfType::From_QDS) { - //y74 - qdt_different_keys.erase( - std::remove(qdt_different_keys.begin(), qdt_different_keys.end(), ""), - qdt_different_keys.end() - ); - if(has_different_settings_to_system) - wxGetApp().get_tab(Preset::TYPE_PRINT)->cache_config_diff(qdt_different_keys); - wxGetApp().get_tab(Preset::TYPE_PRINTER)->select_preset(old_preset_name); - q->on_config_change(wxGetApp().preset_bundle->full_config()); - } ConfigOption* bed_type_opt = preset_bundle->project_config.option("curr_bed_type"); if (bed_type_opt != nullptr) { @@ -6536,6 +6901,9 @@ std::vector Plater::priv::load_files(const std::vector& input_ // For exporting from the amf/3mf we shouldn't check printer_presets for the containing information about "Print Host upload" // QDS: add preset combo box re-active logic // currently found only needs re-active here +//y75 + _3mf_type = en_3mf_file_type; +//y75 wxGetApp().load_current_presets(false, false); // Update filament colors for the MM-printer profile in the full config // to avoid black (default) colors for Extruders in the ObjectList, @@ -6546,6 +6914,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ DynamicConfig& proj_cfg = preset_bundle->project_config; // do some post process after loading config { + preset_bundle->extruder_nozzle_stat.set_force_keep_flag(false); //QDS: rewrite wipe tower pos stored in 3mf file , the code above should be seriously reconsidered ConfigOptionFloats* wipe_tower_x = proj_cfg.opt("wipe_tower_x"); ConfigOptionFloats* wipe_tower_y = proj_cfg.opt("wipe_tower_y"); @@ -6564,6 +6933,16 @@ std::vector Plater::priv::load_files(const std::vector& input_ filament_map->values.resize(filament_count, 1); } + ConfigOptionInts* filament_nozzle_map = proj_cfg.opt("filament_nozzle_map", true); + if(filament_nozzle_map->size() != filament_count){ + filament_nozzle_map->values.resize(filament_count,0); + } + + ConfigOptionInts* filament_volume_map = proj_cfg.opt("filament_volume_map", true); + if(filament_volume_map->size() != filament_count){ + filament_volume_map->values.resize(filament_count,static_cast(NozzleVolumeType::nvtStandard)); + } + // Sync filament multi colour ConfigOptionStrings* filament_multi_color = proj_cfg.opt("filament_multi_colour", true); if (filament_multi_color->size() != filament_count) { @@ -6591,6 +6970,13 @@ std::vector Plater::priv::load_files(const std::vector& input_ } // Update filament combobox after loading config wxGetApp().plater()->sidebar().update_presets(Preset::TYPE_FILAMENT); + { + // update nozzle count display after loading + int extruder_count = preset_bundle->get_printer_extruder_count(); + auto nozzle_volumes_values = preset_bundle->project_config.option("nozzle_volume_type")->values; + for (int extruder_id = 0; extruder_id < extruder_count; ++extruder_id) + updateNozzleCountDisplay(preset_bundle, extruder_id, NozzleVolumeType(nozzle_volumes_values[extruder_id])); + } } } if (!silence) wxGetApp().app_config->update_config_dir(path.parent_path().string()); @@ -7034,6 +7420,21 @@ std::vector Plater::priv::load_files(const std::vector& input_ } } q->schedule_background_process(true); + +//y75 + if (_3mf_type != En3mfType::From_QDS) { + qdt_different_keys.erase( + std::remove(qdt_different_keys.begin(), qdt_different_keys.end(), ""), + qdt_different_keys.end() + ); + if(has_different_settings_to_system) + wxGetApp().get_tab(Preset::TYPE_PRINT)->cache_config_diff(qdt_different_keys); + wxGetApp().get_tab(Preset::TYPE_PRINTER)->select_preset(old_preset_name); + q->on_config_change(wxGetApp().preset_bundle->full_config()); + wxGetApp().load_current_presets(false, false); + } +//y75 + return obj_idxs; } @@ -7602,6 +8003,77 @@ void Plater::priv::center_selection() view3D->center_selected(); } +void Plater::priv::distribute_selection_y() +{ + GLGizmoAlignment alignment_helper(*view3D->get_canvas3d()); + alignment_helper.distribute_objects(GLGizmoAlignment::AlignType::DISTRIBUTE_Y); +} + +void Plater::priv::distribute_selection_x() +{ + GLGizmoAlignment alignment_helper(*view3D->get_canvas3d()); + alignment_helper.distribute_objects(GLGizmoAlignment::AlignType::DISTRIBUTE_X); +} + +void Plater::priv::distribute_selection_z() +{ + GLGizmoAlignment alignment_helper(*view3D->get_canvas3d()); + alignment_helper.distribute_objects(GLGizmoAlignment::AlignType::DISTRIBUTE_Z); +} + +void Plater::priv::align_selection_y_max() +{ + GLGizmoAlignment alignment_helper(*view3D->get_canvas3d()); +alignment_helper.align_objects(GLGizmoAlignment::AlignType::Y_MAX); +} + +void Plater::priv::align_selection_y_min() +{ + GLGizmoAlignment alignment_helper(*view3D->get_canvas3d()); +alignment_helper.align_objects(GLGizmoAlignment::AlignType::Y_MIN); +} +void Plater::priv::align_selection_y_center() +{ + GLGizmoAlignment alignment_helper(*view3D->get_canvas3d()); +alignment_helper.align_objects(GLGizmoAlignment::AlignType::CENTER_Y); +} + +void Plater::priv::align_selection_x_max() +{ + GLGizmoAlignment alignment_helper(*view3D->get_canvas3d()); +alignment_helper.align_objects(GLGizmoAlignment::AlignType::X_MAX); +} + +void Plater::priv::align_selection_x_min() +{ + GLGizmoAlignment alignment_helper(*view3D->get_canvas3d()); +alignment_helper.align_objects(GLGizmoAlignment::AlignType::X_MIN); +} + +void Plater::priv::align_selection_x_center() +{ + GLGizmoAlignment alignment_helper(*view3D->get_canvas3d()); +alignment_helper.align_objects(GLGizmoAlignment::AlignType::CENTER_X); +} + +void Plater::priv::align_selection_z_max() +{ + GLGizmoAlignment alignment_helper(*view3D->get_canvas3d()); +alignment_helper.align_objects(GLGizmoAlignment::AlignType::Z_MAX); +} + +void Plater::priv::align_selection_z_min() +{ + GLGizmoAlignment alignment_helper(*view3D->get_canvas3d()); +alignment_helper.align_objects(GLGizmoAlignment::AlignType::Z_MIN); +} + +void Plater::priv::align_selection_z_center() +{ + GLGizmoAlignment alignment_helper(*view3D->get_canvas3d()); +alignment_helper.align_objects(GLGizmoAlignment::AlignType::CENTER_Z); +} + void Plater::priv::mirror(Axis axis) { view3D->mirror_selection(axis); @@ -7841,7 +8313,11 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool if (preset_bundle->get_printer_extruder_count() > 1) { PartPlate* cur_plate = background_process.get_current_plate(); std::vector f_maps = cur_plate->get_real_filament_maps(preset_bundle->project_config); - invalidated = background_process.apply(this->model, preset_bundle->full_config(false, f_maps)); + std::vector f_volume_maps = cur_plate->get_filament_volume_maps(); + if (f_volume_maps.empty()) { + f_volume_maps = preset_bundle->get_default_nozzle_volume_types_for_filaments(f_maps); + } + invalidated = background_process.apply(this->model, preset_bundle->full_config(false, f_maps, f_volume_maps)); background_process.fff_print()->set_extruder_filament_info(get_extruder_filament_info()); } else @@ -9855,9 +10331,16 @@ int Plater::priv::update_helio_background_process(std::string& printer_id, std:: for (HelioQuery::SupportedData pdata : HelioQuery::global_supported_materials) { if (!pdata.native_name.empty()) { std::string native_name = pdata.native_name; + + //cstom material name match + size_t atPos = used_filament.find('@'); + std::string target_name = (atPos != std::string::npos) ? used_filament.substr(0, atPos) : used_filament; + boost::trim(target_name); + boost::algorithm::to_lower(native_name); - boost::algorithm::to_lower(used_filament); - if (used_filament.find(native_name) != std::string::npos) { + boost::algorithm::to_lower(target_name); + + if (target_name == native_name) { is_supported_by_helio = true; material_id = pdata.id; break; @@ -10494,6 +10977,12 @@ void Plater::priv::on_plate_name_change(SimpleEvent &) { selection_changed(); } +void Plater::priv::on_move_plate(SimpleEvent &) +{ + wxGetApp().obj_list()->update_selections(); + selection_changed(); +} + //QDS: repair model through netfabb void Plater::priv::on_repair_model(wxCommandEvent &event) { @@ -10554,6 +11043,18 @@ void Plater::priv::show_preview_only_hint(wxCommandEvent &event) notification_manager->qdt_show_preview_only_notification(into_u8(_L("Preview only mode:\nThe loaded file contains gcode only, Can not enter the Prepare page"))); } +void Plater::priv::on_switch_to_prepare_tab(wxCommandEvent &event) +{ + Sidebar& sidebar = GUI::wxGetApp().sidebar(); + if (sidebar.need_auto_sync_after_connect_printer()) { + sidebar.set_need_auto_sync_after_connect_printer(false); + sidebar.sync_extruder_list(); + } + + q->update(true); + q->show_wrapping_detect_dialog_if_necessary(); +} + void Plater::priv::on_apple_change_color_mode(wxSysColourChangedEvent& evt) { m_is_dark = wxSystemSettings::GetAppearance().IsDark(); if (view3D->get_canvas3d() && view3D->get_canvas3d()->is_initialized()) { @@ -11252,13 +11753,10 @@ bool Plater::priv::check_ams_status_impl(bool is_slice_all) if (preset_bundle && preset_bundle->printers.get_edited_preset().get_printer_type(preset_bundle) == obj->get_show_printer_type()) { bool is_same_as_printer = true; auto nozzle_volumes_values = preset_bundle->project_config.option("nozzle_volume_type")->values; + assert(obj->GetExtderSystem()->GetTotalExtderCount() == 2 && nozzle_volumes_values.size() == 2); if (obj->GetExtderSystem()->GetTotalExtderCount() == 2 && nozzle_volumes_values.size() == 2) { - NozzleVolumeType right_nozzle_type = NozzleVolumeType(obj->GetExtderSystem()->GetNozzleFlowType(0) - 1); - NozzleVolumeType left_nozzle_type = NozzleVolumeType(obj->GetExtderSystem()->GetNozzleFlowType(1) - 1); - NozzleVolumeType preset_left_type = NozzleVolumeType(nozzle_volumes_values[0]); - NozzleVolumeType preset_right_type = NozzleVolumeType(nozzle_volumes_values[1]); - is_same_as_printer = (left_nozzle_type == preset_left_type && right_nozzle_type == preset_right_type); + is_same_as_printer = is_extruder_stat_synced(); } std::vector> ams_count_info; @@ -11342,6 +11840,72 @@ bool Plater::priv::get_machine_sync_status() return preset_bundle && preset_bundle->printers.get_edited_preset().get_printer_type(preset_bundle) == obj->get_show_printer_type(); } +bool Plater::priv::is_extruder_stat_synced(int target_extruder_id) +{ + using namespace MultiNozzleUtils; + Slic3r::DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager(); + if (!dev) + return false; + + MachineObject* obj = dev->get_selected_machine(); + if (!obj) + return false; + + PresetBundle* preset_bundle = wxGetApp().preset_bundle; + bool is_synced = true; + + auto nozzle_volume_values = preset_bundle->project_config.option("nozzle_volume_type")->values; + auto nozzle_diameter_values = preset_bundle->printers.get_edited_preset().config.option("nozzle_diameter")->values; + auto extruder_nozzle_stats = preset_bundle->extruder_nozzle_stat.get_raw_stat(); + + + std::vector> preset_nozzle_infos(nozzle_diameter_values.size()); + + for (size_t extruder_id = 0; extruder_id < nozzle_diameter_values.size(); ++extruder_id) { + NozzleVolumeType preset_volume_type = NozzleVolumeType(nozzle_volume_values[extruder_id]); + std::string preset_diameter = format_diameter_to_str(nozzle_diameter_values[extruder_id]); + + if (preset_volume_type == nvtHybrid) { + if (extruder_id < extruder_nozzle_stats.size()) { + for (auto& elem : extruder_nozzle_stats[extruder_id]) { + NozzleVolumeType type = elem.first; + int count = elem.second; + NozzleGroupInfo preset_info(preset_diameter, type, extruder_id, count); + preset_nozzle_infos[extruder_id].emplace_back(preset_info); + } + } + } + else { + NozzleGroupInfo preset_info(preset_diameter, preset_volume_type, extruder_id, preset_bundle->extruder_nozzle_stat.get_extruder_nozzle_count(extruder_id, preset_volume_type)); + preset_nozzle_infos[extruder_id].emplace_back(preset_info); + } + } + + auto printer_groups = obj->GetNozzleSystem()->GetNozzleGroups(); + std::vector> target_nozzle_groups; + if (target_extruder_id == -1) + target_nozzle_groups = preset_nozzle_infos; + else + target_nozzle_groups = { preset_nozzle_infos[target_extruder_id] }; + + for (auto preset_groups : target_nozzle_groups) { + for (auto& preset_group : preset_groups) { + if (preset_group.nozzle_count == 0) { + if (std::find_if(printer_groups.begin(), printer_groups.end(), [&preset_group](const NozzleGroupInfo& elem) { return preset_group.is_same_type(elem); }) != + printer_groups.end()) { + is_synced = false; + break; + } + } + else if (std::find(printer_groups.begin(), printer_groups.end(), preset_group) == printer_groups.end()) { + is_synced = false; + break; + } + } + } + return is_synced; +} + Camera& Plater::priv::get_current_camera() { if (current_panel == assemble_view) { @@ -12312,12 +12876,13 @@ void Plater::priv::record_start_print_preset(std::string action) { for (int k = 0; k < values.size(); ++k) { std::string str = values[k]; const ConfigOption* config = full_config.option(str); - auto serialized = config->serialize(); - if (str == "post_process") { - serialized = sanitize_config_paths(serialized); - } - if (config) + if (config) { + auto serialized = config->serialize(); + if (str == "post_process") { + serialized = sanitize_config_paths(serialized); + } j_system[str] = serialized; + } } } } @@ -12806,7 +13371,7 @@ int Plater::save_project(bool saveAs) //QDS import model by model id void Plater::import_model_id(wxString download_info) { - BOOST_LOG_TRIVIAL(trace) << __FUNCTION__ << __LINE__ << " download info: " << download_info; + BOOST_LOG_TRIVIAL(trace) << __FUNCTION__ << __LINE__ << " start downloading"; wxString download_origin_url = download_info; wxString download_url; @@ -13408,6 +13973,7 @@ void Plater::_calib_pa_pattern(const Calib_Params ¶ms) // scale cube to suit test GizmoObjectManipulation &giz_obj_manip = p->view3D->get_canvas3d()->get_gizmos_manager().get_object_manipulation(); + giz_obj_manip.UpdateAndShow(true); giz_obj_manip.set_uniform_scaling(true); giz_obj_manip.on_change("size", 0, pa_pattern.handle_xy_size()); giz_obj_manip.set_uniform_scaling(false); @@ -14313,7 +14879,12 @@ void Plater::load_gcode(const wxString& filename) //p->preview->get_canvas3d()->zoom_to_gcode(); p->preview->get_canvas3d()->zoom_to_plate(0); p->partplate_list.get_curr_plate()->update_slice_result_valid_state(true); - current_print.apply(this->model(), wxGetApp().preset_bundle->full_config(false, current_print.get_filament_maps())); + std::vector f_maps = current_print.get_filament_maps(); + std::vector f_volume_maps = p->partplate_list.get_curr_plate()->get_filament_volume_maps(); + if (f_volume_maps.empty()) { + f_volume_maps = wxGetApp().preset_bundle->get_default_nozzle_volume_types_for_filaments(f_maps); + } + current_print.apply(this->model(), wxGetApp().preset_bundle->full_config(false, f_maps, f_volume_maps)); if (p->preview->get_canvas3d()->get_gcode_layers_zs().empty()) { MessageDialog(this, _L("The selected file") + ":\n" + filename + "\n" + _L("does not contain valid gcode."), @@ -15277,6 +15848,7 @@ void Plater::update_ui_from_settings() { p->update_ui_from_settings(); } void Plater::select_view(const std::string& direction) { p->select_view(direction); } +void Plater::set_slice_from_slice_btn(bool flag) { p->set_slice_from_slice_btn(flag); } //QDS: add no_slice logic void Plater::select_view_3D(const std::string& name, bool no_slice) { p->select_view_3D(name, no_slice); } @@ -15678,7 +16250,7 @@ void Plater::merge(size_t obj_idx, std::vector& vol_indeces) size_t last_id = p->model.objects.size() - 1; for (size_t i = 0; i < new_objects.size(); ++i) { - selection.add_object((unsigned int)(last_id - i), i == 0); + selection.add_volume((unsigned int)(last_id - i), 0, 0, i == 0); } } @@ -17655,7 +18227,12 @@ void Plater::on_config_change(const DynamicPrintConfig &config) old_nozzle_size = opt_old->values.size(); new_nozzle_size = opt_new->values.size(); } - + auto temp_is_system_preset = cur_selected_preset_is_system_preset(); + bool preset_custom_change = false; + if (temp_is_system_preset != m_last_is_system_preset) { + m_last_is_system_preset = temp_is_system_preset; + preset_custom_change = true; + } for (auto opt_key : diff_keys) { if (opt_key == "filament_colour") { update_scheduled = true; // update should be scheduled (for update 3DScene) #2738 @@ -17725,14 +18302,15 @@ void Plater::on_config_change(const DynamicPrintConfig &config) update_scheduled = true; } // QDS - else if (opt_key == "support_interface_filament" || - opt_key == "support_filament") { + else if (opt_key == "support_interface_filament" || opt_key == "support_filament" || opt_key == "wall_filament" || opt_key == "sparse_infill_filament" || + opt_key == "solid_infill_filament") { update_scheduled = true; } } - if (bed_shape_changed) + if (bed_shape_changed || preset_custom_change) { set_bed_shape(); + } config_change_notification(config, std::string("print_sequence")); @@ -17743,6 +18321,14 @@ void Plater::on_config_change(const DynamicPrintConfig &config) this->p->schedule_background_process(); } +bool Plater::cur_selected_preset_is_system_preset() +{ + PresetBundle &preset_bundle = *wxGetApp().preset_bundle; + auto & selected_preset = preset_bundle.printers.get_selected_preset(); + //bool is_qdt_preset = preset_bundle.printers.get_edited_preset().is_qdt_vendor_preset(&preset_bundle); + return selected_preset.is_system; +} + void Plater::update_flush_volume_matrix(size_t old_nozzle_size, size_t new_nozzle_size) { size_t nozzle_nums = wxGetApp().preset_bundle->get_printer_extruder_count(); @@ -17894,6 +18480,20 @@ std::vector Plater::get_extruder_colors_from_plater_config(const GC } } +bool Plater::is_color_size_equal() const +{ + const Slic3r::DynamicPrintConfig* config = &wxGetApp().preset_bundle->project_config; + if (!config->has("filament_multi_colour")) return false; + + const auto& multi_color = (config->option("filament_multi_colour"))->values; + const auto& single_color = (config->option("filament_colour"))->values; + if (multi_color.size() == single_color.size()) + { + return true; + } + return false; +} + std::vector Plater::get_filament_colors_render_info() const { const Slic3r::DynamicPrintConfig* config = &wxGetApp().preset_bundle->project_config; @@ -17955,12 +18555,23 @@ void Plater::set_global_filament_map(const std::vector& filament_map) project_config.option("filament_map")->values = filament_map; } +void Plater::set_global_filament_volume_map(const std::vector& filament_volume_map) +{ + auto& project_config = wxGetApp().preset_bundle->project_config; + project_config.option("filament_volume_map")->values = filament_volume_map; +} + std::vector Plater::get_global_filament_map() const { auto& project_config = wxGetApp().preset_bundle->project_config; return project_config.option("filament_map")->values; } +std::vector Plater::get_global_filament_volume_map() const +{ + auto& project_config = wxGetApp().preset_bundle->project_config; + return project_config.option("filament_volume_map")->values; +} FilamentMapMode Plater::get_global_filament_map_mode() const { @@ -18288,6 +18899,18 @@ void Plater::suppress_background_process(const bool stop_background_process) } void Plater::center_selection() { p->center_selection(); } +void Plater::distribute_selection_y() { p->distribute_selection_y(); } +void Plater::distribute_selection_x() { p->distribute_selection_x(); } +void Plater::distribute_selection_z() { p->distribute_selection_z(); } +void Plater::align_selection_y_max() { p->align_selection_y_max(); } +void Plater::align_selection_y_min() { p->align_selection_y_min(); } +void Plater::align_selection_y_center() { p->align_selection_y_center(); } +void Plater::align_selection_x_max() { p->align_selection_x_max(); } +void Plater::align_selection_x_min() { p->align_selection_x_min(); } +void Plater::align_selection_x_center() { p->align_selection_x_center(); } +void Plater::align_selection_z_max() { p->align_selection_z_max(); } +void Plater::align_selection_z_min() { p->align_selection_z_min(); } +void Plater::align_selection_z_center() { p->align_selection_z_center(); } void Plater::mirror(Axis axis) { p->mirror(axis); } void Plater::split_object() { p->split_object(); } void Plater::split_volume() { p->split_volume(); } @@ -18322,6 +18945,9 @@ void Plater::pop_warning_and_go_to_device_page(wxString printer_name, PrinterWar bool Plater::is_same_printer_for_connected_and_selected(bool popup_warning) { + if (!wxGetApp().getDeviceManager()) { + return false; + } MachineObject *obj = wxGetApp().getDeviceManager()->get_selected_machine(); if (obj == nullptr) { return false; @@ -18548,7 +19174,11 @@ void Plater::apply_background_progress() Print::ApplyStatus invalidated; if (preset_bundle->get_printer_extruder_count() > 1) { std::vector f_maps = part_plate->get_real_filament_maps(preset_bundle->project_config); - invalidated = p->background_process.apply(this->model(), preset_bundle->full_config(false, f_maps)); + std::vector f_volume_maps = part_plate->get_filament_volume_maps(); + if (f_volume_maps.empty()) { + f_volume_maps = preset_bundle->get_default_nozzle_volume_types_for_filaments(f_maps); + } + invalidated = p->background_process.apply(this->model(), preset_bundle->full_config(false, f_maps, f_volume_maps)); } else invalidated = p->background_process.apply(this->model(), preset_bundle->full_config(false)); @@ -18593,7 +19223,11 @@ int Plater::select_plate(int plate_index, bool need_slice) //always apply the current plate's print if (preset_bundle->get_printer_extruder_count() > 1) { std::vector f_maps = part_plate->get_real_filament_maps(preset_bundle->project_config); - invalidated = p->background_process.apply(this->model(), preset_bundle->full_config(false, f_maps)); + std::vector f_volume_maps = part_plate->get_filament_volume_maps(); + if (f_volume_maps.empty()) { + f_volume_maps = preset_bundle->get_default_nozzle_volume_types_for_filaments(f_maps); + } + invalidated = p->background_process.apply(this->model(), preset_bundle->full_config(false, f_maps, f_volume_maps)); } else invalidated = p->background_process.apply(this->model(), preset_bundle->full_config(false)); @@ -18923,19 +19557,24 @@ void Plater::open_filament_map_setting_dialog(wxCommandEvent &evt) int value = evt.GetInt(); //1 means from gcode view bool need_slice = value ==1; // If from gcode view, should slice + auto preset_bundle = wxGetApp().preset_bundle; const auto& project_config = wxGetApp().preset_bundle->project_config; auto filament_colors = config()->option("filament_colour")->values; auto filament_types = config()->option("filament_type")->values; auto plate_filament_maps = curr_plate->get_real_filament_maps(project_config); auto plate_filament_map_mode = curr_plate->get_filament_map_mode(); + auto plate_filament_volume_maps = curr_plate->get_real_filament_volume_maps(project_config); if (plate_filament_maps.size() != filament_colors.size()) // refine it later, save filament map to app config plate_filament_maps.resize(filament_colors.size(), 1); + if (plate_filament_volume_maps.size() != filament_colors.size()) + plate_filament_volume_maps.resize(filament_colors.size(), 0); FilamentMapDialog filament_dlg(this, filament_colors, filament_types, plate_filament_maps, + plate_filament_volume_maps, curr_plate->get_extruders(true), plate_filament_map_mode, this->get_machine_sync_status(), @@ -18949,16 +19588,21 @@ void Plater::open_filament_map_setting_dialog(wxCommandEvent &evt) FilamentMapMode old_map_mode = curr_plate->get_filament_map_mode(); FilamentMapMode new_map_mode = filament_dlg.get_mode(); + std::vector new_filament_volume_maps = filament_dlg.get_filament_volume_maps(); + std::vector old_filament_volume_maps = curr_plate->get_real_filament_volume_maps(project_config); + if (new_map_mode != old_map_mode) { curr_plate->set_filament_map_mode(new_map_mode); } if (new_map_mode == fmmManual){ curr_plate->set_filament_maps(new_filament_maps); + curr_plate->set_filament_volume_maps(new_filament_volume_maps); } bool need_invalidate = (old_map_mode != new_map_mode || - old_filament_maps != new_filament_maps); + old_filament_maps != new_filament_maps || + old_filament_volume_maps != new_filament_volume_maps); if (need_invalidate) { if (need_slice) { @@ -18974,16 +19618,20 @@ void Plater::open_filament_map_setting_dialog(wxCommandEvent &evt) return; } - +bool Plater::force_ban_check_volume_bbox_state_with_extruder_area() { + return m_force_ban_check_volume_bbox_state_with_extruder_area; +} //QDS: select Plate by hover_id -int Plater::select_plate_by_hover_id(int hover_id, bool right_click, bool isModidyPlateName) +int Plater::select_plate_by_hover_id(int hover_id, bool right_click, bool isModidyPlateName, bool is_swap_plate) { int ret; int action, plate_index; plate_index = hover_id / PartPlate::GRABBER_COUNT; action = isModidyPlateName ? PartPlate::PLATE_NAME_ID : hover_id % PartPlate::GRABBER_COUNT; - + if (is_swap_plate) { + action = -1; + } BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": enter, hover_id %1%, plate_index %2%, action %3%")%hover_id % plate_index %action; if (action == 0) { @@ -19012,7 +19660,11 @@ int Plater::select_plate_by_hover_id(int hover_id, bool right_click, bool isModi //always apply the current plate's print if (preset_bundle->get_printer_extruder_count() > 1) { std::vector f_maps = part_plate->get_real_filament_maps(preset_bundle->project_config); - invalidated = p->background_process.apply(this->model(), preset_bundle->full_config(false, f_maps)); + std::vector f_volume_maps = part_plate->get_filament_volume_maps(); + if (f_volume_maps.empty()) { + f_volume_maps = preset_bundle->get_default_nozzle_volume_types_for_filaments(f_maps); + } + invalidated = p->background_process.apply(this->model(), preset_bundle->full_config(false, f_maps, f_volume_maps)); } else invalidated = p->background_process.apply(this->model(), preset_bundle->full_config(false)); @@ -19172,6 +19824,31 @@ int Plater::select_plate_by_hover_id(int hover_id, bool right_click, bool isModi BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "can not select plate %1%" << plate_index; ret = -1; } + } else if (is_swap_plate) { + ret = select_plate(plate_index); + if (!ret) { + PlateMoveDialog dlg(this, wxID_ANY, _L("Move Plate")); + int result = dlg.ShowModal(); + if (result == wxID_YES) { + int swap_index = dlg.get_swap_index(); + int cur_index = p->partplate_list.get_curr_plate_index(); + if (cur_index != swap_index && swap_index >= 0) { + take_snapshot("move plate to the front"); + m_force_ban_check_volume_bbox_state_with_extruder_area = true; + ret = p->partplate_list.move_plate_to_index(plate_index, swap_index); + p->partplate_list.update_slice_context_to_current_plate(p->background_process); + p->preview->update_gcode_result(p->partplate_list.get_current_slice_result()); + p->sidebar->obj_list()->reload_all_plates(); + p->partplate_list.update_plates(); + update(); + m_force_ban_check_volume_bbox_state_with_extruder_area = false; + p->partplate_list.select_plate(swap_index); + } + } + } else { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "can not select plate %1%" << plate_index; + ret = -1; + } } else { @@ -19535,6 +20212,28 @@ void Plater::update_machine_sync_status() GUI::wxGetApp().sidebar().update_sync_status(obj); } +void Plater::update_filament_volume_map(int extruder_id, int volume_type) +{ + int selected_volume_type = volume_type == static_cast(NozzleVolumeType::nvtHybrid) ? 0 : volume_type; + auto& partplate_list = get_partplate_list(); + for (int idx = 0; idx < partplate_list.get_plate_count(); ++idx) { + auto plate = partplate_list.get_plate(idx); + if (!plate) continue; + auto filament_map = plate->get_filament_maps(); + auto filament_volume_map = plate->get_filament_volume_maps(); + if (filament_map.empty() || filament_volume_map.empty()) continue; + if (filament_volume_map.size() < filament_map.size()) { + filament_volume_map.resize(filament_map.size(), 0); + } + for (int i = 0; i < filament_map.size(); ++i) { + if (filament_map[i] == extruder_id + 1) { + filament_volume_map[i] = selected_volume_type; + } + } + plate->set_filament_volume_maps(filament_volume_map); + } +} + void Plater::show_wrapping_detect_dialog_if_necessary() { if ((wxGetApp().app_config->get("show_wrapping_detect_dialog") == "true")) { @@ -19553,6 +20252,11 @@ bool Plater::get_machine_sync_status() return p->get_machine_sync_status(); } +bool Plater::is_extruder_stat_synced(int target_extruder_id) +{ + return p->is_extruder_stat_synced(target_extruder_id); +} + #if ENABLE_ENVIRONMENT_MAP void Plater::init_environment_texture() { diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 65e1234..45fd59a 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -129,6 +129,7 @@ wxDECLARE_EVENT(EVT_NOTICE_CHILDE_SIZE_CHANGED, SimpleEvent); wxDECLARE_EVENT(EVT_NOTICE_FULL_SCREEN_CHANGED, IntEvent); using ColorEvent = Event; wxDECLARE_EVENT(EVT_ADD_CUSTOM_FILAMENT, ColorEvent); +wxDECLARE_EVENT(EVT_SWITCH_TO_PREPARE_TAB, wxCommandEvent); // helio wxDECLARE_EVENT(EVT_HELIO_PROCESSING_COMPLETED, HelioCompletionEvent); @@ -227,6 +228,9 @@ public: void on_full_screen(IntEvent &); void get_big_btn_sync_pos_size(wxPoint &pt, wxSize &size); void get_small_btn_sync_pos_size(wxPoint &pt, wxSize &size); + void set_extruder_nozzle_count(int extruder_id, int nozzle_count); + void enable_nozzle_count_edit(bool enable); + void enable_purge_mode_btn(bool enable); //w42 //y59 int box_count; @@ -300,6 +304,9 @@ public: bool need_auto_sync_after_connect_printer() const { return m_need_auto_sync_after_connect_printer; } void set_need_auto_sync_after_connect_printer(bool need_auto_sync) { m_need_auto_sync_after_connect_printer = need_auto_sync; } + //y75 + int get_combos_filament_size() { return static_cast(combos_filament().size()); } + private: void auto_calc_flushing_volumes_internal(const int filament_id, const int extruder_id); @@ -320,6 +327,9 @@ private: class Plater: public wxPanel { + bool m_force_ban_check_volume_bbox_state_with_extruder_area{false}; + bool m_last_is_system_preset{true}; + public: using fs_path = boost::filesystem::path; @@ -453,6 +463,7 @@ public: bool is_any_job_running() const; void select_view(const std::string& direction); //QDS: add no_slice logic + void set_slice_from_slice_btn(bool flag); void select_view_3D(const std::string& name, bool no_slice = true); void reload_paint_after_background_process_apply(); @@ -610,6 +621,7 @@ public: bool update_filament_colors_in_full_config(); void config_change_notification(const DynamicPrintConfig &config, const std::string& key); void on_config_change(const DynamicPrintConfig &config); + bool cur_selected_preset_is_system_preset(); void force_filament_colors_update(); void force_print_bed_update(); // On activating the parent window. @@ -618,11 +630,14 @@ public: std::vector get_filament_colors_render_info() const; std::vector get_filament_color_render_type() const; std::vector get_colors_for_color_print(const GCodeProcessorResult* const result = nullptr) const; + bool is_color_size_equal() const; void set_global_filament_map_mode(FilamentMapMode mode); void set_global_filament_map(const std::vector& filament_map); + void set_global_filament_volume_map(const std::vector& volume_map); std::vector get_global_filament_map() const; FilamentMapMode get_global_filament_map_mode() const; + std::vector get_global_filament_volume_map() const; void update_menus(); wxString get_selected_printer_name_in_combox(); @@ -693,6 +708,18 @@ public: //QDS: add clone logic void clone_selection(); void center_selection(); + void distribute_selection_y(); + void distribute_selection_x(); + void distribute_selection_z(); + void align_selection_y_max(); + void align_selection_y_min(); + void align_selection_y_center(); + void align_selection_x_max(); + void align_selection_x_min(); + void align_selection_x_center(); + void align_selection_z_max(); + void align_selection_z_min(); + void align_selection_z_center(); void search(bool plater_is_active, Preset::Type type, wxWindow *tag, TextInput *etag, wxWindow *stag); void mirror(Axis axis); void split_object(); @@ -756,6 +783,7 @@ public: const Camera& get_picking_camera() const; Camera& get_picking_camera(); + bool force_ban_check_volume_bbox_state_with_extruder_area(); //QDS: partplate list related functions PartPlateList& get_partplate_list(); void validate_current_plate(bool& model_fits, bool& validate_error); @@ -764,7 +792,7 @@ public: //QDS: update progress result void apply_background_progress(); //QDS: select the plate by hover_id - int select_plate_by_hover_id(int hover_id, bool right_click = false, bool isModidyPlateName = false); + int select_plate_by_hover_id(int hover_id, bool right_click = false, bool isModidyPlateName = false, bool is_swap_plate = false); //QDS: delete the plate, index= -1 means the current plate int delete_plate(int plate_index = -1); //QDS: select the sliced plate by index @@ -792,8 +820,10 @@ public: bool check_ams_status(bool is_slice_all); // only check sync status and printer model id bool get_machine_sync_status(); - + // check whether nozzle staus is synced with printer, extruder = -1 means check both extruder + bool is_extruder_stat_synced(int extruder_id = -1); void update_machine_sync_status(); + void update_filament_volume_map(int extruder_id, int volume_type); void show_wrapping_detect_dialog_if_necessary(); diff --git a/src/slic3r/GUI/PrePrintChecker.cpp b/src/slic3r/GUI/PrePrintChecker.cpp index 289b5cc..1f2c73e 100644 --- a/src/slic3r/GUI/PrePrintChecker.cpp +++ b/src/slic3r/GUI/PrePrintChecker.cpp @@ -1,8 +1,12 @@ #include "PrePrintChecker.hpp" +#include "SelectMachine.hpp" #include "GUI_Utils.hpp" #include "I18N.hpp" #include +#include "slic3r/GUI/DeviceCore/DevNozzleSystem.h" +#include "slic3r/GUI/DeviceCore/DevNozzleRack.h" + namespace Slic3r { namespace GUI { @@ -23,6 +27,8 @@ std::string PrePrintChecker::get_print_status_info(PrintDialogStatus status) case PrintStatusInSystemPrinting: return "PrintStatusInSystemPrinting"; case PrintStatusInPrinting: return "PrintStatusInPrinting"; case PrintStatusNozzleMatchInvalid: return "PrintStatusNozzleMatchInvalid"; + case PrintStatusNozzleNoMatchedHotends: return "PrintStatusNozzleNoMatchedHotends"; + case PrintStatusNozzleRackMaximumInstalled: return "PrintStatusNozzleRackMaximumInstalled"; case PrintStatusNozzleDataInvalid: return "PrintStatusNozzleDataInvalid"; case PrintStatusNozzleDiameterMismatch: return "PrintStatusNozzleDiameterMismatch"; case PrintStatusNozzleTypeMismatch: return "PrintStatusNozzleTypeMismatch"; @@ -38,6 +44,10 @@ std::string PrePrintChecker::get_print_status_info(PrintDialogStatus status) case PrintStatusUnsupportedPrinter: return "PrintStatusUnsupportedPrinter"; case PrintStatusColorQuantityExceed: return "PrintStatusColorQuantityExceed"; // Handle filament errors + case PrintStatusRackReading: return "PrintStatusRackReading"; + case PrintStatusRackNozzleMappingWaiting: return "PrintStatusRackNozzleMappingWaiting"; + case PrintStatusRackNozzleMappingError: return "PrintStatusRackNozzleMappingError"; + case PrintStatusInvalidMapping: return "PrintStatusInvalidMapping"; case PrintStatusAmsOnSettingup: return "PrintStatusAmsOnSettingup"; case PrintStatusAmsMappingInvalid: return "PrintStatusAmsMappingInvalid"; case PrintStatusAmsMappingU0Invalid: return "PrintStatusAmsMappingU0Invalid"; @@ -47,6 +57,10 @@ std::string PrePrintChecker::get_print_status_info(PrintDialogStatus status) case PrintStatusTimelapseNoSdcard: return "PrintStatusTimelapseNoSdcard"; case PrintStatusTimelapseWarning: return "PrintStatusTimelapseWarning"; case PrintStatusMixAmsAndVtSlotWarning: return "PrintStatusMixAmsAndVtSlotWarning"; + case PrintStatusToolHeadCoolingFanWarning: return "PrintStatusToolHeadCoolingFanWarning"; + case PrintStatusHasUnreliableNozzleWarning: return "PrintStatusRackHasUnreliableNozzleWarning"; + case PrintStatusRackNozzleNumUnmeetWarning: return "PrintStatusRackNozzleNumUnmeetWarning"; + case PrintStatusRackNozzleMappingWarning: return "PrintStatusRackNozzleMappingWarning"; case PrintStatusWarningKvalueNotUsed: return "PrintStatusWarningKvalueNotUsed"; case PrintStatusHasFilamentInBlackListWarning: return "PrintStatusHasFilamentInBlackListWarning"; case PrintStatusFilamentWarningHighChamberTemp: return "PrintStatusFilamentWarningHighChamberTemp"; @@ -92,6 +106,8 @@ wxString PrePrintChecker::get_pre_state_msg(PrintDialogStatus status) case PrintStatusWarningKvalueNotUsed: return _L("Set dynamic flow calibration to 'OFF' to enable custom dynamic flow value."); case PrintStatusNotSupportedPrintAll: return _L("This printer does not support printing all plates"); case PrintStatusColorQuantityExceed: return _L("The current firmware supports a maximum of 16 materials. You can either reduce the number of materials to 16 or fewer on the Preparation Page, or try updating the firmware. If you are still restricted after the update, please wait for subsequent firmware support."); + case PrintStatusFilamentWarningHighChamberTempCloseDoor: return _L("High chamber temperature is required. Please close the door."); + case PrintStatusHasUnreliableNozzleWarning: return _L("Please check if the required nozzle diameter and flow rate match the current display."); } return wxEmptyString; } @@ -102,7 +118,7 @@ void PrePrintChecker::clear() filamentList.clear(); } -void PrePrintChecker::add(PrintDialogStatus state, wxString msg, wxString tip, const wxString& wiki_url) +void PrePrintChecker::add(PrintDialogStatus state, wxString msg, wxString tip, const wxString& wiki_url, prePrintInfoStyle style) { prePrintInfo info; @@ -133,6 +149,7 @@ void PrePrintChecker::add(PrintDialogStatus state, wxString msg, wxString tip, c } info.wiki_url = wiki_url; + info.m_style = style; switch (info.type) { case prePrintInfoType::Filament: @@ -149,42 +166,27 @@ void PrePrintChecker::add(PrintDialogStatus state, wxString msg, wxString tip, c } } - -//void PrePrintMsgBoard::add(const wxString &msg, const wxString &tips, bool is_error) -//{ -// if (msg.IsEmpty()) { return; } -// -// /*message*/ -// // create label -// if (!m_sizer->IsEmpty()) { m_sizer->AddSpacer(FromDIP(10)); } -// Label *msg_label = new Label(this, wxEmptyString); -// m_sizer->Add(msg_label, 0, wxLEFT, 0); -// -// // set message -// msg_label->SetLabel(msg); -// msg_label->SetMinSize(wxSize(FromDIP(420), -1)); -// msg_label->SetMaxSize(wxSize(FromDIP(420), -1)); -// msg_label->Wrap(FromDIP(420)); -// -// // font color -// auto colour = is_error ? wxColour("#D01B1B") : wxColour(0xFF, 0x6F, 0x00); -// msg_label->SetForegroundColour(colour); -// -// /*tips*/ -// if (!tips.IsEmpty()) { /*Not supported yet*/ -// } -// -// Layout(); -// Fit(); -//} - -PrinterMsgPanel::PrinterMsgPanel(wxWindow *parent) - : wxPanel(parent) +PrinterMsgPanel::PrinterMsgPanel(wxWindow *parent, SelectMachineDialog* select_dialog) + : wxPanel(parent), m_select_dialog(select_dialog) { m_sizer = new wxBoxSizer(wxVERTICAL); this->SetSizer(m_sizer); } +void PrinterMsgPanel::Clear() +{ + m_infos.clear(); + m_not_show_again_infos.clear(); + ClearGUI(); +} + +void PrinterMsgPanel::ClearGUI() +{ + m_sizer->Clear(true); + m_scale_btns.clear(); + m_ctrl_btns.clear(); +} + static wxColour _GetLabelColour(const prePrintInfo& info) { if (info.level == Error) @@ -207,16 +209,19 @@ bool PrinterMsgPanel::UpdateInfos(const std::vector& infos) } m_infos = infos; - m_sizer->Clear(true); + ClearGUI(); for (const prePrintInfo& info : infos) { + if (m_not_show_again_infos.count(info) != 0) { + continue; + }; + if (!info.msg.empty()) { Label* label = new Label(this); label->SetFont(::Label::Body_13); label->SetForegroundColour(_GetLabelColour(info)); - if (info.wiki_url.empty()) { label->SetLabel(info.msg); @@ -229,9 +234,18 @@ bool PrinterMsgPanel::UpdateInfos(const std::vector& infos) label->Bind(wxEVT_LEFT_DOWN, [info](wxMouseEvent& event) { wxLaunchDefaultBrowser(info.wiki_url); }); } - label->Wrap(this->GetMinSize().GetWidth()); - label->Show(); - m_sizer->Add(label, 0, wxBOTTOM, FromDIP(4)); + ScalableButton* btn = CreateTypeButton(info); + label->Wrap(this->GetMinSize().GetWidth() - btn->GetSize().x - FromDIP(6)); + + wxSizer* msg_sizer = new wxBoxSizer(wxHORIZONTAL); + msg_sizer->Add(btn, 0, wxLEFT | wxTOP, FromDIP(2)); + msg_sizer->AddSpacer(FromDIP(2)); + msg_sizer->Add(label, 0, wxLEFT | wxBOTTOM); + msg_sizer->Layout(); + m_sizer->Add(msg_sizer, 0, wxBOTTOM, FromDIP(4)); + + // some special styles + AppendStyles(info); } } @@ -243,6 +257,117 @@ bool PrinterMsgPanel::UpdateInfos(const std::vector& infos) return true; } +void PrinterMsgPanel::Rescale() +{ + for (auto item : m_scale_btns) { + item->msw_rescale(); + } + + for (auto item : m_ctrl_btns) { + item->Rescale(); + } + + Layout(); + Fit(); +} + +ScalableButton* PrinterMsgPanel::CreateTypeButton(const prePrintInfo& info) +{ + ScalableButton* btn = nullptr; + if (info.level == Error) { + btn = new ScalableButton(this, wxID_ANY, "dev_error"); + } else if (info.level == Warning) { + btn = new ScalableButton(this, wxID_ANY, "dev_warning"); + } else { + btn = new ScalableButton(this, wxID_ANY, "dev_warning"); + } + + btn->SetBackgroundColour(*wxWHITE); + btn->SetMaxSize(wxSize(FromDIP(16), FromDIP(16))); + btn->SetMinSize(wxSize(FromDIP(16), FromDIP(16))); + btn->SetSize(wxSize(FromDIP(16), FromDIP(16))); + wxGetApp().UpdateDarkUI(btn); + m_scale_btns.push_back(btn); + return btn; +}; + +static Label* s_create_btn_label(PrinterMsgPanel* panel, const wxString& btn_name) +{ + Label* btn = new Label(panel, btn_name); + btn->SetFont(Label::Body_13); + auto font = btn->GetFont(); + font.SetUnderlined(true); + btn->SetFont(font); + btn->SetBackgroundColour(*wxWHITE); + btn->SetForegroundColour(wxColour("#4479fb")); + + btn->Bind(wxEVT_ENTER_WINDOW, [panel](auto &e) { panel->SetCursor(wxCURSOR_HAND); }); + btn->Bind(wxEVT_LEAVE_WINDOW, [panel](auto &e) { panel->SetCursor(wxCURSOR_ARROW); }); + + wxGetApp().UpdateDarkUI(btn); + return btn; +} + +void PrinterMsgPanel::AppendStyles(const prePrintInfo& info) +{ + // special styles + if (info.testStyle(prePrintInfoStyle::BtnNozzleRefresh) || + info.testStyle(prePrintInfoStyle::BtnConfirmNotShowAgain) || + info.testStyle(prePrintInfoStyle::BtnInstallFanF000)) { + + wxBoxSizer* btn_sizer = new wxBoxSizer(wxHORIZONTAL); + if (info.testStyle(prePrintInfoStyle::BtnNozzleRefresh)){ + auto btn = s_create_btn_label(this, _L("Refresh")); + btn->Bind(wxEVT_LEFT_DOWN, &PrinterMsgPanel::OnRefreshNozzleBtnClicked, this); + btn_sizer->Add(btn, 0, wxLEFT, FromDIP(16)); + } + + if (info.testStyle(prePrintInfoStyle::BtnConfirmNotShowAgain)) { + auto btn = s_create_btn_label(this, _L("Confirm")); + btn->Bind(wxEVT_LEFT_DOWN, [this, info](auto& e) { + this->OnNotShowAgain(info); + }); + + btn_sizer->Add(btn, 0, wxLEFT, FromDIP(16)); + } + + if (info.testStyle(prePrintInfoStyle::BtnInstallFanF000)) { + auto btn = s_create_btn_label(this, _L("How to install")); + btn->Bind(wxEVT_LEFT_DOWN, [this, info](auto& e) { + wxLaunchDefaultBrowser("https://wiki.qidi3d.com/en/software/qidi-studio"); + }); + + btn_sizer->Add(btn, 0, wxLEFT, FromDIP(16)); + } + + m_sizer->Add(btn_sizer, 0, wxLEFT); + m_sizer->AddSpacer(FromDIP(4)); + } + + if (info.testStyle(prePrintInfoStyle::NozzleState)) { + NozzleStatePanel* nozzle_info = new NozzleStatePanel(this); + nozzle_info->UpdateInfoBy(m_select_dialog->get_plater(), m_select_dialog->get_current_machine()); + m_sizer->Add(nozzle_info, 0, wxLEFT, FromDIP(16)); + m_sizer->AddSpacer(FromDIP(4)); + } +} + +void PrinterMsgPanel::OnRefreshNozzleBtnClicked(wxMouseEvent& event) +{ + auto obj_ = m_select_dialog->get_current_machine(); + if (obj_) { + obj_->GetNozzleSystem()->GetNozzleRack()->CtrlRackReadAll(true); + } +} + +void PrinterMsgPanel::OnNotShowAgain(const prePrintInfo& info) +{ + m_not_show_again_infos.insert(info); + + auto cp_infos = m_infos; + m_infos.clear(); + UpdateInfos(cp_infos); +} } }; diff --git a/src/slic3r/GUI/PrePrintChecker.hpp b/src/slic3r/GUI/PrePrintChecker.hpp index bfa1a69..ee0f402 100644 --- a/src/slic3r/GUI/PrePrintChecker.hpp +++ b/src/slic3r/GUI/PrePrintChecker.hpp @@ -3,6 +3,10 @@ #include #include "Widgets/Label.hpp" + +class Button; +class ScalableButton; + namespace Slic3r { namespace GUI { enum prePrintInfoLevel { @@ -16,10 +20,25 @@ enum prePrintInfoType { Filament }; +enum class prePrintInfoStyle : int +{ + Default = 0, + NozzleState = 0x001, + BtnNozzleRefresh = 0x002, + BtnConfirmNotShowAgain = 0x004, + BtnInstallFanF000 = 0x008, +}; + +inline constexpr prePrintInfoStyle operator|(prePrintInfoStyle a, prePrintInfoStyle b) noexcept +{ + return static_cast(static_cast(a) | static_cast(b)); +} + struct prePrintInfo { prePrintInfoLevel level; prePrintInfoType type; + prePrintInfoStyle m_style = prePrintInfoStyle::Default; wxString msg; wxString tips; wxString wiki_url; @@ -29,7 +48,28 @@ public: bool operator==(const prePrintInfo& other) const { return level == other.level && type == other.type && msg == other.msg && tips == other.tips && - wiki_url == other.wiki_url && index == other.index; + wiki_url == other.wiki_url && index == other.index && + m_style == other.m_style; + } + + bool operator<(const prePrintInfo& other) const { + if (level != other.level) + return level < other.level; + if (type != other.type) + return type < other.type; + if (msg != other.msg) + return msg < other.msg; + if (tips != other.tips) + return tips < other.tips; + if (wiki_url != other.wiki_url) + return wiki_url < other.wiki_url; + if (index != other.index) + return index < other.index; + return m_style < other.m_style; + } + + bool testStyle(prePrintInfoStyle style) const { + return (static_cast(m_style) & static_cast(style)) == static_cast(style); } }; @@ -52,6 +92,8 @@ enum PrintDialogStatus : unsigned int { PrintStatusInSystemPrinting, PrintStatusInPrinting, PrintStatusNozzleMatchInvalid, + PrintStatusNozzleNoMatchedHotends, + PrintStatusNozzleRackMaximumInstalled, PrintStatusNozzleDataInvalid, PrintStatusNozzleDiameterMismatch, PrintStatusNozzleTypeMismatch, @@ -65,6 +107,10 @@ enum PrintDialogStatus : unsigned int { PrintStatusNotSupportedPrintAll, PrintStatusBlankPlate, PrintStatusUnsupportedPrinter, + PrintStatusRackReading, + PrintStatusRackNozzleMappingWaiting, + PrintStatusRackNozzleMappingError, + PrintStatusInvalidMapping, PrintStatusPrinterErrorEnd, // Errors for filament, Block Print @@ -89,6 +135,9 @@ enum PrintDialogStatus : unsigned int { PrintStatusTimelapseWarning, PrintStatusMixAmsAndVtSlotWarning, PrintStatusToolHeadCoolingFanWarning, + PrintStatusHasUnreliableNozzleWarning, + PrintStatusRackNozzleNumUnmeetWarning, + PrintStatusRackNozzleMappingWarning, PrintStatusPrinterWarningEnd, // Warnings for filament @@ -138,6 +187,7 @@ enum PrintDialogStatus : unsigned int { }; +class NozzleStatePanel; class PrePrintChecker { public: @@ -147,7 +197,7 @@ public: public: void clear(); /*auto merge*/ - void add(PrintDialogStatus state, wxString msg, wxString tip, const wxString& wiki_url); + void add(PrintDialogStatus state, wxString msg, wxString tip, const wxString& wiki_url, prePrintInfoStyle style); static ::std::string get_print_status_info(PrintDialogStatus status); wxString get_pre_state_msg(PrintDialogStatus status); @@ -161,47 +211,36 @@ public: static bool is_warning_printer(PrintDialogStatus status) { return (PrintStatusPrinterWarningBegin < status) && (PrintStatusPrinterWarningEnd > status); }; static bool is_warning_filament(PrintDialogStatus status) { return (PrintStatusFilamentWarningBegin < status) && (PrintStatusFilamentWarningEnd > status); }; }; -//class PrePrintMsgBoard : public wxWindow -//{ -//public: -// PrePrintMsgBoard(wxWindow * parent, -// wxWindowID winid = wxID_ANY, -// const wxPoint & pos = wxDefaultPosition, -// const wxSize & size = wxDefaultSize, -// long style = wxTAB_TRAVERSAL | wxNO_BORDER, -// const wxString &name = wxASCII_STR(wxPanelNameStr) -// ); -// -//public: -// // Operations -// void addError(const wxString &msg, const wxString &tips = wxEmptyString) { Add(msg, tips, true); }; -// void addWarning(const wxString &msg, const wxString &tips = wxEmptyString) { Add(msg, tips, false); }; -// void clear() { m_sizer->Clear(); }; -// -// // Const Access -// bool isEmpty() const { return m_sizer->IsEmpty(); } -// -//private: -// void add(const wxString &msg, const wxString &tips, bool is_error); -// -//private: -// wxBoxSizer *m_sizer{nullptr}; -//}; - - +class SelectMachineDialog; class PrinterMsgPanel : public wxPanel { public: - PrinterMsgPanel(wxWindow *parent); + PrinterMsgPanel(wxWindow *parent, SelectMachineDialog* select_dialog); public: + void Clear(); bool UpdateInfos(const std::vector& infos); + void Rescale(); + +private: + void ClearGUI(); + void AppendStyles(const prePrintInfo& info); + ScalableButton* CreateTypeButton(const prePrintInfo& info); + + // events + void OnNotShowAgain(const prePrintInfo& info); + void OnRefreshNozzleBtnClicked(wxMouseEvent& event); private: + SelectMachineDialog* m_select_dialog = nullptr; + wxBoxSizer* m_sizer = nullptr; std::vector m_infos; + std::vector m_scale_btns; + std::vector m_ctrl_btns; + std::set m_not_show_again_infos; }; diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 078d53c..2759301 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -1246,11 +1246,9 @@ wxWindow* PreferencesDialog::create_general_page() p_ogl_manager->set_toolbar_rendering_style(idx); }); -#ifdef QDT_ENABLE_ADVANCED_GCODE_VIEWER auto enable_advanced_gcode_viewer = create_item_checkbox(_L("Enable advanced gcode viewer"), page, _L("Enable advanced gcode viewer."), 50, - "enable_advanced_gcode_viewer"); -#endif + "enable_advanced_gcode_viewer_"); float range_min = 1.0, range_max = 2.5; auto item_grabber_size_settings = create_item_range_input(_L("Grabber scale"), page, @@ -1364,9 +1362,7 @@ wxWindow* PreferencesDialog::create_general_page() sizer_page->Add(item_enable_record_gcodeviewer_option_item, 0, wxTOP, FromDIP(3)); sizer_page->Add(enable_lod_settings, 0, wxTOP, FromDIP(3)); -#ifdef QDT_ENABLE_ADVANCED_GCODE_VIEWER sizer_page->Add(enable_advanced_gcode_viewer, 0, wxTOP, FromDIP(3)); -#endif sizer_page->Add(item_toolbar_style, 0, wxTOP, FromDIP(3)); sizer_page->Add(item_grabber_size_settings, 0, wxTOP, FromDIP(3)); sizer_page->Add(title_presets, 0, wxTOP | wxEXPAND, FromDIP(20)); diff --git a/src/slic3r/GUI/PrintOptionsDialog.cpp b/src/slic3r/GUI/PrintOptionsDialog.cpp index e1665a4..0ca630c 100644 --- a/src/slic3r/GUI/PrintOptionsDialog.cpp +++ b/src/slic3r/GUI/PrintOptionsDialog.cpp @@ -144,6 +144,19 @@ PrintOptionsDialog::PrintOptionsDialog(wxWindow* parent) } evt.Skip(); }); + m_cb_plate_type->Bind(wxEVT_TOGGLEBUTTON, [this](wxCommandEvent& evt) { + if (obj) { + obj->command_xcam_control_build_plate_type_detector(m_cb_plate_type->GetValue()); + } + evt.Skip(); + }); + m_cb_plate_align->Bind(wxEVT_TOGGLEBUTTON, [this](wxCommandEvent& evt) { + if (obj) { + obj->command_xcam_control_build_plate_align_detector(m_cb_plate_align->GetValue()); + } + evt.Skip(); + }); + m_cb_sup_sound->Bind(wxEVT_TOGGLEBUTTON, [this](wxCommandEvent& evt) { if (obj) { obj->command_xcam_control_allow_prompt_sound(m_cb_sup_sound->GetValue()); @@ -360,9 +373,23 @@ void PrintOptionsDialog::update_options(MachineObject* obj_) airprinting_bottom_space->Show(false); } + if (obj_->is_support_build_plate_type_detect || obj_->is_support_build_plate_align_detect){ + text_plate_build->Show(); + text_plate_build_caption->Show(); + m_cb_plate_type->Show(obj_->is_support_build_plate_type_detect); + text_plate_type->Show(obj_->is_support_build_plate_type_detect); + text_plate_type_caption->Show(obj_->is_support_build_plate_type_detect); - if (obj_->is_support_build_plate_marker_detect) { + m_cb_plate_align->Show(obj_->is_support_build_plate_align_detect); + text_plate_align->Show(obj_->is_support_build_plate_align_detect); + text_plate_align_caption->Show(obj_->is_support_build_plate_align_detect); + + text_plate_mark->Hide(); + m_cb_plate_mark->Hide(); + text_plate_mark_caption->Hide(); + } + else if (obj_->is_support_build_plate_marker_detect) { if (obj_->m_plate_maker_detect_type == MachineObject::POS_CHECK && (text_plate_mark->GetLabel() != _L("Enable detection of build plate position"))) { text_plate_mark->SetLabel(_L("Enable detection of build plate position")); text_plate_mark_caption->SetLabel(_L("The localization tag of build plate is detected, and printing is paused if the tag is not in predefined range.")); @@ -376,12 +403,35 @@ void PrintOptionsDialog::update_options(MachineObject* obj_) text_plate_mark->Show(); m_cb_plate_mark->Show(); text_plate_mark_caption->Show(); + + text_plate_build->Hide(); + text_plate_build_caption->Hide(); + + m_cb_plate_type->Hide(); + text_plate_type->Hide(); + text_plate_type_caption->Hide(); + + m_cb_plate_align->Hide(); + text_plate_align->Hide(); + text_plate_align_caption->Hide(); // line2->Show(); } else { text_plate_mark->Hide(); m_cb_plate_mark->Hide(); text_plate_mark_caption->Hide(); + + text_plate_build->Hide(); + text_plate_build_caption->Hide(); + + m_cb_plate_type->Hide(); + text_plate_type->Hide(); + text_plate_type_caption->Hide(); + + m_cb_plate_align->Hide(); + text_plate_align->Hide(); + text_plate_align_caption->Hide(); + line2->Hide(); } @@ -450,6 +500,8 @@ void PrintOptionsDialog::update_options(MachineObject* obj_) m_cb_sup_sound->SetValue(obj_->xcam_allow_prompt_sound); m_cb_filament_tangle->SetValue(obj_->xcam_filament_tangle_detect); m_cb_nozzle_blob->SetValue(obj_->nozzle_blob_detection_enabled); + m_cb_plate_type->SetValue(obj_->xcam_build_plate_type_detect.GetValue()); + m_cb_plate_align->SetValue(obj_->xcam_build_plate_align_detect.GetValue()); m_cb_ai_monitoring->SetValue(obj_->xcam_ai_monitoring); @@ -641,7 +693,7 @@ wxBoxSizer* PrintOptionsDialog::create_settings_group(wxWindow* parent) ai_refine_sizer->Add(line_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(18)); line_sizer = new wxBoxSizer(wxHORIZONTAL); - text_spaghetti_detection_caption0 = new Label(ai_refine_panel, _L("Detect spaghetti failure(scattered lose filament).")); + text_spaghetti_detection_caption0 = new Label(ai_refine_panel, _L("Detects spaghetti failure(scattered lose filament).")); text_spaghetti_detection_caption0->SetFont(Label::Body_12); text_spaghetti_detection_caption0->SetForegroundColour(STATIC_TEXT_CAPTION_COL); text_spaghetti_detection_caption0->Wrap(-1); @@ -685,7 +737,7 @@ wxBoxSizer* PrintOptionsDialog::create_settings_group(wxWindow* parent) ai_refine_sizer->Add(line_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(18)); line_sizer = new wxBoxSizer(wxHORIZONTAL); - text_purgechutepileup_detection_caption0 = new Label(ai_refine_panel, _L("Monitor if the waste is piled up in the purge chute.")); + text_purgechutepileup_detection_caption0 = new Label(ai_refine_panel, _L("Monitors if the waste is piled up in the purge chute.")); text_purgechutepileup_detection_caption0->SetFont(Label::Body_12); text_purgechutepileup_detection_caption0->SetForegroundColour(STATIC_TEXT_CAPTION_COL); text_purgechutepileup_detection_caption0->Wrap(-1); @@ -725,7 +777,7 @@ wxBoxSizer* PrintOptionsDialog::create_settings_group(wxWindow* parent) ai_refine_sizer->Add(line_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(18)); line_sizer = new wxBoxSizer(wxHORIZONTAL); - text_nozzleclumping_detection_caption0 = new Label(ai_refine_panel, _L("Check if the nozzle is clumping by filaments or other foreign objects.")); + text_nozzleclumping_detection_caption0 = new Label(ai_refine_panel, _L("Checks if the nozzle is clumping by filaments or other foreign objects.")); text_nozzleclumping_detection_caption0->SetFont(Label::Body_12); text_nozzleclumping_detection_caption0->SetForegroundColour(STATIC_TEXT_CAPTION_COL); text_nozzleclumping_detection_caption0->Wrap(-1); @@ -831,6 +883,61 @@ wxBoxSizer* PrintOptionsDialog::create_settings_group(wxWindow* parent) sizer->Add(line_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(18)); + /* build plate detection type & align */ + line_sizer = new wxBoxSizer(wxHORIZONTAL); + text_plate_build = new Label(parent, _L("Build Plate Detection")); + text_plate_build->SetFont(Label::Body_14); + line_sizer->Add(FromDIP(5), 0, 0, 0); + line_sizer->Add(text_plate_build, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5)); + line_sizer->Add(FromDIP(5), 0, 0, 0); + sizer->Add(line_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(18)); + + line_sizer = new wxBoxSizer(wxHORIZONTAL); + text_plate_build_caption = new Label(parent, _L("Ensures the build plate type and placement are correct.")); + text_plate_build_caption->SetFont(Label::Body_12); + text_plate_build_caption->SetForegroundColour(STATIC_TEXT_CAPTION_COL); + line_sizer->Add(FromDIP(5), 0, 0, 0); + line_sizer->Add(text_plate_build_caption, 1, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5)); + sizer->Add(line_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(18)); + + line_sizer = new wxBoxSizer(wxHORIZONTAL); + m_cb_plate_type = new CheckBox(parent); + text_plate_type = new Label(parent, _L("Type Detection")); + text_plate_type->SetFont(Label::Body_14); + line_sizer->Add(FromDIP(5), 0, 0, 0); + line_sizer->Add(m_cb_plate_type, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5)); + line_sizer->Add(text_plate_type, 1, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5)); + line_sizer->Add(FromDIP(5), 0, 0, 0); + sizer->Add(line_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(18)); + + line_sizer = new wxBoxSizer(wxHORIZONTAL); + text_plate_type_caption = new Label(parent, _L("Pauses printing when the detected build plate type does not match the selected one.")); + text_plate_type_caption->Wrap(FromDIP(400)); + text_plate_type_caption->SetFont(Label::Body_12); + text_plate_type_caption->SetForegroundColour(STATIC_TEXT_CAPTION_COL); + line_sizer->Add(FromDIP(38), 0, 0, 0); + line_sizer->Add(text_plate_type_caption, 1, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(0)); + sizer->Add(line_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(18)); + + line_sizer = new wxBoxSizer(wxHORIZONTAL); + m_cb_plate_align = new CheckBox(parent); + text_plate_align = new Label(parent, _L("Alignment Detection")); + text_plate_align->SetFont(Label::Body_14); + line_sizer->Add(FromDIP(5), 0, 0, 0); + line_sizer->Add(m_cb_plate_align, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5)); + line_sizer->Add(text_plate_align, 1, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5)); + line_sizer->Add(FromDIP(5), 0, 0, 0); + sizer->Add(line_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(18)); + + line_sizer = new wxBoxSizer(wxHORIZONTAL); + text_plate_align_caption = new Label(parent, _L("Pauses printing when build plate misalignment is detected.")); + text_plate_align_caption->Wrap(FromDIP(400)); + text_plate_align_caption->SetFont(Label::Body_12); + text_plate_align_caption->SetForegroundColour(STATIC_TEXT_CAPTION_COL); + line_sizer->Add(FromDIP(38), 0, 0, 0); + line_sizer->Add(text_plate_align_caption, 1, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(0)); + sizer->Add(line_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(18)); + line2 = new StaticLine(parent, false); line2->SetLineColour(STATIC_BOX_LINE_COL); sizer->Add(line2, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(20)); @@ -937,7 +1044,7 @@ wxBoxSizer* PrintOptionsDialog::create_settings_group(wxWindow* parent) line_sizer->Add(FromDIP(5), 0, 0, 0); line_sizer = new wxBoxSizer(wxHORIZONTAL); - wxString nozzle_blob_caption_text = _L("Check if the nozzle is clumping by filament or other foreign objects."); + wxString nozzle_blob_caption_text = _L("Checks if the nozzle is clumping by filament or other foreign objects."); text_nozzle_blob_caption = new Label(parent, nozzle_blob_caption_text); text_nozzle_blob_caption->SetFont(Label::Body_12); text_nozzle_blob_caption->Wrap(-1); @@ -1427,7 +1534,6 @@ wxString PrinterPartsDialog::GetString(NozzleType nozzle_type) const { case Slic3r::ntStainlessSteel: return _L("Stainless Steel"); case Slic3r::ntTungstenCarbide: return _L("Tungsten Carbide"); case Slic3r::ntBrass: return _L("Brass"); - case Slic3r::ntE3D: return "E3D"; default: break; } diff --git a/src/slic3r/GUI/PrintOptionsDialog.hpp b/src/slic3r/GUI/PrintOptionsDialog.hpp index b126b98..277fdb1 100644 --- a/src/slic3r/GUI/PrintOptionsDialog.hpp +++ b/src/slic3r/GUI/PrintOptionsDialog.hpp @@ -142,6 +142,14 @@ protected: wxBoxSizer* create_settings_group(wxWindow* parent); wxPanel *m_line; + Label* text_plate_build{nullptr}; + Label* text_plate_build_caption{nullptr}; + CheckBox* m_cb_plate_type{nullptr}; + Label* text_plate_type{nullptr}; + Label* text_plate_type_caption{nullptr}; + CheckBox* m_cb_plate_align{nullptr}; + Label* text_plate_align{nullptr}; + Label* text_plate_align_caption{nullptr}; bool print_halt = false; diff --git a/src/slic3r/GUI/Project.cpp b/src/slic3r/GUI/Project.cpp index cc3041a..5ef88e4 100644 --- a/src/slic3r/GUI/Project.cpp +++ b/src/slic3r/GUI/Project.cpp @@ -22,14 +22,24 @@ #include #include +#include + +#include +#include #include "wxExtensions.hpp" #include "GUI_App.hpp" #include "GUI_ObjectList.hpp" #include "MainFrame.hpp" +#include "GUI_Utils.hpp" #include +#include #include +#include +#include +#include +#include namespace Slic3r { namespace GUI { @@ -46,10 +56,10 @@ const std::vector license_list = { ProjectPanel::ProjectPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, long style) : wxPanel(parent, id, pos, size, style) { - m_project_home_url = wxString::Format("file://%s/web/model/index.html", from_u8(resources_dir())); + m_project_home_url = wxString::Format("file://%s/web/model_new/index.html", from_u8(resources_dir())); wxString strlang = wxGetApp().current_language_code_safe(); if (strlang != "") - m_project_home_url = wxString::Format("file://%s/web/model/index.html?lang=%s", from_u8(resources_dir()), strlang); + m_project_home_url = wxString::Format("file://%s/web/model_new/index.html?lang=%s", from_u8(resources_dir()), strlang); wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL); @@ -215,21 +225,25 @@ void ProjectPanel::on_reload(wxCommandEvent& evt) j["profile"]["cover_img"] = wxGetApp().url_encode(p_cover_file); j["profile"]["preview_img"] = files["Profile Pictures"]; - json m_Res = json::object(); - m_Res["command"] = "show_3mf_info"; - m_Res["sequence_id"] = std::to_string(ProjectPanel::m_sequence_id++); - m_Res["model"] = j; - - wxString strJS = wxString::Format("HandleStudio(%s)", m_Res.dump(-1, ' ', false, json::error_handler_t::ignore)); + json payload = json::object(); + payload["command"] = "show_3mf_info"; + payload["sequence_id"] = std::to_string(ProjectPanel::m_sequence_id++); + payload["model"] = j; + m_last_payload = payload; + auto dispatch_payload = [this](const json& data) { + wxString strJS = wxString::Format("HandleStudio(%s)", data.dump(-1, ' ', false, json::error_handler_t::ignore)); #ifdef __APPLE__ - wxGetApp().CallAfter([this, strJS] { RunScript(strJS.ToStdString()); }); -#else - if (m_web_init_completed) { wxGetApp().CallAfter([this, strJS] { RunScript(strJS.ToStdString()); }); - } +#else + if (m_web_init_completed) { + wxGetApp().CallAfter([this, strJS] { RunScript(strJS.ToStdString()); }); + } #endif + }; + + dispatch_payload(m_last_payload); }); } @@ -298,6 +312,44 @@ void ProjectPanel::OnScriptMessage(wxWebViewEvent& evt) } else if (strCmd == "request_3mf_info") { m_web_init_completed = true; + if (!m_last_payload.empty()) { + auto payload_copy = m_last_payload; + payload_copy["sequence_id"] = std::to_string(ProjectPanel::m_sequence_id++); + wxString strJS = wxString::Format("HandleStudio(%s)", payload_copy.dump(-1, ' ', false, json::error_handler_t::ignore)); + wxGetApp().CallAfter([this, strJS] { + RunScript(strJS.ToStdString()); + }); + } + } + else if (strCmd == "request_confirm_save_project") { + std::string sequence_id; + if (j.contains("sequence_id")) { + const auto& seq = j["sequence_id"]; + if (seq.is_string()) + sequence_id = seq.get(); + else if (seq.is_number_integer()) + sequence_id = std::to_string(seq.get()); + } + + MessageDialog dlg(this, + _L("Do you want to save the changes ?"), + _L("Save"), + wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxCENTRE); + int res = dlg.ShowModal(); + if (res == wxID_YES) { + save_project(); + }else if (res == wxID_NO ) { + json resp = json::object(); + resp["command"] = "discard_project"; + if (!sequence_id.empty()) + resp["sequence_id"] = sequence_id; + + wxString strJS = wxString::Format("window.HandleEditor && window.HandleEditor(%s);", + resp.dump(-1, ' ', false, json::error_handler_t::ignore)); + wxGetApp().CallAfter([this, strJS] { + RunScript(strJS.ToStdString()); + }); + } else {} } else if (strCmd == "modelmall_model_open") { if (j.contains("data")) { @@ -317,6 +369,358 @@ void ProjectPanel::OnScriptMessage(wxWebViewEvent& evt) } } } + else if (strCmd == "editor_upload_file") { + json response = json::object(); + std::string sequence_id; + if (j.contains("sequence_id")) + sequence_id = j["sequence_id"].get(); + + response["command"] = "editor_upload_file_result"; + response["sequence_id"] = sequence_id; + + try { + if (!j.contains("data") || !j["data"].is_object()) + throw std::runtime_error("missing payload"); + + const json& payload = j["data"]; + std::string base64 = payload.value("base64", std::string()); + if (base64.empty()) + throw std::runtime_error("empty file content"); + + std::string original_name = payload.value("filename", std::string()); + std::string extension; + if (!original_name.empty()) { + fs::path original_path(original_name); + if (original_path.has_extension()) + extension = original_path.extension().string(); + } + + size_t decoded_capacity = wxBase64DecodedSize(base64.length()); + if (decoded_capacity == 0) + throw std::runtime_error("invalid base64 length"); + + std::string decoded; + decoded.resize(decoded_capacity); + size_t decoded_size = wxBase64Decode(decoded.data(), decoded.size(), base64.c_str(), base64.length()); + if (decoded_size == wxInvalidSize || decoded_size == 0) + throw std::runtime_error("base64 decode failed"); + decoded.resize(decoded_size); + + fs::path temp_dir; + try { + temp_dir = fs::temp_directory_path(); + } catch (const std::exception& e) { + throw std::runtime_error(std::string("failed to locate temp directory: ") + e.what()); + } + temp_dir /= "qidi_editor"; + + boost::system::error_code ec; + fs::create_directories(temp_dir, ec); + if (ec) { + throw std::runtime_error("failed to create temp directory: " + ec.message()); + } + + fs::path temp_file = temp_dir / fs::unique_path("editor-%%%%%%%%-%%%%%%%%"); + if (!extension.empty()) { + if (extension.front() != '.') + extension.insert(extension.begin(), '.'); + temp_file.replace_extension(extension); + } + + std::ofstream output(temp_file.string(), std::ios::binary | std::ios::trunc); + if (!output.is_open()) + throw std::runtime_error("failed to open temp file for writing"); + output.write(decoded.data(), static_cast(decoded.size())); + if (!output.good()) + throw std::runtime_error("failed to write temp file"); + output.close(); + + wxString stored_path_wx(temp_file.wstring()); + std::string stored_path(stored_path_wx.utf8_str()); + + json data = json::object(); + data["path"] = stored_path; + data["encoded_path"] = wxGetApp().url_encode(stored_path); + data["filename"] = original_name; + if (payload.contains("size")) + data["size"] = payload["size"]; + if (payload.contains("type")) + data["type"] = payload["type"]; + + response["data"] = data; + } catch (const std::exception& e) { + response["error"] = e.what(); + } + + wxString payload = wxString::FromUTF8(response.dump(-1, ' ', false, json::error_handler_t::ignore)); + wxString script = wxString::Format("window.HandleEditor && window.HandleEditor(%s);", payload); + wxGetApp().CallAfter([this, script] { + RunScript(script.ToStdString()); + }); + } + else if (strCmd == "update_3mf_info") { + json response = json::object(); + + response["command"] = "update_3mf_info_result"; + response["sequence_id"] = j["sequence_id"]; + + try { + if (!j.contains("model") || !j["model"].is_object()) + throw std::runtime_error("invalid payload"); + + const json& payload = j["model"]; + + Model& model_ref = wxGetApp().plater()->model(); + if (model_ref.model_info == nullptr) + model_ref.model_info = std::make_shared(); + if (model_ref.profile_info == nullptr) + model_ref.profile_info = std::make_shared(); + + auto decode_string = [](const json& node, const char* key) -> std::string { + if (!node.contains(key)) + return std::string(); + const auto& val = node.at(key); + if (!val.is_string()) + return std::string(); + std::string out = val.get(); + if (out.empty()) + return out; + try { + return wxGetApp().url_decode(out); + } catch (...) { + return out; + } + }; + + auto decode_required = [&](const json& node, const char* key) -> std::string { + std::string value = decode_string(node, key); + if (value.empty()) + throw std::runtime_error(std::string("missing ") + key); + return value; + }; + + const json model_section = payload.value("model", json::object()); + const json file_section = payload.value("file", json::object()); + const json profile_section = payload.value("profile", json::object()); + + model_ref.model_info->model_name = decode_required(model_section, "name"); + model_ref.model_info->description = decode_required(model_section, "description"); + + + model_ref.profile_info->ProfileTile = decode_string(profile_section, "name"); + model_ref.profile_info->ProfileDescription = decode_string(profile_section, "description"); + + std::string auxiliary_root = encode_path(model_ref.get_auxiliary_file_temp_path().c_str()); + if (auxiliary_root.empty()) + throw std::runtime_error("failed to locate auxiliary directory"); + + wxString aux_root_wx(auxiliary_root.c_str()); + + auto sanitize_filename = [](std::string name) -> std::string { + if (name.empty()) + return name; + for (char& ch : name) { + if (ch == '\\' || ch == '/' || ch == ':' || ch == '*' || + ch == '?' || ch == '"' || ch == '<' || ch == '>' || ch == '|') + ch = '_'; + } + return name; + }; + + auto json_array_or_empty = [](const json& parent, const char* key) -> json { + if (parent.contains(key) && parent.at(key).is_array()) + return parent.at(key); + return json::array(); + }; + + auto ensure_directory = [&](const wxString& folder_name) -> fs::path { + wxString full_path = aux_root_wx + "/" + folder_name; + fs::path dir_path(full_path.ToStdWstring()); + boost::system::error_code ec; + if (!fs::exists(dir_path, ec)) { + fs::create_directories(dir_path, ec); + if (ec) + throw std::runtime_error("failed to create auxiliary directory: " + ec.message()); + } + return dir_path; + }; + + auto detect_extension_from_data_uri = [](const std::string& data_uri) -> std::string { + auto slash = data_uri.find('/'); + auto semicolon = data_uri.find(';'); + if (slash != std::string::npos && semicolon != std::string::npos && semicolon > slash) { + std::string ext = data_uri.substr(slash + 1, semicolon - slash - 1); + if (!ext.empty()) + return "." + ext; + } + return std::string(); + }; + + auto normalize_path = [](const fs::path& path) { + std::wstring w = path.lexically_normal().wstring(); + std::transform(w.begin(), w.end(), w.begin(), towlower); + return w; + }; + + auto copy_entries = [&](const json& list, const wxString& folder_name, bool is_image, std::vector* stored_paths) { + fs::path dest_dir = ensure_directory(folder_name); + if (stored_paths) + stored_paths->clear(); + + if (!list.is_array()) + return; + + size_t index = 0; + std::set desired_paths; + for (const auto& entry : list) { + if (!entry.is_object()) + continue; + + std::string raw_filepath = entry.value("filepath", std::string()); + std::string raw_filename = entry.value("filename", std::string()); + + bool is_data_uri = boost::algorithm::starts_with(raw_filepath, "data:"); + std::string decoded_filepath = raw_filepath; + if (!is_data_uri) + decoded_filepath = wxGetApp().url_decode(raw_filepath); + + fs::path dest_file; + bool reused_existing = false; + + if (!is_data_uri && !decoded_filepath.empty()) { + fs::path src_path(decoded_filepath); + boost::system::error_code ec; + if (fs::exists(src_path, ec) && fs::equivalent(src_path.parent_path(), dest_dir, ec)) { + dest_file = src_path; + reused_existing = true; + } + } + + std::string filename = sanitize_filename(wxGetApp().url_decode(raw_filename)); + if (!reused_existing) { + std::string extension; + if (!filename.empty()) { + auto pos = filename.find_last_of('.'); + if (pos != std::string::npos) + extension = filename.substr(pos); + } + + if (filename.empty()) { + extension = !extension.empty() ? extension : (is_data_uri ? detect_extension_from_data_uri(raw_filepath) : fs::path(decoded_filepath).extension().string()); + if (!extension.empty() && extension.front() != '.') + extension.insert(extension.begin(), '.'); + filename = "file_" + std::to_string(index++) + extension; + } + + dest_file = dest_dir / filename; + while (desired_paths.count(dest_file.lexically_normal().wstring()) != 0) + dest_file = dest_dir / (dest_file.stem().string() + "_" + std::to_string(index++) + dest_file.extension().string()); + + if (is_data_uri) { + auto comma = raw_filepath.find(','); + if (comma == std::string::npos) + throw std::runtime_error("invalid data url"); + std::string base64 = raw_filepath.substr(comma + 1); + std::string buffer; + buffer.resize(wxBase64DecodedSize(base64.length())); + size_t decoded_size = wxBase64Decode(buffer.data(), buffer.size(), base64.c_str(), base64.length()); + if (decoded_size == wxInvalidSize || decoded_size == 0) + throw std::runtime_error("failed to decode image data"); + buffer.resize(decoded_size); + std::ofstream out(dest_file.string(), std::ios::binary | std::ios::trunc); + if (!out.is_open()) + throw std::runtime_error("failed to write file"); + out.write(buffer.data(), static_cast(buffer.size())); + out.close(); + } else { + if (decoded_filepath.empty()) + throw std::runtime_error("missing attachment path"); + fs::path src_path(decoded_filepath); + if (!fs::exists(src_path)) + throw std::runtime_error("attachment not found: " + decoded_filepath); + boost::system::error_code ec; + fs::copy_file(src_path, dest_file, fs::copy_option::overwrite_if_exists, ec); + if (ec) + throw std::runtime_error("copy attachment failed: " + ec.message()); + } + } + + desired_paths.insert(normalize_path(dest_file)); + if (stored_paths) + stored_paths->push_back(dest_file); + } + + boost::system::error_code ec; + fs::directory_iterator end_it; + for (fs::directory_iterator it(dest_dir, ec); !ec && it != end_it; ++it) { + if (fs::is_directory(it->path(), ec)) + continue; + if (desired_paths.count(normalize_path(it->path())) == 0) + fs::remove(it->path(), ec); + } + }; + + std::vector model_picture_paths; + copy_entries(json_array_or_empty(model_section, "preview_img"), "Model Pictures", true, &model_picture_paths); + copy_entries(json_array_or_empty(file_section, "BOM"), "Bill of Materials", false, nullptr); + copy_entries(json_array_or_empty(file_section, "Assembly"), "Assembly Guide", false, nullptr); + copy_entries(json_array_or_empty(file_section, "Other"), "Others", false, nullptr); + std::vector profile_picture_paths; + copy_entries(json_array_or_empty(profile_section, "preview_img"), "Profile Pictures", true, &profile_picture_paths); + + if (!model_picture_paths.empty()) { + const fs::path &cover_src = model_picture_paths.front(); + model_ref.model_info->cover_file = cover_src.filename().string(); + + fs::path full_path = cover_src.parent_path(); + fs::path full_root_path = full_path.parent_path(); + wxString full_root_path_str = encode_path(full_root_path.string().c_str()); + wxString thumbnails_dir = wxString::Format("%s/.thumbnails", full_root_path_str); + fs::path dir_path(thumbnails_dir.ToStdWstring()); + + boost::system::error_code ec; + if (!fs::exists(dir_path, ec)) + fs::create_directories(dir_path, ec); + + wxImage thumbnail_img; + if (generate_image(cover_src.string(), thumbnail_img, _3MF_COVER_SIZE)) { + auto cover_img_path = dir_path / "thumbnail_3mf.png"; + thumbnail_img.SaveFile(encode_path(cover_img_path.string().c_str())); + } + if (generate_image(cover_src.string(), thumbnail_img, PRINTER_THUMBNAIL_SMALL_SIZE)) { + auto small_img_path = dir_path / "thumbnail_small.png"; + thumbnail_img.SaveFile(encode_path(small_img_path.string().c_str())); + } + if (generate_image(cover_src.string(), thumbnail_img, PRINTER_THUMBNAIL_MIDDLE_SIZE)) { + auto middle_img_path = dir_path / "thumbnail_middle.png"; + thumbnail_img.SaveFile(encode_path(middle_img_path.string().c_str())); + } + } else { + model_ref.model_info->cover_file.clear(); + } + + if (!profile_picture_paths.empty()) + model_ref.profile_info->ProfileCover = profile_picture_paths.front().filename().string(); + else + model_ref.profile_info->ProfileCover.clear(); + + + wxGetApp().plater()->set_plater_dirty(true); + Slic3r::put_other_changes(); + update_model_data(); + + response["status"] = "success"; + response["message"] = "Project information saved"; + } catch (const std::exception& e) { + response["error"] = e.what(); + } + + wxString payload = wxString::FromUTF8(response.dump(-1, ' ', false, json::error_handler_t::ignore)); + wxString script = wxString::Format("window.HandleEditor && window.HandleEditor(%s);", payload); + wxGetApp().CallAfter([this, script] { + RunScript(script.ToStdString()); + }); + } else if (strCmd == "debug_info") { //wxString msg = j["msg"]; //OutputDebugString(wxString::Format("Model_Web: msg = %s \r\n", msg)); @@ -335,8 +739,18 @@ void ProjectPanel::update_model_data() clear_model_info(); //basics info - if (model.model_info == nullptr) + //Note: Under the master branch, model_info will never return nullptr, but under the GitHub branch, it might. The reason is unclear. + if (model.model_info == nullptr) { + json payload = json::object(); + payload["command"] = "show_3mf_info"; + payload["sequence_id"] = std::to_string(ProjectPanel::m_sequence_id++); + payload["model"] = json::object(); + + wxString js = wxString::Format("HandleStudio(%s)", + payload.dump(-1, ' ', false, json::error_handler_t::ignore)); + wxGetApp().CallAfter([this, js]{ RunScript(js.ToStdString()); }); return; + } auto event = wxCommandEvent(EVT_PROJECT_RELOAD); event.SetEventObject(this); @@ -345,6 +759,8 @@ void ProjectPanel::update_model_data() void ProjectPanel::clear_model_info() { + m_last_payload = json::object(); + json m_Res = json::object(); m_Res["command"] = "clear_3mf_info"; m_Res["sequence_id"] = std::to_string(ProjectPanel::m_sequence_id++); @@ -500,10 +916,33 @@ void ProjectPanel::RunScript(std::string content) WebView::RunScript(m_browser, content); } +bool ProjectPanel::is_editing_page() const +{ + if (m_browser == nullptr) + return false; + wxString current_url = m_browser->GetCurrentURL(); + if (current_url.IsEmpty()) + return false; + return current_url.Lower().Contains("editor.html"); +} + bool ProjectPanel::Show(bool show) { if (show) update_model_data(); return wxPanel::Show(show); } +void ProjectPanel::save_project() +{ + json resp = json::object(); + resp["command"] = "save_project"; + resp["sequence_id"] = std::to_string(ProjectPanel::m_sequence_id++); + + wxString strJS = wxString::Format("window.HandleEditor && window.HandleEditor(%s);", + resp.dump(-1, ' ', false, json::error_handler_t::ignore)); + wxGetApp().CallAfter([this, strJS] { + RunScript(strJS.ToStdString()); + }); +} + }} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Project.hpp b/src/slic3r/GUI/Project.hpp index b0f1b88..d8a4650 100644 --- a/src/slic3r/GUI/Project.hpp +++ b/src/slic3r/GUI/Project.hpp @@ -67,6 +67,7 @@ private: wxString m_root_dir; std::map m_model_id_map; static inline int m_sequence_id = 8000; + json m_last_payload = json::object(); public: ProjectPanel(wxWindow *parent, wxWindowID id = wxID_ANY, const wxPoint &pos = wxDefaultPosition, const wxSize &size = wxDefaultSize, long style = wxTAB_TRAVERSAL); @@ -86,11 +87,13 @@ public: bool Show(bool show); void OnScriptMessage(wxWebViewEvent& evt); void RunScript(std::string content); + bool is_editing_page() const; std::map> Reload(wxString aux_path); std::string formatBytes(unsigned long bytes); std::string get_model_id(std::string desgin_id); wxString to_base64(std::string path); + void save_project(); }; wxDECLARE_EVENT(EVT_PROJECT_RELOAD, wxCommandEvent); diff --git a/src/slic3r/GUI/PurgeModeDialog.cpp b/src/slic3r/GUI/PurgeModeDialog.cpp new file mode 100644 index 0000000..9ef30d6 --- /dev/null +++ b/src/slic3r/GUI/PurgeModeDialog.cpp @@ -0,0 +1,310 @@ +#include "PurgeModeDialog.hpp" + +#include +#include +#include +#include +#include + +#include "I18N.hpp" +#include "GUI_App.hpp" +#include "wxExtensions.hpp" +#include "Widgets/Label.hpp" +#include "wx/graphics.h" + +namespace Slic3r { namespace GUI { + +static const wxColour BgNormalColor = wxColour("#FFFFFF"); +static const wxColour BgSelectColor = wxColour("#EBF9F0"); +static const wxColour BgDisableColor = wxColour("#CECECE"); + +static const wxColour BorderNormalColor = wxColour("#CECECE"); +static const wxColour BorderSelectedColor = wxColour("#4479fb"); +static const wxColour BorderDisableColor = wxColour("#EEEEEE"); + +static const wxColour TextNormalBlackColor = wxColour("#262E30"); +static const wxColour TextNormalGreyColor = wxColour("#6B6B6B"); +static const wxColour TextDisableColor = wxColour("#CECECE"); +static const wxColour TextErrorColor = wxColour("#E14747"); + +PurgeModeDialog::PurgeModeDialog(wxWindow *parent) : DPIDialog(parent, wxID_ANY, _L("Purge Mode Settings"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE) +{ + SetBackgroundColour(*wxWHITE); + SetMinSize(wxSize(FromDIP(520), FromDIP(320))); + SetMaxSize(wxSize(FromDIP(520), FromDIP(320))); + std::string icon_path = (boost::format("%1%/images/QIDIStudioTitle.ico") % Slic3r::resources_dir()).str(); + SetIcon(wxIcon(Slic3r::encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO)); + + auto main_sizer = new wxBoxSizer(wxVERTICAL); + + // Options panel + auto options_panel = new wxPanel(this); + auto options_sizer = new wxBoxSizer(wxVERTICAL); + auto panels_sizer = new wxBoxSizer(wxHORIZONTAL); + + // Get current mode from preset bundle + auto &preset_bundle = *wxGetApp().preset_bundle; + auto current_mode = preset_bundle.project_config.option>("prime_volume_mode"); + PrimeVolumeMode mode = current_mode ? current_mode->value : pvmDefault; + m_selected_mode = mode; + + // Standard option + m_standard_panel = new PurgeModeBtnPanel(options_panel, _L("Standard Mode"), _L("Performs full purging for the best print quality. Requires more material and time."), + "shield"); + m_standard_panel->SetMinSize(wxSize(FromDIP(200), FromDIP(150))); + m_standard_panel->Select(mode == pvmDefault); + m_standard_panel->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &) { select_option(pvmDefault); }); + panels_sizer->Add(m_standard_panel, 1, wxEXPAND | wxRIGHT, FromDIP(12)); + + // Purge Saving option + m_saving_panel = new PurgeModeBtnPanel(options_panel, _L("Purge Saving"), + _L("Reduces purge waste and prints faster. May cause slight color mixing or small surface defects."), "leaf"); + m_saving_panel->SetMinSize(wxSize(FromDIP(200), FromDIP(150))); + m_saving_panel->Select(mode == pvmSaving); + m_saving_panel->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &) { select_option(pvmSaving); }); + panels_sizer->Add(m_saving_panel, 1, wxEXPAND | wxLEFT, FromDIP(12)); + + options_sizer->Add(panels_sizer, 0, wxEXPAND | wxALL, FromDIP(20)); + + // Learn more text + auto wiki_sizer = new wxBoxSizer(wxHORIZONTAL); + auto learn_more_text = new wxStaticText(options_panel, wxID_ANY, _L("Learn more about purge mode")); + learn_more_text->SetFont(Label::Body_12); + learn_more_text->SetForegroundColour(wxColour("#6B6B6A")); + wiki_sizer->Add(learn_more_text, 0, wxALIGN_CENTER_VERTICAL); + auto wiki = new WikiPanel(options_panel); + wiki->SetWikiUrl("https://wiki.qidi3d.com/en/software/qidi-studio"); + wiki_sizer->Add(wiki, 0, wxLEFT, FromDIP(2)); + options_sizer->Add(wiki_sizer, 0, wxLEFT | wxRIGHT, FromDIP(20)); + + options_panel->SetSizer(options_sizer); + main_sizer->Add(options_panel, 1, wxEXPAND); + + auto btn_sizer = new wxBoxSizer(wxHORIZONTAL); + btn_sizer->AddStretchSpacer(); + + StateColor ok_btn_bg( + std::pair(wxColour("#1B8844"), StateColor::Pressed), + std::pair(wxColour("#3DCB73"), StateColor::Hovered), + std::pair(wxColour("#4479fb"), StateColor::Normal) + ); + StateColor ok_btn_text( + std::pair(wxColour("#FFFFFE"), StateColor::Normal) + ); + StateColor cancel_btn_bg( + std::pair(wxColour("#CECECE"), StateColor::Pressed), + std::pair(wxColour("#EEEEEE"), StateColor::Hovered), + std::pair(wxColour("#FFFFFF"), StateColor::Normal) + ); + StateColor cancel_btn_bd( + std::pair(wxColour("#262E30"), StateColor::Normal) + ); + StateColor cancel_btn_text( + std::pair(wxColour("#262E30"), StateColor::Normal) + ); + + auto ok_btn = new Button(this, _L("Confirm")); + ok_btn->SetMinSize(wxSize(FromDIP(62), FromDIP(24))); + ok_btn->SetCornerRadius(FromDIP(12)); + ok_btn->SetBackgroundColor(ok_btn_bg); + ok_btn->SetFont(Label::Body_12); + ok_btn->SetBorderColor(wxColour("#4479fb")); + ok_btn->SetTextColor(ok_btn_text); + ok_btn->SetId(wxID_OK); + + auto cancel_btn = new Button(this, _L("Cancel")); + cancel_btn->SetMinSize(wxSize(FromDIP(62), FromDIP(24))); + cancel_btn->SetCornerRadius(FromDIP(12)); + cancel_btn->SetBackgroundColor(cancel_btn_bg); + cancel_btn->SetFont(Label::Body_12); + cancel_btn->SetBorderColor(cancel_btn_bd); + cancel_btn->SetTextColor(cancel_btn_text); + cancel_btn->SetId(wxID_CANCEL); + + btn_sizer->Add(ok_btn, 0, wxRIGHT, FromDIP(12)); + btn_sizer->Add(cancel_btn, 0); + + main_sizer->Add(btn_sizer, 0, wxEXPAND | wxALL, FromDIP(20)); + + SetSizer(main_sizer); + Fit(); + CenterOnParent(); + + wxGetApp().UpdateDlgDarkUI(this); +} + +void PurgeModeDialog::select_option(PrimeVolumeMode mode) +{ + m_selected_mode = mode; + update_panel_selection(); +} + +void PurgeModeDialog::update_panel_selection() +{ + if (m_selected_mode == pvmDefault) { + m_standard_panel->Select(true); + m_saving_panel->Select(false); + } else { + m_standard_panel->Select(false); + m_saving_panel->Select(true); + } +} + +void PurgeModeDialog::on_dpi_changed(const wxRect &suggested_rect) +{ + const int &em = em_unit(); + + msw_buttons_rescale(this, em, {wxID_OK, wxID_CANCEL}); + + const wxSize &size = wxSize(70 * em, 32 * em); + SetMinSize(size); + + Fit(); + Refresh(); +} + +GUI::PurgeModeBtnPanel::PurgeModeBtnPanel(wxWindow *parent, const wxString &label, const wxString &detail, const std::string &icon_path) : wxPanel(parent) +{ + SetBackgroundColour(*wxWHITE); + SetBackgroundStyle(wxBG_STYLE_PAINT); + m_hover = false; + + const int horizontal_margin = FromDIP(12); + + auto sizer = new wxBoxSizer(wxVERTICAL); + + icon = create_scaled_bitmap(icon_path, nullptr, 20); + m_btn = new wxStaticBitmap(this, wxID_ANY, icon, wxDefaultPosition, wxDefaultSize, wxNO_BORDER); + + check_icon = create_scaled_bitmap("completed_2", nullptr, 20); + m_check_btn = new wxStaticBitmap(this, wxID_ANY, check_icon, wxDefaultPosition, wxDefaultSize, wxNO_BORDER); + m_check_btn->Hide(); + + // icon + auto icon_sizer = new wxBoxSizer(wxHORIZONTAL); + icon_sizer->Add(m_btn, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, horizontal_margin); + icon_sizer->AddStretchSpacer(); + icon_sizer->Add(m_check_btn, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, horizontal_margin); + + // label + m_label = new wxStaticText(this, wxID_ANY, label); + m_label->SetFont(Label::Head_14); + m_label->SetForegroundColour(TextNormalBlackColor); + + auto label_sizer = new wxBoxSizer(wxHORIZONTAL); + label_sizer->Add(m_label, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, horizontal_margin); + + // detail + auto detail_sizer = new wxBoxSizer(wxHORIZONTAL); + m_detail = new Label(this, detail); + m_detail->SetFont(Label::Body_12); + m_detail->SetForegroundColour(TextNormalGreyColor); + m_detail->Wrap(FromDIP(200)); + detail_sizer->Add(m_detail, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, horizontal_margin); + + sizer->AddSpacer(FromDIP(15)); + sizer->Add(icon_sizer, 0, wxEXPAND); + sizer->AddSpacer(FromDIP(10)); + sizer->Add(label_sizer, 0, wxEXPAND); + sizer->AddSpacer(FromDIP(6)); + sizer->Add(detail_sizer, 0, wxEXPAND); + sizer->AddSpacer(FromDIP(10)); + + SetSizer(sizer); + Layout(); + Fit(); + + GUI::wxGetApp().UpdateDarkUIWin(this); + + auto forward_click_to_parent = [this](wxMouseEvent &event) { + wxCommandEvent click_event(wxEVT_LEFT_DOWN, GetId()); + click_event.SetEventObject(this); + this->ProcessEvent(click_event); + }; + + m_btn->Bind(wxEVT_LEFT_DOWN, forward_click_to_parent); + m_label->Bind(wxEVT_LEFT_DOWN, forward_click_to_parent); + m_detail->Bind(wxEVT_LEFT_DOWN, forward_click_to_parent); + + Bind(wxEVT_PAINT, &PurgeModeBtnPanel::OnPaint, this); + Bind(wxEVT_ENTER_WINDOW, &PurgeModeBtnPanel::OnEnterWindow, this); + Bind(wxEVT_LEAVE_WINDOW, &PurgeModeBtnPanel::OnLeaveWindow, this); +} + +void PurgeModeBtnPanel::OnPaint(wxPaintEvent &event) +{ + wxAutoBufferedPaintDC dc(this); + wxGraphicsContext *gc = wxGraphicsContext::Create(dc); + + if (gc) { + dc.Clear(); + wxRect rect = GetClientRect(); + gc->SetBrush(wxTransparentColour); + gc->DrawRoundedRectangle(0, 0, rect.width, rect.height, 0); + wxColour bg_color = m_selected ? BgSelectColor : BgNormalColor; + + wxColour border_color = m_hover || m_selected ? BorderSelectedColor : BorderNormalColor; + + bg_color = StateColor::darkModeColorFor(bg_color); + border_color = StateColor::darkModeColorFor(border_color); + gc->SetBrush(wxBrush(bg_color)); + gc->SetPen(wxPen(border_color, 1)); + gc->DrawRoundedRectangle(1, 1, rect.width - 2, rect.height - 2, 8); + delete gc; + } +} + +void PurgeModeBtnPanel::UpdateStatus() +{ + if (m_selected) { + m_btn->SetBackgroundColour(BgSelectColor); + m_label->SetBackgroundColour(BgSelectColor); + m_detail->SetBackgroundColour(BgSelectColor); + if (m_check_btn) { + m_check_btn->SetBackgroundColour(BgSelectColor); + m_check_btn->Show(); + } + } else { + m_btn->SetBackgroundColour(BgNormalColor); + m_label->SetBackgroundColour(BgNormalColor); + m_detail->SetBackgroundColour(BgNormalColor); + if (m_check_btn) { + m_check_btn->SetBackgroundColour(BgNormalColor); + m_check_btn->Hide(); + } + } + Layout(); + Refresh(); + GUI::wxGetApp().UpdateDarkUIWin(this); +} + +void PurgeModeBtnPanel::OnEnterWindow(wxMouseEvent &event) +{ + if (!m_hover) { + m_hover = true; + UpdateStatus(); + Refresh(); + event.Skip(); + } +} + +void PurgeModeBtnPanel::OnLeaveWindow(wxMouseEvent &event) +{ + if (m_hover) { + wxPoint pos = this->ScreenToClient(wxGetMousePosition()); + if (this->GetClientRect().Contains(pos)) return; + m_hover = false; + UpdateStatus(); + Refresh(); + event.Skip(); + } +} + +void PurgeModeBtnPanel::Select(bool selected) +{ + m_selected = selected; + UpdateStatus(); + Refresh(); +} + +}} // namespace Slic3r::GUI \ No newline at end of file diff --git a/src/slic3r/GUI/PurgeModeDialog.hpp b/src/slic3r/GUI/PurgeModeDialog.hpp new file mode 100644 index 0000000..faca9aa --- /dev/null +++ b/src/slic3r/GUI/PurgeModeDialog.hpp @@ -0,0 +1,68 @@ +#ifndef slic3r_GUI_PurgeModeDialog_hpp_ +#define slic3r_GUI_PurgeModeDialog_hpp_ + +#include +#include + +#include "GUI_Utils.hpp" +#include "libslic3r/PrintConfig.hpp" +#include "Widgets/Button.hpp" +#include "Widgets/Label.hpp" + +class wxStaticText; +class wxBoxSizer; + +namespace Slic3r { + +namespace GUI { + +class PurgeModeBtnPanel : public wxPanel +{ +public: + PurgeModeBtnPanel(wxWindow *parent, const wxString &label, const wxString &detail, const std::string &icon_path); + void Select(bool selected); + +protected: + void OnPaint(wxPaintEvent &event); + +private: + void OnEnterWindow(wxMouseEvent &event); + void OnLeaveWindow(wxMouseEvent &evnet); + + void UpdateStatus(); + + wxBitmap icon; + wxBitmap check_icon; + + wxStaticBitmap *m_btn; + wxStaticBitmap *m_check_btn; + wxStaticText *m_label; + Label *m_detail; + std::string m_icon_path; + bool m_hover{false}; + bool m_selected{false}; +}; + +class PurgeModeDialog : public DPIDialog +{ +public: + PurgeModeDialog(wxWindow *parent); + + PrimeVolumeMode get_selected_mode() const { return m_selected_mode; } + +protected: + void on_dpi_changed(const wxRect &suggested_rect) override; + +private: + void select_option(PrimeVolumeMode mode); + void update_panel_selection(); + + PurgeModeBtnPanel *m_standard_panel; + PurgeModeBtnPanel *m_saving_panel; + PrimeVolumeMode m_selected_mode; +}; + +} // namespace GUI +} // namespace Slic3r + +#endif // slic3r_GUI_PurgeModeDialog_hpp_ \ No newline at end of file diff --git a/src/slic3r/GUI/SafetyOptionsDialog.cpp b/src/slic3r/GUI/SafetyOptionsDialog.cpp index c693068..c275d88 100644 --- a/src/slic3r/GUI/SafetyOptionsDialog.cpp +++ b/src/slic3r/GUI/SafetyOptionsDialog.cpp @@ -210,7 +210,7 @@ wxBoxSizer* SafetyOptionsDialog::create_settings_group(wxWindow* parent) line_sizer = new wxBoxSizer(wxHORIZONTAL); m_cb_idel_heating_protection = new CheckBox(m_idel_heating_container); - m_text_idel_heating_protection = new Label(m_idel_heating_container, _L("Idel Heating Protection")); + m_text_idel_heating_protection = new Label(m_idel_heating_container, _L("Idle Heating Protection")); m_text_idel_heating_protection->SetFont(Label::Body_14); line_sizer->AddSpacer(FromDIP(5)); line_sizer->Add(m_cb_idel_heating_protection, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5)); diff --git a/src/slic3r/GUI/SelectMachine.cpp b/src/slic3r/GUI/SelectMachine.cpp index 81c7f01..c9231f8 100644 --- a/src/slic3r/GUI/SelectMachine.cpp +++ b/src/slic3r/GUI/SelectMachine.cpp @@ -13,20 +13,29 @@ #include "Widgets/StaticBox.hpp" #include "Widgets/CheckBox.hpp" #include "Widgets/Label.hpp" +#include "BackgroundSlicingProcess.hpp" #include "ConnectPrinter.hpp" #include "slic3r/Utils/QDTUtil.hpp" + #include "DeviceCore/DevConfig.h" #include "DeviceCore/DevNozzleSystem.h" #include "DeviceCore/DevExtensionTool.h" #include "DeviceCore/DevExtruderSystem.h" #include "DeviceCore/DevFilaBlackList.h" #include "DeviceCore/DevFilaSystem.h" +#include "DeviceCore/DevInfo.h" +#include "DeviceCore/DevStorage.h" +#include "DeviceCore/DevUpgrade.h" + #include "DeviceCore/DevManager.h" #include "DeviceCore/DevMapping.h" -#include "DeviceCore/DevStorage.h" + +#include "DeviceCore/DevUtilBackend.h" #include "../Utils/QDTUtil.hpp" + +#include #include #include #include @@ -38,6 +47,23 @@ #include "BitmapCache.hpp" #include "BindDialog.hpp" +// definitions +#define S_RACK_NOZZLE_OFFSET_CALI_WARNING _L(\ +"Turning off this option will affect print quality if:\n" \ +"1. Uncalibrated hotends is used\n" \ +"2. The current nozzle, heatbed, or chamber temperature differs from the calibration conditions\n" \ +"3. The toolhead hotend was manually removed or installed") + +#define S_RACK_FLOW_DYNAMICS_CALI_WARNING _L("After modifying this option, the system will reassign the nozzles based on the calibrated dynamic flow calibration coefficient.") + +#define S_RACK_NOZZLE_NOT_ENOUGH _L("There are not enough available hotends currently.") +#define S_RACK_NOZZLE_NO_MATCHED _L("No hotends available.") + +#define S_RACK_NOZZLE_SUGEEST_CALI _L("Please complete the hotend rack setup and try again.") +#define S_RACK_NOZZLE_SUGEEST_REFRESH _L("Please refresh the nozzle information and try again.") +#define S_RACK_NOZZLE_SUGEEST_RESLICE _L("Please re-slice.") +#define S_RACK_NOZZLE_SUGEEST_RESLICE_FILA _L("Please re-slice to avoid filament waste.") + namespace Slic3r { namespace GUI { wxDEFINE_EVENT(EVT_SWITCH_PRINT_OPTION, wxCommandEvent); @@ -52,7 +78,7 @@ wxDEFINE_EVENT(EVT_CLEAR_IPADDRESS, wxCommandEvent); #define WRAP_GAP FromDIP(2) static wxString task_canceled_text = _L("Task canceled"); - +static int s_nozzle_mapping_last_request_time = 0; static bool _HasExt(const std::vector& ams_mapping_result) { if (ams_mapping_result.empty()) { @@ -82,9 +108,11 @@ std::string get_nozzle_volume_type_cloud_string(NozzleVolumeType nozzle_volume_t { if (nozzle_volume_type == NozzleVolumeType::nvtStandard) { return "standard_flow"; - } - else if (nozzle_volume_type == NozzleVolumeType::nvtHighFlow) { + } else if (nozzle_volume_type == NozzleVolumeType::nvtHighFlow) { return "high_flow"; + } else if(nozzle_volume_type == NozzleVolumeType::nvtHybrid) { + // to be supported + return "hybrid_flow"; } else { assert(false); @@ -371,7 +399,7 @@ SelectMachineDialog::SelectMachineDialog(Plater *plater) sizer_of_printer_switch->Add(m_printer_box, 0, wxTOP, 0); sizer_of_printer_switch->Add(switch_button_panel, 0, wxTOP, 10); - m_text_printer_msg = new PrinterMsgPanel(m_basic_panel); + m_text_printer_msg = new PrinterMsgPanel(m_basic_panel, this); m_text_printer_msg->SetMinSize(wxSize(FromDIP(420), -1)); m_text_printer_msg->SetMaxSize(wxSize(FromDIP(420), -1)); m_text_printer_msg->SetBackgroundColour(m_colour_def_color); @@ -511,7 +539,7 @@ SelectMachineDialog::SelectMachineDialog(Plater *plater) m_filament_panel->Hide(); - m_statictext_ams_msg = new PrinterMsgPanel(m_scroll_area); + m_statictext_ams_msg = new PrinterMsgPanel(m_scroll_area, this); m_statictext_ams_msg->SetMinSize(wxSize(FromDIP(655), -1)); m_statictext_ams_msg->SetMaxSize(wxSize(FromDIP(655), -1)); m_statictext_ams_msg->SetBackgroundColour(m_colour_def_color); @@ -616,6 +644,7 @@ SelectMachineDialog::SelectMachineDialog(Plater *plater) new PrintOption(m_options_other, _L("Flow Dynamics Calibration"), _L("This process determines the dynamic flow values to improve overall print quality.\n*Automatic mode: Skip if the filament was calibrated recently."), ops_auto, "flow_cali"); + option_flow_dynamics_cali->Bind(EVT_SWITCH_PRINT_OPTION, &SelectMachineDialog::on_flow_pa_caliation_option_changed, this); auto option_nozzle_offset_cali_cali = new PrintOption( m_options_other, @@ -623,6 +652,45 @@ SelectMachineDialog::SelectMachineDialog(Plater *plater) _L("Calibrate nozzle offsets to enhance print quality.\n*Automatic mode: Check for calibration before printing. Skip if unnecessary."), ops_auto, "nozzle_offset_cali" ); + option_nozzle_offset_cali_cali->Bind(EVT_SWITCH_PRINT_OPTION, &SelectMachineDialog::on_nozzle_offset_option_changed, this); + + m_pa_value_panel = new wxPanel(m_options_other); + m_pa_value_panel->SetMaxSize(wxSize(FromDIP(315), -1)); + m_pa_value_panel->SetMinSize(wxSize(FromDIP(315), -1)); + wxBoxSizer *m_pa_value_panel_sizer = new wxBoxSizer(wxHORIZONTAL); + + m_pa_value_message = new Label(m_pa_value_panel, _L("Nozzles and filaments of the same type share the same PA profile")); + m_pa_value_message->SetFont(Label::Body_14); + m_pa_value_message->SetBackgroundColour(*wxWHITE); + m_pa_value_message->Wrap(FromDIP(243)); + + m_pa_value_switch = new SwitchButton(m_pa_value_panel); + m_pa_value_switch->SetBackgroundColour(*wxWHITE); + m_pa_value_switch->SetValue(true); + m_pa_value_switch->Bind(wxEVT_TOGGLEBUTTON, &SelectMachineDialog::on_pa_value_switch_changed, this); + + m_pa_value_tips = new ScalableButton(m_pa_value_panel, wxID_ANY, "icon_qusetion", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, true); + m_pa_value_tips->SetBackgroundColour(*wxWHITE); + m_pa_value_tips->Bind(wxEVT_BUTTON, [this](wxCommandEvent &e){ + std::string language = wxGetApp().app_config->get("language"); + wxString region = "en"; + if (language.find("zh") == 0) { + region = "zh"; + } + wxString url = wxString::Format("https://wiki.qidi3d.com/%s/software/qidi-studio", region); + wxLaunchDefaultBrowser(url); + e.Skip(); + }); + + m_pa_value_panel_sizer->Add(m_pa_value_message, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, FromDIP(4)); + m_pa_value_panel_sizer->Add(m_pa_value_tips, 0, wxALIGN_CENTER_VERTICAL); + m_pa_value_panel_sizer->Add(0, 0, 1, wxEXPAND); + m_pa_value_panel_sizer->Add(m_pa_value_switch, 0, wxALIGN_CENTER_VERTICAL); + + m_pa_value_panel->SetSizer(m_pa_value_panel_sizer); + m_pa_value_panel->Hide(); + + auto options_sizer = new wxBoxSizer(wxVERTICAL); m_sizer_options = new wxGridSizer(0, 2, FromDIP(5), FromDIP(10)); m_sizer_options->Add(option_timelapse, 0, wxEXPAND); @@ -637,7 +705,11 @@ SelectMachineDialog::SelectMachineDialog(Plater *plater) m_checkbox_list_order.push_back(option_flow_dynamics_cali); m_checkbox_list_order.push_back(option_nozzle_offset_cali_cali); - m_options_other->SetSizer(m_sizer_options); + options_sizer->Add(m_sizer_options, 0, wxEXPAND, 0); + options_sizer->Add(0, 0, 1, wxTOP, FromDIP(8)); + options_sizer->Add(m_pa_value_panel); + + m_options_other->SetSizer(options_sizer); m_options_other->Layout(); m_options_other->Fit(); @@ -647,7 +719,7 @@ SelectMachineDialog::SelectMachineDialog(Plater *plater) m_checkbox_list["flow_cali"] = option_flow_dynamics_cali; m_checkbox_list["nozzle_offset_cali"] = option_nozzle_offset_cali_cali; for (auto print_opt : m_checkbox_list_order) { - print_opt->Bind(EVT_SWITCH_PRINT_OPTION, [this](auto &e) { save_option_vals(); }); + print_opt->Bind(EVT_SWITCH_PRINT_OPTION, [this](auto& e) { save_option_vals(); e.Skip();}); } option_auto_bed_level->Hide(); @@ -1050,6 +1122,7 @@ void SelectMachineDialog::sync_ams_mapping_result(std::vector &res wxString ams_id = "Ext";// wxColour ams_col = wxColour(0xCE, 0xCE, 0xCE); it->second->item->set_ams_info(ams_col, ams_id); + it->second->item->set_nozzle_info(get_mapped_nozzle_str(it->first)); } return; } @@ -1089,6 +1162,7 @@ void SelectMachineDialog::sync_ams_mapping_result(std::vector &res cols.push_back(DevAmsTray::decode_color(col)); } m->set_ams_info(ams_col, ams_id,f->ctype, cols); + m->set_nozzle_info(get_mapped_nozzle_str(id)); //y59 if (exceed_slot_num) m->disable(); @@ -1216,23 +1290,13 @@ bool SelectMachineDialog::do_ams_mapping(MachineObject *obj_,bool use_ams) //single nozzle else { - if (obj_->is_support_amx_ext_mix_mapping()){ - map_opt = { false, true, false, false }; //four values: use_left_ams, use_right_ams, use_left_ext, use_right_ext - if (!use_ams) { - map_opt[1] = false; - map_opt[3] = true; - } - filament_result = DevMappingUtil::ams_filament_mapping(obj_, m_filaments, m_ams_mapping_result, map_opt); - //auto_supply_with_ext(obj_->vt_slot); - } - else { - map_opt = { false, true, false, false }; - if (!use_ams) { - map_opt[1] = false; - map_opt[3] = true; - } - filament_result = DevMappingUtil::ams_filament_mapping(obj_, m_filaments, m_ams_mapping_result, map_opt); + map_opt = { false, true, false, false }; //four values: use_left_ams, use_right_ams, use_left_ext, use_right_ext + if (!use_ams) { + map_opt[1] = false; + map_opt[3] = true; } + filament_result = DevMappingUtil::ams_filament_mapping(obj_, m_filaments, m_ams_mapping_result, map_opt); + // auto_supply_with_ext(obj_->vt_slot); } if (filament_result == 0) { @@ -1492,78 +1556,6 @@ void SelectMachineDialog::auto_supply_with_ext(std::vector slots) { } } -bool SelectMachineDialog::is_nozzle_type_match(DevExtderSystem data, wxString& error_message) const { - if (data.GetTotalExtderCount() <= 1 || !wxGetApp().preset_bundle) - return false; - - const auto& project_config = wxGetApp().preset_bundle->project_config; - //check nozzle used - auto used_filaments = wxGetApp().plater()->get_partplate_list().get_curr_plate()->get_used_filaments(); // 1 based - auto filament_maps = wxGetApp().plater()->get_partplate_list().get_curr_plate()->get_real_filament_maps(project_config); // 1 based - std::map used_extruders_flow; - std::vector used_extruders; // 0 based - for (auto f : used_filaments) { - int filament_extruder = filament_maps[f - 1] - 1; - if (std::find(used_extruders.begin(), used_extruders.end(), filament_extruder) == used_extruders.end()) used_extruders.emplace_back(filament_extruder); - } - - std::sort(used_extruders.begin(), used_extruders.end()); - - auto nozzle_volume_type_opt = dynamic_cast(wxGetApp().preset_bundle->project_config.option("nozzle_volume_type")); - for (auto i = 0; i < used_extruders.size(); i++) { - if (nozzle_volume_type_opt) { - NozzleVolumeType nozzle_volume_type = (NozzleVolumeType) (nozzle_volume_type_opt->get_at(used_extruders[i])); - if (nozzle_volume_type == NozzleVolumeType::nvtStandard) { used_extruders_flow[used_extruders[i]] = "Standard";} - else {used_extruders_flow[used_extruders[i]] = "High Flow";} - } - } - - vector map_extruders = {1, 0}; - - - // The default two extruders are left, right, but the order of the extruders on the machine is right, left. - std::vector flow_type_of_machine; - for (const auto& it : data.GetExtruders()) - { - if (it.GetNozzleFlowType() == NozzleFlowType::H_FLOW) - { - flow_type_of_machine.push_back(L("High Flow")); - } - else if (it.GetNozzleFlowType() == NozzleFlowType::S_FLOW) - { - flow_type_of_machine.push_back(L("Standard")); - } - } - - //Only when all preset nozzle types and machine nozzle types are exactly the same, return true. - for (std::map::iterator it = used_extruders_flow.begin(); it!= used_extruders_flow.end(); it++) { - int target_machine_nozzle_id = map_extruders[it->first]; - - if (target_machine_nozzle_id < flow_type_of_machine.size()) { - if (flow_type_of_machine[target_machine_nozzle_id] != used_extruders_flow[it->first]) { - - wxString pos; - if (target_machine_nozzle_id == DEPUTY_EXTRUDER_ID) - { - pos = _L("left nozzle"); - } - else if(target_machine_nozzle_id == MAIN_EXTRUDER_ID) - { - pos = _L("right nozzle"); - } - - error_message = wxString::Format(_L("The nozzle flow setting of %s(%s) doesn't match with the slicing file(%s). " - "Please make sure the nozzle installed matches with settings in printer, " - "then set the corresponding printer preset while slicing."), pos, - _L(flow_type_of_machine[target_machine_nozzle_id]), - _L(used_extruders_flow[it->first])); - return false; - } - } - } - return true; -} - int SelectMachineDialog::convert_filament_map_nozzle_id_to_task_nozzle_id(int nozzle_id) { if (nozzle_id == (int)FilamentMapNozzleId::NOZZLE_LEFT) { @@ -1592,7 +1584,6 @@ void SelectMachineDialog::update_print_status_msg() Layout(); Fit(); } - } void SelectMachineDialog::update_print_error_info(int code, std::string msg, std::string extra) @@ -1618,7 +1609,7 @@ bool SelectMachineDialog::check_sdcard_for_timelpase(MachineObject* obj) return false; } -void SelectMachineDialog::show_status(PrintDialogStatus status, std::vector params, wxString wiki_url) +void SelectMachineDialog::show_status(PrintDialogStatus status, std::vector params, wxString wiki_url, prePrintInfoStyle style) { wxString msg; wxString tips; @@ -1819,7 +1810,17 @@ void SelectMachineDialog::show_status(PrintDialogStatus status, std::vector _get_used_nozzle_idxes() return used_nozzle_idxes; } - -static bool _is_nozzle_data_valid(MachineObject* obj_, const DevExtderSystem &ext_data) +bool SelectMachineDialog::is_nozzle_hrc_matched(const NozzleType& nozzle_type, std::string& filament_type) const { - if (obj_ == nullptr) return false; - - PresetBundle *preset_bundle = wxGetApp().preset_bundle; - - try { - PartPlate *cur_plate = wxGetApp().plater()->get_partplate_list().get_curr_plate(); - auto used_filament_idxs = cur_plate->get_used_filaments(); /*the index is started from 1*/ - for (int used_filament_idx : used_filament_idxs) - { - int used_nozzle_idx = cur_plate->get_physical_extruder_by_filament_id(preset_bundle->full_config(), used_filament_idx); - if (ext_data.GetNozzleType(used_nozzle_idx) == NozzleType::ntUndefine || - ext_data.GetNozzleDiameter(used_nozzle_idx) <= 0.0f || - ext_data.GetNozzleFlowType(used_nozzle_idx) == NozzleFlowType::NONE_FLOWTYPE) { - return false; - } - } - } catch (const std::exception &) { - return false; - } - - return true; -} - - -/**************************************************************//* - * @param tag_nozzle_type -- return the mismatch nozzle type - * @param tag_nozzle_diameter -- return the target nozzle_diameter but mismatch - * @return is same or not -/*************************************************************/ -static bool _is_same_nozzle_diameters(MachineObject* obj, float &tag_nozzle_diameter, int& mismatch_nozzle_id) -{ - if (obj == nullptr) return false; - - PresetBundle* preset_bundle = wxGetApp().preset_bundle; - auto opt_nozzle_diameters = preset_bundle->printers.get_edited_preset().config.option("nozzle_diameter"); - if (!opt_nozzle_diameters) - { - return false; - } - - try - { - PartPlate* cur_plate = wxGetApp().plater()->get_partplate_list().get_curr_plate(); - auto used_filament_idxs = cur_plate->get_used_filaments();/*the index is started from 1*/ - for (int used_filament_idx : used_filament_idxs) - { - int used_nozzle_idx = cur_plate->get_physical_extruder_by_filament_id(preset_bundle->full_config(), used_filament_idx); - if (used_nozzle_idx == -1) - { - assert(0); - return false; - } - - tag_nozzle_diameter = float(opt_nozzle_diameters->get_at(used_nozzle_idx)); - if (tag_nozzle_diameter != obj->GetExtderSystem()->GetNozzleDiameter(used_nozzle_idx)) - { - mismatch_nozzle_id = used_nozzle_idx; - return false; - } - } - } - catch (const std::exception&) - { - return false; - } - - return true; -} - -bool SelectMachineDialog::is_nozzle_hrc_matched(const DevExtder* extruder, std::string& filament_type) const -{ - auto printer_nozzle_hrc = Print::get_hrc_by_nozzle_type(extruder->GetNozzleType()); + auto printer_nozzle_hrc = Print::get_hrc_by_nozzle_type(nozzle_type); auto preset_bundle = wxGetApp().preset_bundle; MaterialHash::const_iterator iter = m_materialList.begin(); while (iter != m_materialList.end()) { Material* item = iter->second; MaterialItem* m = item->item; - auto filament_nozzle_hrc = preset_bundle->get_required_hrc_by_filament_type(m->m_material_name.ToStdString()); + auto filament_nozzle_hrc = preset_bundle->get_required_hrc_by_filament_id(m->m_filament_id); if (abs(filament_nozzle_hrc) > abs(printer_nozzle_hrc)) { filament_type = m->m_material_name.ToStdString(); @@ -2263,6 +2193,8 @@ void SelectMachineDialog::on_ok_btn(wxCommandEvent &event) // auto tid = m_ams_mapping_result[i].tray_id; + // auto option = GUI::wxGetApp().preset_bundle->get_filament_by_filament_id(m_ams_mapping_result[i].filament_id); + // std::string filament_name = option ? option->filament_name : ""; // std::string filament_type = boost::to_upper_copy(m_ams_mapping_result[i].type); // std::string filament_brand; @@ -2276,7 +2208,9 @@ void SelectMachineDialog::on_ok_btn(wxCommandEvent &event) // std::string action; // wxString info; // wxString wiki_url; - // DeviceManager::check_filaments_in_blacklist_url(obj_->printer_type, filament_brand, filament_type, m_ams_mapping_result[i].filament_id, ams_id, slot_id, "", in_blacklist, + // std::string nozzle_flow = obj_->GetFilaSystem() ? obj_->GetFilaSystem()->GetNozzleFlowStringByAmsId(std::to_string(ams_id)) : std::string(); + + // DevFilaBlacklist::check_filaments_in_blacklist_url(obj_->printer_type, filament_brand, filament_type, m_ams_mapping_result[i].filament_id, ams_id, slot_id, filament_name, nozzle_flow, in_blacklist, // action, info, wiki_url); // if (in_blacklist && action == "warning") { // confirm_text.push_back(ConfirmBeforeSendInfo(info, wiki_url)); @@ -2786,8 +2720,10 @@ void SelectMachineDialog::load_option_vals(MachineObject *obj) m_checkbox_list["enable_multi_box"]->enable(true); m_checkbox_list["enable_multi_box"]->update_tooltip(wxEmptyString); bool useExt = _HasExt(m_ams_mapping_result); - if (useExt){ + //y75 + if (useExt && !m_ams_mapping_result.empty()) { m_checkbox_list["enable_multi_box"]->setValue("off"); + save_option_vals(); } } else { m_checkbox_list["enable_multi_box"]->enable(false); @@ -2806,6 +2742,26 @@ void SelectMachineDialog::load_option_vals(MachineObject *obj) // m_checkbox_list["timelapse"]->update_tooltip(error_messgae); //} + //y75 + //if (obj->GetNozzleSystem()->GetNozzleRack()->IsSupported()) { + // m_checkbox_list["nozzle_offset_cali"]->update_tooltip_options_area(S_RACK_NOZZLE_OFFSET_CALI_WARNING); + //} else { + // m_checkbox_list["nozzle_offset_cali"]->update_tooltip_options_area(wxEmptyString); + //} + + //bool options_line_ignore = false; + //if(wxGetApp().app_config){ + // options_line_ignore = wxGetApp().app_config->get("disable_auto_flow_cali_tips") == "true"; + //} + + //if (obj->is_support_pa_mode && m_checkbox_list["flow_cali"]->IsShown()) { + // std::string flow_cali_value = m_checkbox_list["flow_cali"]->getValue(); + // if (flow_cali_value == "off") { m_pa_value_panel->Show(); } + //} else { + // m_pa_value_panel->Hide(); + //} + //y75 + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " end"; } @@ -2982,6 +2938,9 @@ void SelectMachineDialog::on_send_print() m_print_job->task_ams_mapping = ams_mapping_array; m_print_job->task_ams_mapping2 = ams_mapping_array2; m_print_job->task_ams_mapping_info = ams_mapping_info; + if (!obj_->get_nozzle_mapping_result().GetNozzleMappingJson().empty()) { + m_print_job->task_nozzle_mapping = obj_->get_nozzle_mapping_result().GetNozzleMappingJson().dump(); + } /* build nozzles info for multi extruders printers */ if (build_nozzles_info(m_print_job->task_nozzles_info)) { @@ -2997,6 +2956,7 @@ void SelectMachineDialog::on_send_print() timelapse_option = m_checkbox_list["timelapse"]->getValue() == "on"; } + //y m_print_job->set_print_config( MachineBedTypeString[0], (m_checkbox_list["bed_leveling"]->getValue() == "on"), @@ -3008,7 +2968,8 @@ void SelectMachineDialog::on_send_print() m_checkbox_list["bed_leveling"]->getValueInt(), m_checkbox_list["enable_multi_box"]->getValueInt(), m_checkbox_list["flow_cali"]->getValueInt(), - m_checkbox_list["nozzle_offset_cali"]->getValueInt() + m_checkbox_list["nozzle_offset_cali"]->getValueInt(), + (m_pa_value_switch->GetValue() ? 0 : 1) ); if (obj_->HasAms()) { @@ -3179,6 +3140,7 @@ void SelectMachineDialog::on_set_finish_mapping(wxCommandEvent &evt) //if (item->id == m_current_filament_id) { // auto ams_colour = wxColour(wxAtoi(selection_data_arr[0]), wxAtoi(selection_data_arr[1]), wxAtoi(selection_data_arr[2]), wxAtoi(selection_data_arr[3])); // m->set_ams_info(ams_colour, selection_data_arr[4], ctype, material_cols); + // m->set_nozzle_info(get_mapped_nozzle_str(item->id)); //} if (f->id == id) { wxString ams_id; @@ -3517,11 +3479,29 @@ void SelectMachineDialog::update_printer_combobox(wxCommandEvent &event) update_user_printer(); } +void SelectMachineDialog::update_ams_backup(MachineObject* obj_) +{ + bool to_show = obj_ && obj_->GetFilaSystem()->CanShowFilamentBackup() && _HasAms(m_ams_mapping_result); + if (to_show != m_ams_backup_tip->IsShown() || to_show != img_ams_backup->IsShown()) { + m_ams_backup_tip->Show(to_show); + img_ams_backup->Show(to_show); + Layout(); + Fit(); + } +} + +Slic3r::MachineObject* SelectMachineDialog::get_current_machine() const +{ + ///show auto refill + DeviceManager* dev_ = Slic3r::GUI::wxGetApp().getDeviceManager(); + if (!dev_) return nullptr; + return dev_->get_my_machine(m_printer_last_select); +} + void SelectMachineDialog::on_timer(wxTimerEvent &event) { - update_show_status(); - //y61 + update_show_status(); if (_HasAms(m_ams_mapping_result) && wxGetApp().plater()->box_msg.auto_reload_detect) { if (!m_ams_backup_tip->IsShown()) { m_ams_backup_tip->Show(); @@ -3539,10 +3519,7 @@ void SelectMachineDialog::on_timer(wxTimerEvent &event) Layout(); Fit(); - ///show auto refill - DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager(); - if(!dev) return; - MachineObject* obj_ = dev->get_selected_machine(); + MachineObject* obj_ = get_current_machine(); if(!obj_) return; if (obj_->GetExtderSystem()->GetTotalExtderCount() > 1) @@ -3552,23 +3529,7 @@ void SelectMachineDialog::on_timer(wxTimerEvent &event) change_materialitem_tip(true); } - if (!obj_->GetFilaSystem()->HasAms() - || obj_->ams_exist_bits == 0 - || !obj_->is_support_filament_backup - || !obj_->GetExtderSystem()->HasFilamentBackup() - || !obj_->GetFilaSystem()->IsAutoRefillEnabled() - || !_HasAms(m_ams_mapping_result)) { - if (m_ams_backup_tip->IsShown()) { - m_ams_backup_tip->Hide(); - img_ams_backup->Hide(); - } - } - else { - if (!m_ams_backup_tip->IsShown()) { - m_ams_backup_tip->Show(); - img_ams_backup->Show(); - } - } + update_ams_backup(obj_); load_option_vals(obj_); update_show_status(obj_); @@ -3680,6 +3641,7 @@ void SelectMachineDialog::on_selection_changed(wxCommandEvent &event) // /* reset timeout and reading printer info */ // m_status_bar->reset(); // m_timeout_count = 0; + // s_nozzle_mapping_last_request_time = 0; // m_ams_mapping_res = false; // m_ams_mapping_valid = false; // m_ams_mapping_result.clear(); @@ -3722,6 +3684,7 @@ void SelectMachineDialog::on_selection_changed(wxCommandEvent &event) // obj->command_get_version(); // obj->command_request_push_all(); + // obj->clear_auto_nozzle_mapping(); // if (!dev->get_selected_machine()) { // dev->set_selected_machine(m_printer_last_select); // }else if (dev->get_selected_machine()->get_dev_id() != m_printer_last_select) { @@ -3737,6 +3700,8 @@ void SelectMachineDialog::on_selection_changed(wxCommandEvent &event) // //reset print status + // m_text_printer_msg->Clear(); + // m_statictext_ams_msg->Clear(); // show_status(PrintDialogStatus::PrintStatusInit); // update_show_status(); @@ -3893,7 +3858,9 @@ void SelectMachineDialog::update_show_status(MachineObject* obj_) } reset_timeout(); - if (!has_box_machine) { + + //y75 + if (!has_box_machine || m_checkbox_list["enable_multi_box"]->getValue() == "off") { m_ams_mapping_result.clear(); sync_ams_mapping_result(m_ams_mapping_result); return; @@ -3935,18 +3902,18 @@ void SelectMachineDialog::update_show_status(MachineObject* obj_) } } - //y67 - const auto &full_config = wxGetApp().preset_bundle->full_config(); - size_t nozzle_nums = full_config.option("nozzle_diameter")->values.size(); + //y67 y75 + //const auto &full_config = wxGetApp().preset_bundle->full_config(); + //size_t nozzle_nums = full_config.option("nozzle_diameter")->values.size(); //if (!DevPrinterConfigUtil::support_ams_ext_mix_print(obj_->printer_type)) - if (nozzle_nums == 1) { - bool useAms = _HasAms(m_ams_mapping_result); - bool useExt = _HasExt(m_ams_mapping_result); - if (useAms && useExt) { - show_status(PrintDialogStatus::PrintStatusAmsMappingMixInvalid); - return; - } - } + // if (nozzle_nums == 1) { + // bool useAms = _HasAms(m_ams_mapping_result); + // bool useExt = _HasExt(m_ams_mapping_result); + // if (useAms && useExt) { + // show_status(PrintDialogStatus::PrintStatusAmsMappingMixInvalid); + // return; + // } + // } if (m_print_status == PrintDialogStatus::PrintStatusAmsMappingInvalid && m_ams_mapping_result.size() == 1 && _HasExt(m_ams_mapping_result)) { show_status(PrintDialogStatus::PrintStatusInit); @@ -4050,19 +4017,20 @@ void SelectMachineDialog::update_show_status(MachineObject* obj_) } /*reading done*/ - if (wxGetApp().app_config) { - if (obj_->upgrade_force_upgrade) { + auto upgrade_ptr = obj_->GetUpgrade().lock(); + if (upgrade_ptr) { + if (upgrade_ptr->IsUpgradeForceUpgrade()) { show_status(PrintDialogStatus::PrintStatusNeedForceUpgrading); return; } - if (obj_->upgrade_consistency_request) { + if (upgrade_ptr->IsUpgradeConsistencyRequest()) { show_status(PrintStatusNeedConsistencyUpgrading); return; } } - if (!obj_->is_fdm_type()) { + if (!obj_->GetInfo()->IsFdmMode()) { show_status(PrintDialogStatus::PrintStatusModeNotFDM); return; } @@ -4083,7 +4051,7 @@ void SelectMachineDialog::update_show_status(MachineObject* obj_) show_status(PrintDialogStatus::PrintStatusNoSdcard); return; } - if (wxGetApp().preset_bundle->filament_presets.size() > 16 && m_print_type != PrintFromType::FROM_SDCARD_VIEW) { + if (wxGetApp().preset_bundle->filament_presets.size() > 16 && m_print_type != PrintFromType::FROM_SDCARD_VIEW) { if (!obj_->is_enable_ams_np && !obj_->is_enable_np) { show_status(PrintDialogStatus::PrintStatusColorQuantityExceed); @@ -4108,6 +4076,14 @@ void SelectMachineDialog::update_show_status(MachineObject* obj_) return; } + if (!CheckErrorRackStatus(obj_)) { + return; + } + + if (!CheckErrorExtruderNozzleWithSlicing(obj_)) { + return; + } + /*disable print when there is no mapping*/ if (obj_->GetExtderSystem()->GetTotalExtderCount() > 1) { for (auto mres : m_ams_mapping_result) { @@ -4118,83 +4094,6 @@ void SelectMachineDialog::update_show_status(MachineObject* obj_) } } - const auto &full_config = wxGetApp().preset_bundle->full_config(); - size_t nozzle_nums = full_config.option("nozzle_diameter")->values.size(); - - /*the nozzle type of preset and machine are different*/ - if (nozzle_nums > 1 && m_print_type == FROM_NORMAL) { - if (!_is_nozzle_data_valid(obj_, *obj_->GetExtderSystem())) { - show_status(PrintDialogStatus::PrintStatusNozzleDataInvalid); - return; - } - - wxString error_message; - if (!is_nozzle_type_match(*obj_->GetExtderSystem(), error_message)) { - std::vector params{error_message}; - params.emplace_back(_L("Tips: If you changed your nozzle of your printer lately, Please go to 'Device -> Printer parts' to change your nozzle setting.")); - show_status(PrintDialogStatus::PrintStatusNozzleMatchInvalid, params); - return; - } - } - - // check nozzle type and diameter - if (m_print_type == PrintFromType::FROM_NORMAL) - { - int mismatch_nozzle_id = 0; - float nozzle_diameter = 0; - if (!_is_same_nozzle_diameters(obj_, nozzle_diameter, mismatch_nozzle_id)) - { - std::vector msg_params; - if (obj_->GetExtderSystem()->GetTotalExtderCount() == 2) { - wxString mismatch_nozzle_str; - if (mismatch_nozzle_id == MAIN_EXTRUDER_ID) { - mismatch_nozzle_str = _L("right nozzle"); - } else { - mismatch_nozzle_str = _L("left nozzle"); - } - - const wxString &nozzle_config = wxString::Format(_L("The %s diameter(%.1fmm) of current printer doesn't match with the slicing file (%.1fmm). " - "Please make sure the nozzle installed matches with settings in printer, then set the " - "corresponding printer preset when slicing."), - mismatch_nozzle_str, obj_->GetExtderSystem()->GetNozzleDiameter(mismatch_nozzle_id), nozzle_diameter); - msg_params.emplace_back(nozzle_config); - } else { - const wxString &nozzle_config = wxString::Format(_L("The current nozzle diameter (%.1fmm) doesn't match with the slicing file (%.1fmm). " - "Please make sure the nozzle installed matches with settings in printer, then set the " - "corresponding printer preset when slicing."), - obj_->GetExtderSystem()->GetNozzleDiameter(0), nozzle_diameter); - msg_params.emplace_back(nozzle_config); - } - - msg_params.emplace_back(_L("Tips: If you changed your nozzle of your printer lately, Please go to 'Device -> Printer parts' to change your nozzle setting.")); - show_status(PrintDialogStatus::PrintStatusNozzleDiameterMismatch, msg_params); - return; - } - - const auto &used_nozzle_idxes = _get_used_nozzle_idxes(); - for (const auto &extder : obj_->GetExtderSystem()->GetExtruders()) { - if (used_nozzle_idxes.count(extder.GetNozzleId()) == 0) { continue; } - - std::string filament_type; - if (!is_nozzle_hrc_matched(&extder, filament_type)) { - std::vector error_msg; - error_msg.emplace_back(wxString::Format(_L("The hardness of current material (%s) exceeds the hardness of %s(%s). Please verify the nozzle or material settings and try again."), - filament_type, _get_nozzle_name(obj_->GetExtderSystem()->GetTotalExtderCount(), extder.GetNozzleId()), format_steel_name(extder.GetNozzleType()))); - show_status(PrintDialogStatus::PrintStatusNozzleTypeMismatch, error_msg); - return; - } - } - } - - if (!DevPrinterConfigUtil::support_ams_ext_mix_print(obj_->printer_type)) { - bool useAms = _HasAms(m_ams_mapping_result); - bool useExt = _HasExt(m_ams_mapping_result); - if (useAms && useExt) { - show_status(PrintDialogStatus::PrintStatusAmsMappingMixInvalid); - return; - } - } - if (obj_->GetFilaSystem()->IsAmsSettingUp()) { show_status(PrintDialogStatus::PrintStatusAmsOnSettingup); return; @@ -4205,6 +4104,15 @@ void SelectMachineDialog::update_show_status(MachineObject* obj_) return; } + if (!DevPrinterConfigUtil::support_ams_ext_mix_print(obj_->printer_type)) { + bool useAms = _HasAms(m_ams_mapping_result); + bool useExt = _HasExt(m_ams_mapping_result); + if (useAms && useExt) { + show_status(PrintDialogStatus::PrintStatusAmsMappingMixInvalid); + return; + } + } + // filaments check for black list for (auto i = 0; i < m_ams_mapping_result.size(); i++) { const auto &ams_id = m_ams_mapping_result[i].get_ams_id(); @@ -4212,6 +4120,9 @@ void SelectMachineDialog::update_show_status(MachineObject* obj_) auto tid = m_ams_mapping_result[i].tray_id; + auto option = GUI::wxGetApp().preset_bundle->get_filament_by_filament_id(m_ams_mapping_result[i].filament_id); + std::string filament_name = option ? option->filament_name : ""; + std::string filament_type = boost::to_upper_copy(m_ams_mapping_result[i].type); std::string filament_brand; @@ -4223,7 +4134,9 @@ void SelectMachineDialog::update_show_status(MachineObject* obj_) std::string action; wxString info; wxString wiki_url; - DevFilaBlacklist::check_filaments_in_blacklist_url(obj_->printer_type, filament_brand, filament_type, m_ams_mapping_result[i].filament_id, ams_id, slot_id, "", in_blacklist, + std::string nozzle_flow = obj_->GetFilaSystem() ? obj_->GetFilaSystem()->GetNozzleFlowStringByAmsId(std::to_string(ams_id)) : std::string(); + + DevFilaBlacklist::check_filaments_in_blacklist_url(obj_->printer_type, filament_brand, filament_type, m_ams_mapping_result[i].filament_id, ams_id, slot_id, filament_name, nozzle_flow, in_blacklist, action, info, wiki_url); if (in_blacklist) { @@ -4239,12 +4152,19 @@ void SelectMachineDialog::update_show_status(MachineObject* obj_) } } + if (!CheckErrorSyncNozzleMappingResult(obj_)) /*check nozzle mapping result for rack*/ { + return; + } + /** warning check **/ struct ExtruderStatus { bool has_ams{false}; bool has_vt_slot{false}; }; + + const auto& full_config = wxGetApp().preset_bundle->full_config(); + size_t nozzle_nums = full_config.option("nozzle_diameter")->values.size(); std::vector extruder_status(nozzle_nums); for (const FilamentInfo &item : m_ams_mapping_result) { if (item.ams_id.empty()) continue; @@ -4358,6 +4278,9 @@ void SelectMachineDialog::update_show_status(MachineObject* obj_) // check extension tool warning UpdateStatusCheckWarning_ExtensionTool(obj_); + // check rack nozzle warning + CheckWarningRackStatus(obj_); + /** normal check **/ show_status(PrintDialogStatus::PrintStatusReadyToGo); #endif @@ -4413,6 +4336,7 @@ void SelectMachineDialog::reset_ams_material() wxString ams_id = "-"; wxColour ams_col = wxColour(0xEE, 0xEE, 0xEE); m->set_ams_info(ams_col, ams_id); + m->set_nozzle_info(get_mapped_nozzle_str(id)); iter++; } } @@ -4466,6 +4390,10 @@ void SelectMachineDialog::on_dpi_changed(const wxRect &suggested_rect) m_mapping_popup.msw_rescale(); + + m_statictext_ams_msg->Rescale(); + m_text_printer_msg->Rescale(); + Fit(); Refresh(); } @@ -4569,6 +4497,9 @@ void SelectMachineDialog::set_default() } } + m_text_printer_msg->Clear(); + m_statictext_ams_msg->Clear(); + if (m_print_type == PrintFromType::FROM_NORMAL) { reset_and_sync_ams_list(); set_default_normal(m_plater->get_partplate_list().get_curr_plate()->thumbnail_data); @@ -4584,6 +4515,8 @@ void SelectMachineDialog::set_default() void SelectMachineDialog::change_materialitem_tip(bool no_ams_only_ext) { + Slic3r::DeviceManager *dev_ = Slic3r::GUI::wxGetApp().getDeviceManager(); + MachineObject *obj_ = dev_->get_selected_machine(); MaterialHash::iterator iter = m_materialList.begin(); while (iter != m_materialList.end()) { int id = iter->first; @@ -4595,7 +4528,10 @@ void SelectMachineDialog::change_materialitem_tip(bool no_ams_only_ext) else { wxString tip_text; if (item->item->m_match) { - tip_text = _L("Upper half area: Original\nLower half area: Filament in Box\nAnd you can click it to modify"); + if (!item->item->m_mapped_nozzle_str.empty()) + tip_text = (_L("Upper half area: Original\nMiddle area: Filament in AMS\nLower half area: Selected nozzle\nAnd you can click it to modify")); + else + tip_text = _L("Upper half area: Original\nLower half area: Filament in AMS\nAnd you can click it to modify"); } else { tip_text = _L("Unable to automatically match to suitable filament. Please click to manually match."); } @@ -4682,18 +4618,18 @@ void SelectMachineDialog::reset_and_sync_ams_list() if (m_filaments_map[extruder] == 1) { - item = new MaterialItem(m_filament_left_panel, colour_rgb, _L(display_materials[extruder])); + item = new MaterialItem(m_filament_left_panel, colour_rgb, _L(display_materials[extruder]), m_filaments_id[extruder]); m_sizer_ams_mapping_left->Add(item, 0, wxALL, FromDIP(5)); } else if (m_filaments_map[extruder] == 2) { - item = new MaterialItem(m_filament_right_panel, colour_rgb, _L(display_materials[extruder])); + item = new MaterialItem(m_filament_right_panel, colour_rgb, _L(display_materials[extruder]), m_filaments_id[extruder]); m_sizer_ams_mapping_right->Add(item, 0, wxALL, FromDIP(5)); } } else { - item = new MaterialItem(m_filament_panel, colour_rgb, _L(display_materials[extruder])); + item = new MaterialItem(m_filament_panel, colour_rgb, _L(display_materials[extruder]), m_filaments_id[extruder]); m_sizer_ams_mapping->Add(item, 0, wxALL, FromDIP(5)); } item->SetToolTip(m_ams_tooltip); @@ -4743,9 +4679,6 @@ void SelectMachineDialog::reset_and_sync_ams_list() //y68 if (obj_ && !m_ams_mapping_result.empty()) { if (m_mapping_popup.IsShown()) return; - wxPoint pos = item->ClientToScreen(wxPoint(0, 0)); - pos.y += item->GetRect().height; - m_mapping_popup.Move(pos); if (obj_) { m_mapping_popup.set_parent_item(item); @@ -5099,6 +5032,13 @@ void SelectMachineDialog::set_default_normal(const ThumbnailData &data) m_stext_weight->SetLabel(weight); } +static wxSize s_get_full_resolution() +{ + wxDisplay display; + wxRect screenRect = display.GetGeometry(); + return wxSize(screenRect.GetWidth(), screenRect.GetHeight()); +} + void SelectMachineDialog::set_default_from_sdcard() { DeviceManager *dev_manager = Slic3r::GUI::wxGetApp().getDeviceManager(); @@ -5196,15 +5136,15 @@ void SelectMachineDialog::set_default_from_sdcard() auto nozzle_id = m_filaments_map[fo.id]; if (nozzle_id == 1) { - item = new MaterialItem(m_filament_left_panel, wxColour(fo.color), fo.get_display_filament_type()); + item = new MaterialItem(m_filament_left_panel, wxColour(fo.color), fo.get_display_filament_type(), fo.filament_id); m_sizer_ams_mapping_left->Add(item, 0, wxALL, FromDIP(5)); } else if (nozzle_id == 2) { - item = new MaterialItem(m_filament_right_panel, wxColour(fo.color), fo.get_display_filament_type()); + item = new MaterialItem(m_filament_right_panel, wxColour(fo.color), fo.get_display_filament_type(), fo.filament_id); m_sizer_ams_mapping_right->Add(item, 0, wxALL, FromDIP(5)); } } } else { - item = new MaterialItem(m_filament_panel, wxColour(fo.color), fo.get_display_filament_type()); + item = new MaterialItem(m_filament_panel, wxColour(fo.color), fo.get_display_filament_type(), fo.filament_id); m_sizer_ams_mapping->Add(item, 0, wxALL, FromDIP(5)); } @@ -5233,10 +5173,7 @@ void SelectMachineDialog::set_default_from_sdcard() wxPoint rect = item->ClientToScreen(wxPoint(0, 0)); if (obj_) { - if (m_mapping_popup.IsShown()) return; - wxPoint pos = item->ClientToScreen(wxPoint(0, 0)); - pos.y += item->GetRect().height; - m_mapping_popup.Move(pos); + if (m_mapping_popup.IsShown()) { return; }; if (diameters_count > 1) { if (obj_ && can_hybrid_mapping(*obj_->GetExtderSystem())) { @@ -5399,7 +5336,7 @@ void SelectMachineDialog::UpdateStatusCheckWarning_ExtensionTool(MachineObject* if (auto extension_tool = obj_->GetExtensionTool().lock()) { - if (extension_tool->IsToolTypeFanF000() && !extension_tool->IsMounted() ) + if (!extension_tool->IsToolTypeFanF000() || !extension_tool->IsMounted() ) { for (const FilamentInfo& item : m_ams_mapping_result) { @@ -5408,7 +5345,7 @@ void SelectMachineDialog::UpdateStatusCheckWarning_ExtensionTool(MachineObject* { show_status(PrintDialogStatus::PrintStatusToolHeadCoolingFanWarning, { _L("Install toolhead enhanced cooling fan to prevent filament softening.")}, - "https://e.qidi3d.com/t?c=l3T7caKGeNt3omA9"); + wxEmptyString, prePrintInfoStyle::BtnInstallFanF000); return; } } @@ -5416,6 +5353,522 @@ void SelectMachineDialog::UpdateStatusCheckWarning_ExtensionTool(MachineObject* } } +// return true if no error +bool SelectMachineDialog::CheckErrorRackStatus(MachineObject* obj_) +{ + if (!obj_) { + return true; + } + + auto rack = obj_->GetNozzleSystem()->GetNozzleRack(); + if (!rack->IsSupported()) { + return true; + } + + if (rack->GetReadingCount() > 0) { + const wxString& msg = wxString::Format(_L("Refreshing information of hotends(%d/%d)."), rack->GetReadingIdx(), rack->GetReadingCount()); + show_status(PrintStatusRackReading, { msg + " " + _L("Please wait a moment...")}); + return false; + } + + return true; +} + +void SelectMachineDialog::CheckWarningRackStatus(MachineObject* obj_) +{ + if (!obj_) { + return; + } + + const auto& nozzle_sys = obj_->GetNozzleSystem(); + const auto& rack = nozzle_sys->GetNozzleRack(); + if (!rack->IsSupported()) { + return; + } + + auto nozzle_group_res = DevUtilBackend::GetNozzleGroupResult(m_plater); + if (!nozzle_group_res) { + return; + } + + if (m_print_type != FROM_NORMAL) { + return;// there are no slicing data when print from sdcard + } + + const std::vector& nozzle_vec = nozzle_group_res->get_nozzle_vec(LOGIC_R_EXTRUDER_ID); + if (nozzle_vec.empty()) { + return;// no need to check if no right nozzles used in slicing + } + + std::unordered_map need_nozzle_map; + for (auto slicing_nozzle : nozzle_vec) { + try { + NozzleDef data; + data.nozzle_diameter = boost::lexical_cast(slicing_nozzle.diameter); + data.nozzle_flow_type = (slicing_nozzle.volume_type == NozzleVolumeType::nvtHighFlow ? NozzleFlowType::H_FLOW : NozzleFlowType::S_FLOW); + need_nozzle_map[data]++; + } catch (const std::exception& e) { + assert(0); + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "exception: " << e.what(); + } + } + + for (auto need_nozzle : need_nozzle_map) { + auto nozzle_info = need_nozzle.first; + const auto& installed_nozzles = nozzle_sys->CollectNozzles(MAIN_EXTRUDER_ID, nozzle_info.nozzle_flow_type, nozzle_info.nozzle_diameter); + int installed_count = installed_nozzles.size(); + int installed_reliable_count = 0; + for (const auto& nozzle : installed_nozzles) { + if (nozzle.IsInfoReliable()) { + installed_reliable_count++; + } + } + + // check if enough nozzles installed + if (need_nozzle.second > installed_count) { + if (nozzle_sys->GetNozzleRack()->GetCaliStatus() != DevNozzleRack::Rack_CALI_OK) { + show_status(PrintStatusRackNozzleNumUnmeetWarning, + { (S_RACK_NOZZLE_NOT_ENOUGH + " " + S_RACK_NOZZLE_SUGEEST_CALI) }, + wxEmptyString, prePrintInfoStyle::NozzleState); + } else if (nozzle_sys->HasUnknownNozzles()) { + show_status(PrintStatusRackNozzleNumUnmeetWarning, + { (S_RACK_NOZZLE_NOT_ENOUGH + " " + S_RACK_NOZZLE_SUGEEST_REFRESH) }, + wxEmptyString, prePrintInfoStyle::NozzleState | prePrintInfoStyle::BtnNozzleRefresh); + } else { + show_status(PrintStatusRackNozzleNumUnmeetWarning, + { (S_RACK_NOZZLE_NOT_ENOUGH + " " + S_RACK_NOZZLE_SUGEEST_RESLICE_FILA) }, + wxEmptyString, prePrintInfoStyle::NozzleState); + } + + break; + } + + // check if unreliable nozzle maybe used + if (need_nozzle.second > installed_reliable_count && nozzle_sys->HasUnreliableNozzles()) { + show_status(PrintStatusHasUnreliableNozzleWarning, {}, wxEmptyString, + prePrintInfoStyle::BtnNozzleRefresh | prePrintInfoStyle::BtnConfirmNotShowAgain); + } + } +} + +// return key-> logic extruder idx, value-> nozzle data +// for example: +// {0, {0.4, S_FLOW}}, {1, {0.8, H_FLOW}} +// {0, {0.4, S_FLOW}}, {0, {0.4, H_FLOW}} // hybrid +static std::unordered_multimap s_get_slicing_extuder_nozzles() +{ + std::unordered_multimap used_extuder_nozzles; + + PresetBundle* preset_bundle = wxGetApp().preset_bundle; + if (!preset_bundle) { + return used_extuder_nozzles; + } + + PartPlate* cur_plate = wxGetApp().plater()->get_partplate_list().get_curr_plate(); + if (!cur_plate) { + return used_extuder_nozzles; + } + + auto opt_nozzle_diameters = preset_bundle->printers.get_edited_preset().config.option("nozzle_diameter"); + if (!opt_nozzle_diameters) { + return used_extuder_nozzles; + } + + auto nozzle_volume_type_opt = dynamic_cast(preset_bundle->project_config.option("nozzle_volume_type")); + if (!nozzle_volume_type_opt) { + return used_extuder_nozzles; + } + + try { + const auto& used_filament_idxs = cur_plate->get_used_filaments(); /*the index is started from 1*/ + for (int used_filament_idx : used_filament_idxs) { + int physical_idx = cur_plate->get_physical_extruder_by_filament_id(preset_bundle->full_config(), used_filament_idx); + int logic_extruder_idx = cur_plate->get_logical_extruder_by_filament_id(preset_bundle->full_config(), used_filament_idx); + + NozzleDef nozzle_data; + nozzle_data.nozzle_diameter = float(opt_nozzle_diameters->get_at(logic_extruder_idx)); + nozzle_data.nozzle_flow_type = NozzleFlowType::S_FLOW;// default value + + auto volume_type = (NozzleVolumeType)nozzle_volume_type_opt->get_at(logic_extruder_idx); + if (volume_type == NozzleVolumeType::nvtHighFlow) { + nozzle_data.nozzle_flow_type = NozzleFlowType::H_FLOW; + } else if (volume_type == NozzleVolumeType::nvtStandard) { + nozzle_data.nozzle_flow_type = NozzleFlowType::S_FLOW; + } else if (volume_type == NozzleVolumeType::nvtHybrid) { + if (used_extuder_nozzles.find(physical_idx) != used_extuder_nozzles.end()){ + continue;// already collected + } + + auto nozzle_group_res = DevUtilBackend::GetNozzleGroupResult(wxGetApp().plater()); + assert(nozzle_group_res.has_value()); + if (nozzle_group_res) { + const auto& nozzle_infos = DevUtilBackend::CollectNozzleInfo(&nozzle_group_res.value(), logic_extruder_idx); + for (const auto& nozzle_info : nozzle_infos) { + nozzle_data.nozzle_flow_type = nozzle_info.first.nozzle_flow_type; + used_extuder_nozzles.insert({ physical_idx, nozzle_data }); + } + + continue; + } + } else { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "invalid nozzle volume type: " << int(volume_type); + assert(0); + } + + used_extuder_nozzles.insert({ physical_idx, nozzle_data }); + }; + } catch (const std::exception& e) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "exception: " << e.what(); + } + + return used_extuder_nozzles; +} + +// Compare the extruder nozzle info between slicing file and installed on printer +bool SelectMachineDialog::CheckErrorExtruderNozzleWithSlicing(MachineObject* obj_) +{ + if (!obj_) { + return false; + } + + const auto& ext_sys = obj_->GetExtderSystem(); + const auto& nozzle_sys = obj_->GetNozzleSystem(); + if (m_print_type == FROM_NORMAL) { + const auto& slicing_ext_nozzles = s_get_slicing_extuder_nozzles(); + for (auto slicing_ext_nozzle : slicing_ext_nozzles) { + int slicing_ext_idx = slicing_ext_nozzle.first; + auto slicing_ext = slicing_ext_nozzle.second; + auto installed_ext_nozzle = nozzle_sys->GetExtNozzle(slicing_ext_idx); + + // no need to check right extruder nozzle when using nozzle rack + if (slicing_ext_idx == MAIN_EXTRUDER_ID && nozzle_sys->GetNozzleRack()->IsSupported()) { + if (nozzle_sys->CollectNozzles(MAIN_EXTRUDER_ID, slicing_ext.nozzle_flow_type, slicing_ext.nozzle_diameter).empty()) { + if (nozzle_sys->GetNozzleRack()->GetCaliStatus() != DevNozzleRack::Rack_CALI_OK) { + show_status(PrintDialogStatus::PrintStatusNozzleNoMatchedHotends, + { (S_RACK_NOZZLE_NO_MATCHED + " " + S_RACK_NOZZLE_SUGEEST_CALI) }, + wxEmptyString, prePrintInfoStyle::NozzleState); + } else if (nozzle_sys->HasUnknownNozzles() || nozzle_sys->HasUnreliableNozzles()) { + show_status(PrintDialogStatus::PrintStatusNozzleNoMatchedHotends, + { (S_RACK_NOZZLE_NO_MATCHED + " " + S_RACK_NOZZLE_SUGEEST_REFRESH) }, + wxEmptyString, prePrintInfoStyle::NozzleState | prePrintInfoStyle::BtnNozzleRefresh); + } else { + show_status(PrintDialogStatus::PrintStatusNozzleNoMatchedHotends, + { (S_RACK_NOZZLE_NO_MATCHED + " " + S_RACK_NOZZLE_SUGEEST_RESLICE) }, + wxEmptyString, prePrintInfoStyle::NozzleState); + } + + return false; + } + + if (nozzle_sys->IsRackMaximumInstalled()) { + show_status(PrintDialogStatus::PrintStatusNozzleRackMaximumInstalled, + { _L("The toolhead and hotend rack are full. Please remove at least one hotend before printing.") }, + wxEmptyString, prePrintInfoStyle::NozzleState); + return false; + } + + continue; + } + + // check nozzle data valid + { + if (installed_ext_nozzle.GetNozzleType() == NozzleType::ntUndefine || + installed_ext_nozzle.GetNozzleDiameter() <= 0.0f) { + show_status(PrintDialogStatus::PrintStatusNozzleDataInvalid); + return false; + } + + if (obj_->is_nozzle_flow_type_supported() && + installed_ext_nozzle.GetNozzleFlowType() == NozzleFlowType::NONE_FLOWTYPE) { + show_status(PrintDialogStatus::PrintStatusNozzleDataInvalid); + return false; + } + } + + // check nozzle flow type + { + if (obj_->is_nozzle_flow_type_supported() && + slicing_ext.nozzle_flow_type != installed_ext_nozzle.GetNozzleFlowType()) { + + const wxString& pos = _get_nozzle_name(ext_sys->GetTotalExtderCount(), slicing_ext_idx); + const wxString& installed_nozzle_str = installed_ext_nozzle.GetNozzleFlowTypeStr(); + const wxString& slicing_nozzle_str = DevNozzle::GetNozzleFlowTypeStr(slicing_ext.nozzle_flow_type); + wxString error_message = wxString::Format(_L("The nozzle flow setting of %s(%s) doesn't match with the slicing file(%s). " + "Please make sure the nozzle installed matches with settings in printer, " + "then set the corresponding printer preset while slicing."), + pos, installed_nozzle_str, slicing_nozzle_str); + + std::vector params{ error_message }; + params.emplace_back(_L("Tips: If you changed your nozzle of your printer lately, Please go to 'Device -> Printer parts' to change your nozzle setting.")); + show_status(PrintDialogStatus::PrintStatusNozzleMatchInvalid, params); + return false; + } + } + + // check nozzle diameter + { + if (slicing_ext.nozzle_diameter != installed_ext_nozzle.GetNozzleDiameter()) { + std::vector msg_params; + if (ext_sys->GetTotalExtderCount() == 2) { + const wxString& mismatch_nozzle_str = _get_nozzle_name(ext_sys->GetTotalExtderCount(), slicing_ext_idx); + const wxString& nozzle_message = wxString::Format(_L("The %s diameter(%.1fmm) of current printer doesn't match with the slicing file (%.1fmm). " + "Please make sure the nozzle installed matches with settings in printer, then set the " + "corresponding printer preset when slicing."), + mismatch_nozzle_str, installed_ext_nozzle.GetNozzleDiameter(), slicing_ext.nozzle_diameter); + msg_params.emplace_back(nozzle_message); + } else { + const wxString& nozzle_message = wxString::Format(_L("The current nozzle diameter (%.1fmm) doesn't match with the slicing file (%.1fmm). " + "Please make sure the nozzle installed matches with settings in printer, then set the " + "corresponding printer preset when slicing."), + installed_ext_nozzle.GetNozzleDiameter(), slicing_ext.nozzle_diameter); + msg_params.emplace_back(nozzle_message); + } + + msg_params.emplace_back(_L("Tips: If you changed your nozzle of your printer lately, Please go to 'Device -> Printer parts' to change your nozzle setting.")); + show_status(PrintDialogStatus::PrintStatusNozzleDiameterMismatch, msg_params); + return false; + } + } + + // check nozzle type + { + std::string filament_type; + auto installed_ext_nozzle_type = installed_ext_nozzle.GetNozzleType(); + if (!is_nozzle_hrc_matched(installed_ext_nozzle_type, filament_type)) { + std::vector error_msg; + error_msg.emplace_back(wxString::Format(_L("The hardness of current material (%s) exceeds the hardness of %s(%s). Please verify the nozzle or material settings and try again."), + filament_type, _get_nozzle_name(ext_sys->GetTotalExtderCount(), installed_ext_nozzle.GetNozzleId()), format_steel_name(installed_ext_nozzle_type))); + show_status(PrintDialogStatus::PrintStatusNozzleTypeMismatch, error_msg); + return false; + } + } + } + } + + return true; +} + +void SelectMachineDialog::on_flow_pa_caliation_option_changed(wxCommandEvent& event) +{ + // update nozzle mapping result if flow pa calibration option changed + DeviceManager* dev_ = Slic3r::GUI::wxGetApp().getDeviceManager(); + MachineObject* obj_ = dev_ ? dev_->get_my_machine(m_printer_last_select) : nullptr; + if (obj_ && m_plater && obj_->GetNozzleSystem()->GetNozzleRack()->IsSupported()) { + auto nozzle_group_res = DevUtilBackend::GetNozzleGroupResult(m_plater); + if (nozzle_group_res && nozzle_group_res->get_nozzle_count(LOGIC_R_EXTRUDER_ID) != 0) { + + if (event.GetString() == "off") { + MessageDialog dlg(this, S_RACK_FLOW_DYNAMICS_CALI_WARNING, _L("Info"), wxYES | wxCANCEL | wxICON_INFORMATION); + dlg.SetButtonLabel(wxID_YES, _L("Keep it On")); + dlg.SetButtonLabel(wxID_CANCEL, _L("Still turn it Off")); + int rtn = dlg.ShowModal(); + if (rtn == wxID_YES) { + m_checkbox_list["flow_cali"]->restoreValue(); + save_option_vals(); + event.Skip(); + return; + } + + // STUDIO-15239 + // request nozzle mapping result if right extruder nozzles used in slicing + obj_->ctrl_get_auto_nozzle_mapping(m_plater, m_ams_mapping_result, m_checkbox_list["flow_cali"]->getValueInt(), m_pa_value_switch->GetValue() ? 0 : 1); + + } + } + } + + if (obj_ && obj_->is_support_pa_mode) + { + if (event.GetString() == "off") + m_pa_value_panel->Show(); + else + m_pa_value_panel->Hide(); + Layout(); + } + event.Skip(); +} + +void SelectMachineDialog::on_pa_value_switch_changed(wxCommandEvent &event) +{ + DeviceManager *dev_ = Slic3r::GUI::wxGetApp().getDeviceManager(); + MachineObject *obj_ = dev_ ? dev_->get_my_machine(m_printer_last_select) : nullptr; + if (obj_ == nullptr) { + event.Skip(); + return; + } + MessageDialog dlg(this, S_RACK_FLOW_DYNAMICS_CALI_WARNING, _L("Info"), wxYES | wxCANCEL | wxICON_INFORMATION); + dlg.SetButtonLabel(wxID_YES, _L("OK")); + dlg.SetButtonLabel(wxID_CANCEL, _L("Cancel")); + int rtn = dlg.ShowModal(); + if (rtn == wxID_CANCEL) { + m_pa_value_switch->SetValue(!m_pa_value_switch->GetValue()); + return; + } + event.Skip(); + obj_->ctrl_get_auto_nozzle_mapping(m_plater, m_ams_mapping_result, m_checkbox_list["flow_cali"]->getValueInt(), m_pa_value_switch->GetValue() ? 0 : 1); +} + +void SelectMachineDialog::on_nozzle_offset_option_changed(wxCommandEvent& event) +{ + DeviceManager* dev_ = Slic3r::GUI::wxGetApp().getDeviceManager(); + MachineObject* obj_ = dev_ ? dev_->get_my_machine(m_printer_last_select) : nullptr; + if (obj_ == nullptr) { + event.Skip(); + return; + } + + if (!obj_->GetNozzleSystem()->GetNozzleRack()->IsSupported()) { + event.Skip(); + return; + } + + if (event.GetString() == "off") { + MessageDialog dlg(this, S_RACK_NOZZLE_OFFSET_CALI_WARNING, _L("Info"), wxYES | wxCANCEL | wxICON_INFORMATION); + dlg.SetButtonLabel(wxID_YES, _L("Keep it On")); + dlg.SetButtonLabel(wxID_CANCEL, _L("Still turn it Off")); + int rtn = dlg.ShowModal(); + if (rtn == wxID_YES) { + m_checkbox_list["nozzle_offset_cali"]->restoreValue(); + save_option_vals(); + } + } + + event.Skip(); +} + +// return empty string if not supported +// return ? if not mapped +wxString SelectMachineDialog::get_mapped_nozzle_str(int fila_id) +{ + auto obj_ = get_current_machine(); + if (!obj_) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": obj_ NULL"; + return wxEmptyString; + } + + if (!obj_->GetNozzleRack()->IsSupported()) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": not support nozzle rack"; + return wxEmptyString; + } + + if(m_print_type != FROM_NORMAL) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": from normal"; + return wxEmptyString;// there are no slicing data when print from sdcard + } + + if (!m_plater) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": m_plater NULL"; + } + + auto nozzle_group_res = DevUtilBackend::GetNozzleGroupResult(m_plater); + if (!nozzle_group_res) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": nozzle_group_res NULL"; + return wxEmptyString;// there are no nozzle group result from slicing + } + + if (nozzle_group_res->get_extruder_id(fila_id) != LOGIC_R_EXTRUDER_ID) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": LOGIC_R_EXTRUDER_ID not used"; + return wxEmptyString;// the filament is not used at right extruder in slicing + } + + if (m_nozzle_mapping_result.empty()) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": m_nozzle_mapping_result empty"; + return "?"; + } + + auto iter = m_nozzle_mapping_result.find(fila_id); + if (iter == m_nozzle_mapping_result.end()) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": not mapped for " << fila_id; + return "?";// the filament is not mapped by the machine + } + + // get display name of nozzle id + if (iter->second == MAIN_EXTRUDER_ID) { + return "R"; + } else if (obj_->GetNozzleSystem()->GetReplaceNozzleTar().has_value() && iter->second == obj_->GetNozzleSystem()->GetReplaceNozzleTar().value()) { + return "R"; + } else if (iter->second >= 0x10) { + return wxString::Format("%d", iter->second - 0x10 + 1);// display 1~n for rack hotends + } + + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": invalid mapped nozzle id " << iter->second << " for " << fila_id; + return "?"; +} + +bool SelectMachineDialog::CheckErrorSyncNozzleMappingResult(MachineObject* obj_) +{ + if (!obj_) { + return true; + } + + if (!obj_->GetNozzleRack()->IsSupported()){ + return true;// no need to check if not support nozzle rack + } + + if (m_print_type != FROM_NORMAL) { + return true;// there are no slicing data when print from sdcard + } + + auto nozzle_group_res = DevUtilBackend::GetNozzleGroupResult(m_plater);; + if (nozzle_group_res && nozzle_group_res->get_nozzle_count(LOGIC_R_EXTRUDER_ID) == 0) { + return true;// no need to check if no right nozzles used in slicing + } + + const auto& nozzle_mapping_res = obj_->get_nozzle_mapping_result(); + if (!nozzle_mapping_res.HasResult()) { + if (time(nullptr) - s_nozzle_mapping_last_request_time > 10) { // avoid too many requests + int rtn = obj_->ctrl_get_auto_nozzle_mapping(m_plater, m_ams_mapping_result, m_checkbox_list["flow_cali"]->getValueInt(), m_pa_value_switch->GetValue() ? 0 : 1 ); + if (rtn == 0) { + s_nozzle_mapping_last_request_time = time(nullptr); + } else { + const auto& err_msg = wxString::Format(_L("Failed to send nozzle auto-mapping request to printer { code: %d }. " + "Please try to refresh the printer information. " + "If it still does not recover, you can try to rebind the printer and check the network connection."), rtn); + show_status(PrintDialogStatus::PrintStatusRackNozzleMappingWaiting, { err_msg }); + return false; + } + } + + show_status(PrintDialogStatus::PrintStatusRackNozzleMappingWaiting, { _L("The printer is calculating nozzle mapping.") + " " + _L("Please wait a moment...")}); + return false; + } + + if (nozzle_mapping_res.GetResultStr() == "failed") { + const auto& err_msg = wxString::Format(_L("Failed to receive nozzle auto-mapping table from printer { msg: %s }. Please refresh the printer information."), nozzle_mapping_res.GetMqttReason()); + show_status(PrintDialogStatus::PrintStatusRackNozzleMappingError, { err_msg }); + return false; + } + + if (nozzle_mapping_res.GetResultStr() == "fail") { + const wxString& err_msg = wxString::Format(_L("The printer failed to build the nozzle auto-mapping table { code: %d }. Please refreash nozzle information."), nozzle_mapping_res.GetErrno()); + show_status(PrintDialogStatus::PrintStatusRackNozzleMappingError, { err_msg }); + return false; + } + + if (!obj_->get_nozzle_mapping_result().GetNozzleMapping().empty()) { + if (m_nozzle_mapping_result != obj_->get_nozzle_mapping_result().GetNozzleMapping()) { + m_nozzle_mapping_result = obj_->get_nozzle_mapping_result().GetNozzleMapping(); + sync_ams_mapping_result(m_ams_mapping_result); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": sync_ams_mapping_result done."; + } + + float flush_waste_base = obj_->get_nozzle_mapping_result().GetFlushWeightBase(); + float flush_waste_current = obj_->get_nozzle_mapping_result().GetFlushWeightCurrent(); + if ((flush_waste_base != -1) && (flush_waste_current != -1) && flush_waste_current > flush_waste_base) { + float val = flush_waste_current - flush_waste_base; + const wxString& warning_msg = wxString::Format(_L("The current nozzle mapping may produce an extra %0.2f g of waste."), val); + show_status(PrintDialogStatus::PrintStatusRackNozzleMappingWarning, { warning_msg }); + } + + return true; + } + + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "empty"; + const wxString& err_msg = wxString::Format(_L("Failed to receive nozzle auto-mapping table from printer { msg: %s }. Please refresh the printer information."), "empty table"); + show_status(PrintDialogStatus::PrintStatusRackNozzleMappingError, { err_msg }); + return true; +} + //y24 std::string SelectMachineDialog::NormalizeVendor(const std::string& str) { @@ -5499,6 +5952,7 @@ std::string SelectMachineDialog::NormalizeVendor(const std::string& str) m_ops = ops; m_param = param; + m_full_title = title; SetBackgroundColour(StateColor::darkModeColorFor(*wxWHITE)); wxBoxSizer *sizer = new wxBoxSizer(wxHORIZONTAL); @@ -5506,6 +5960,8 @@ std::string SelectMachineDialog::NormalizeVendor(const std::string& str) m_printoption_title = new Label(this, title); m_printoption_title->SetFont(Label::Body_13); + update_title_display(); + m_printoption_tips = new ScalableButton(this, wxID_ANY, "icon_qusetion", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, true); m_printoption_tips->SetMinSize(wxSize(FromDIP(18), FromDIP(18))); m_printoption_tips->SetMaxSize(wxSize(FromDIP(18), FromDIP(18))); @@ -5610,6 +6066,18 @@ void PrintOption::update_tooltip(const wxString &tips) m_printoption_tips->Show(!m_printoption_tips->GetToolTipText().IsEmpty()); } +void PrintOption::update_tooltip_options_area(const wxString& opt_tips) +{ + if (m_printoption_item->GetToolTipText() != opt_tips) { + m_printoption_item->SetToolTip(opt_tips); + } +} + +void PrintOption::restoreValue() +{ + m_printoption_item->restoreValue(); +} + std::string PrintOption::getValue() { return m_printoption_item->getValue(); @@ -5628,10 +6096,32 @@ int PrintOption::getValueInt() } } +void PrintOption::update_title_display() +{ + if (!m_printoption_title || m_full_title.IsEmpty()) { + return; + } + + wxGCDC dc; + wxSize titleSize = dc.GetTextExtent(m_full_title); + int maxTitleWidth = FromDIP(150); + + wxString displayTitle = m_full_title; + if (titleSize.x > maxTitleWidth) { + displayTitle = wxControl::Ellipsize(m_full_title, dc, wxELLIPSIZE_END, maxTitleWidth); + m_printoption_title->SetToolTip(m_full_title); + } else { + m_printoption_title->SetToolTip(wxEmptyString); + } + + m_printoption_title->SetLabel(displayTitle); +} + void PrintOption::msw_rescale() { m_printoption_item->msw_rescale(); m_printoption_tips->msw_rescale(); + update_title_display(); } PrintOptionItem::PrintOptionItem(wxWindow* parent, std::vector ops, std::string param) @@ -5698,6 +6188,7 @@ void PrintOptionItem::on_left_down(wxMouseEvent& evt) auto select_size = GetSize().x / m_ops.size(); int i = 0; + m_old_selected_key = selected_key; for (const auto& entry : m_ops) { auto left_edge = rect.x + i * select_size; @@ -5729,10 +6220,12 @@ void PrintOptionItem::on_left_down(wxMouseEvent& evt) } } - wxCommandEvent event(EVT_SWITCH_PRINT_OPTION); - event.SetString(selected_key); - event.SetEventObject(GetParent()); - wxPostEvent(GetParent(), event); + if (m_old_selected_key != selected_key) { + wxCommandEvent event(EVT_SWITCH_PRINT_OPTION); + event.SetString(selected_key); + event.SetEventObject(GetParent()); + wxPostEvent(GetParent(), event); + } Refresh(); } @@ -6226,5 +6719,174 @@ std::string PrinterInfoBox::NormalizeVendor(const std::string& str) return normalized; } +NozzleStatePanel::NozzleStatePanel(wxWindow* parent) + : wxPanel(parent) +{ + m_sizer = new wxBoxSizer(wxHORIZONTAL); + SetSizer(m_sizer); +} + +static Label* s_create_hcontent(wxWindow* parent, + const wxString& ext_loc_str, + const NozzleDef& nozzle_info, + int count) +{ + wxString content_str; + content_str += ext_loc_str; + content_str += ": "; + content_str += wxString::Format("%0.1fmm", nozzle_info.nozzle_diameter); + content_str += " "; + content_str += DevNozzle::GetNozzleFlowTypeStr(nozzle_info.nozzle_flow_type); + content_str += " "; + content_str += wxString::Format("x%d", count); + + Label* ext_loc_label = new Label(parent, content_str); + ext_loc_label->SetBackgroundColour(*wxWHITE); + ext_loc_label->SetForegroundColour(WXCOLOUR_GREY700); + ext_loc_label->SetFont(Label::Body_12); + return ext_loc_label; +} + +void NozzleStatePanel::UpdateGui() +{ + m_sizer->Clear(true); + + // Slicing + wxSizer* slicing_vbox = new wxBoxSizer(wxVERTICAL); + Label* slicing_title = new Label(this, _L("Sliced file") + ":"); + slicing_title->SetBackgroundColour(*wxWHITE); + slicing_title->SetFont(Label::Body_12); + slicing_vbox->Add(slicing_title, 0, wxALIGN_LEFT | wxTOP | wxBOTTOM, FromDIP(3)); + + std::unordered_map m_slcing_nozzle_labels_l; + auto slicing_l_nozzles = m_slicing_nozzles[DEPUTY_EXTRUDER_ID]; + for (auto item : slicing_l_nozzles) { + slicing_vbox->AddSpacer(FromDIP(3)); + auto label = s_create_hcontent(this, _L("Left"), item.first, item.second); + slicing_vbox->Add(label, 0, wxALIGN_LEFT); + m_slcing_nozzle_labels_l[item.first] = label; + } + m_slicing_labels[DEPUTY_EXTRUDER_ID] = m_slcing_nozzle_labels_l; + + std::unordered_map m_installed_nozzle_labels_r; + auto slicing_r_nozzles = m_slicing_nozzles[MAIN_EXTRUDER_ID]; + for (auto item : slicing_r_nozzles) { + slicing_vbox->AddSpacer(FromDIP(3)); + auto label = s_create_hcontent(this, _L("Right"), item.first, item.second); + slicing_vbox->Add(label, 0, wxALIGN_LEFT); + m_installed_nozzle_labels_r[item.first] = label; + } + + m_slicing_labels[MAIN_EXTRUDER_ID] = m_installed_nozzle_labels_r; + UpdateLabelColour(); + + // Installed + wxSizer* installed_vbox = new wxBoxSizer(wxVERTICAL); + Label* installed_title = new Label(this, _L("Printer") + ":"); + installed_title->SetBackgroundColour(*wxWHITE); + installed_title->SetFont(Label::Body_12); + installed_vbox->Add(installed_title, 0, wxALIGN_LEFT | wxTOP | wxBOTTOM, FromDIP(3)); + + auto installed_l_nozzles = m_installed_nozzles[DEPUTY_EXTRUDER_ID]; + for (auto item : installed_l_nozzles) { + installed_vbox->AddSpacer(FromDIP(3)); + installed_vbox->Add(s_create_hcontent(this, _L("Left"), item.first, item.second), wxALIGN_LEFT); + } + + auto installed_r_nozzles = m_installed_nozzles[MAIN_EXTRUDER_ID]; + for (auto item : installed_r_nozzles) { + + slicing_vbox->AddSpacer(FromDIP(3)); + installed_vbox->Add(s_create_hcontent(this, _L("Right"), item.first, item.second), wxALIGN_LEFT); + } + + wxPanel* separator = new wxPanel(this); + separator->SetMaxSize(wxSize(FromDIP(1), -1)); + separator->SetMinSize(wxSize(FromDIP(1), -1)); + separator->SetBackgroundColour(WXCOLOUR_GREY300); + + m_sizer->Add(slicing_vbox); + m_sizer->Add(separator, 0, wxEXPAND | wxALL, FromDIP(8)); + m_sizer->Add(installed_vbox); + + wxGetApp().UpdateDarkUIWin(this); + Layout(); + Fit(); +} + +void NozzleStatePanel::UpdateInfoBy(Plater* plater, MachineObject* obj) +{ + if (plater && obj) { + auto nozzle_sys = obj->GetNozzleSystem(); + auto nozzle_group_res = DevUtilBackend::GetNozzleGroupResult(plater); + if (nozzle_group_res && nozzle_sys->GetNozzleRack()->IsSupported()) { + + ExtruderNozzleInfos slicing_nozzle_infos; + slicing_nozzle_infos[MAIN_EXTRUDER_ID] = DevUtilBackend::CollectNozzleInfo(&nozzle_group_res.value(), LOGIC_R_EXTRUDER_ID); + slicing_nozzle_infos[DEPUTY_EXTRUDER_ID] = DevUtilBackend::CollectNozzleInfo(&nozzle_group_res.value(), LOGIC_L_EXTRUDER_ID); + UpdateInfo(slicing_nozzle_infos, nozzle_sys->GetExtruderNozzleInfo()); + return; + } + } + + Show(false); +} + +void NozzleStatePanel::UpdateInfo(const ExtruderNozzleInfos& slicing_nozzle_infos, + const ExtruderNozzleInfos& machine_nozzle_infos) +{ + if (m_slicing_nozzles != slicing_nozzle_infos || m_installed_nozzles != machine_nozzle_infos) { + m_slicing_nozzles = slicing_nozzle_infos; + m_installed_nozzles = machine_nozzle_infos; + UpdateGui(); + } +}; + +void s_set_label(std::unordered_map> labels, + int ext_id, + const NozzleDef& nozzle_info, + const wxColour& font_clr) +{ + auto iter = labels.find(ext_id); + if (iter == labels.end()) { + assert(0 && __FUNCTION__); + return; + } + + auto iter2 = iter->second.find(nozzle_info); + if (iter2 == iter->second.end()) { + assert(0 && __FUNCTION__); + return; + } + + iter2->second->SetForegroundColour(font_clr); + iter2->second->Refresh(); +} + +void NozzleStatePanel::UpdateLabelColour() +{ + for (auto slicing_items : m_slicing_nozzles) { + auto installed_items = m_installed_nozzles[slicing_items.first]; + for (auto slicing_item : slicing_items.second) { + if (slicing_item.second < 1) { + continue; + } + + if (installed_items[slicing_item.first] == 0) { + s_set_label(m_slicing_labels, slicing_items.first, slicing_item.first, wxColour("#D01B1B")); + continue; + } + + if (slicing_item.second > installed_items[slicing_item.first]) { + s_set_label(m_slicing_labels, slicing_items.first, slicing_item.first, wxColour("#FF6F00")); + continue; + } + + s_set_label(m_slicing_labels, slicing_items.first, slicing_item.first, WXCOLOUR_GREY700); + } + } +} +// end of class NozzleStatePanel + } } // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/SelectMachine.hpp b/src/slic3r/GUI/SelectMachine.hpp index 51190ee..f805951 100644 --- a/src/slic3r/GUI/SelectMachine.hpp +++ b/src/slic3r/GUI/SelectMachine.hpp @@ -164,6 +164,7 @@ public: public: void setValue(std::string value); std::string getValue() const { return selected_key; } + void restoreValue() { setValue(m_old_selected_key); } // see EVT_SWITCH_PRINT_OPTION void update_options(std::vector ops) { if (m_ops != ops) { @@ -202,6 +203,7 @@ private: ScalableBitmap m_selected_disabled_bk_dark; std::vector m_ops; std::string selected_key; + std::string m_old_selected_key;// for restoreValue on EVT_SWITCH_PRINT_OPTION std::string m_param; bool m_enable = true; @@ -215,6 +217,7 @@ private: Label *m_printoption_title{nullptr}; ScalableButton *m_printoption_tips{ nullptr }; PrintOptionItem *m_printoption_item{nullptr}; + wxString m_full_title; public: PrintOption(wxWindow *parent, wxString title, wxString tips, std::vector ops, std::string param = ""); @@ -224,6 +227,7 @@ public: void enable(bool en); void setValue(std::string value); + void restoreValue(); std::string getValue(); int getValueInt(); @@ -231,7 +235,9 @@ public: bool contain_opt(const std::string& opt_str) const; void update_options(std::vector ops, const wxString &tips); - void update_tooltip(const wxString &tips); + void update_tooltip(const wxString &tips);// icon tips + void update_title_display(); + void update_tooltip_options_area(const wxString& opt_tips);// options area tips void msw_rescale(); @@ -328,6 +334,7 @@ private: std::vector m_list; std::vector m_filaments; std::vector m_ams_mapping_result; + std::unordered_map m_nozzle_mapping_result; std::vector m_filaments_map; std::shared_ptr m_status_bar; @@ -369,7 +376,10 @@ protected: wxPanel * m_options_other {nullptr}; wxGridSizer* m_sizer_options{nullptr}; wxBoxSizer* m_sizer_thumbnail{ nullptr }; - + wxPanel* m_pa_value_panel{nullptr}; + Label* m_pa_value_message{nullptr}; + SwitchButton* m_pa_value_switch{nullptr}; + ScalableButton* m_pa_value_tips{nullptr}; wxBoxSizer* m_basicl_sizer{ nullptr }; wxBoxSizer* rename_sizer_v{ nullptr }; wxBoxSizer* rename_sizer_h{ nullptr }; @@ -479,14 +489,22 @@ public: void finish_mode(); void sync_ams_mapping_result(std::vector& result); void prepare(int print_plate_idx); - void show_status(PrintDialogStatus status, std::vector params = std::vector(), wxString wiki_url = wxEmptyString); + void show_status(PrintDialogStatus status, + std::vector params = std::vector(), + wxString wiki_url = wxEmptyString, + prePrintInfoStyle style = prePrintInfoStyle::Default); void sys_color_changed(); void reset_timeout(); void update_user_printer(); void reset_ams_material(); void update_show_status(MachineObject* obj_ = nullptr); + bool CheckErrorRackStatus(MachineObject* obj_);//return true if no errors + bool CheckErrorExtruderNozzleWithSlicing(MachineObject* obj_);//return true if no errors + bool CheckErrorSyncNozzleMappingResult(MachineObject* obj);// return true if no errors + void UpdateStatusCheckWarning_ExtensionTool(MachineObject* obj_); + void CheckWarningRackStatus(MachineObject* obj_); void update_ams_check(MachineObject* obj); void update_filament_change_count(); @@ -528,7 +546,7 @@ public: bool can_support_pa_auto_cali(); bool is_same_printer_model(); bool is_blocking_printing(MachineObject* obj_); - bool is_nozzle_hrc_matched(const DevExtder* extruder, std::string& filament_type) const; + bool is_nozzle_hrc_matched(const NozzleType& nozzle_type, std::string& filament_type) const; bool check_sdcard_for_timelpase(MachineObject* obj); bool is_timeout(); int update_print_required_data(Slic3r::DynamicPrintConfig config, Slic3r::Model model, Slic3r::PlateDataPtrs plate_data_list, std::string file_name, std::string file_path); @@ -540,13 +558,15 @@ public: bool build_nozzles_info(std::string& nozzles_info); bool can_hybrid_mapping(DevExtderSystem data); void auto_supply_with_ext(std::vector slots); - bool is_nozzle_type_match(DevExtderSystem data, wxString& error_message) const; int convert_filament_map_nozzle_id_to_task_nozzle_id(int nozzle_id); PrintFromType get_print_type() {return m_print_type;}; wxString format_steel_name(NozzleType type); PrintDialogStatus get_status() { return m_print_status; } + Plater* get_plater() const { return m_plater; } + MachineObject* get_current_machine() const; + // y16 std::string NormalizeVendor(const std::string& str); @@ -558,6 +578,9 @@ public: private: void EnableEditing(bool enable); + /* update ams backup*/ + void update_ams_backup(MachineObject* obj_); + /* update scroll area size*/ void update_scroll_area_size(); @@ -570,6 +593,14 @@ private: void save_option_vals(); void save_option_vals(MachineObject *obj); + // events + void on_flow_pa_caliation_option_changed(wxCommandEvent& event); + void on_nozzle_offset_option_changed(wxCommandEvent& event); + void on_pa_value_switch_changed(wxCommandEvent &event); + + // get mapping nozzle display string + wxString get_mapped_nozzle_str(int fila_id); + // enbale or disable external change assist bool is_enable_external_change_assist(std::vector& ams_mapping_result); }; @@ -619,6 +650,30 @@ private: std::set qidi_printers; }; +class NozzleStatePanel : public wxPanel +{ +public: + NozzleStatePanel(wxWindow* parent); + +public: + void UpdateInfoBy(Plater* plater, MachineObject* obj); + void Rescale() {}; + +private: + void UpdateInfo(const ExtruderNozzleInfos& slicing_nozzle_infos, + const ExtruderNozzleInfos& machine_nozzle_infos); + void UpdateGui(); + void UpdateLabelColour(); + +private: + ExtruderNozzleInfos m_slicing_nozzles; + ExtruderNozzleInfos m_installed_nozzles; + + wxSizer* m_sizer; + + // key(extruder_id) -> { key1(nozzle type info), val1(label)} + std::unordered_map> m_slicing_labels; +}; wxDECLARE_EVENT(EVT_SWITCH_PRINT_OPTION, wxCommandEvent); diff --git a/src/slic3r/GUI/SendMultiMachinePage.cpp b/src/slic3r/GUI/SendMultiMachinePage.cpp index 3b31b4d..d410df9 100644 --- a/src/slic3r/GUI/SendMultiMachinePage.cpp +++ b/src/slic3r/GUI/SendMultiMachinePage.cpp @@ -1549,7 +1549,7 @@ void SendMultiMachinePage::sync_ams_list() auto colour_rgb = wxColour((int)rgb[0], (int)rgb[1], (int)rgb[2], (int)rgb[3]); if (extruder >= materials.size() || extruder < 0 || extruder >= display_materials.size()) continue; - MaterialItem* item = new MaterialItem(m_main_page, colour_rgb, _L(display_materials[extruder])); + MaterialItem* item = new MaterialItem(m_main_page, colour_rgb, _L(display_materials[extruder]), m_filaments_id[extruder]); //item->set_ams_info(wxColour("#CECECE"), "A1", 0, std::vector()); item->set_ams_info(wxColour("#CECECE"), "Ext", 0, std::vector()); m_ams_list_sizer->Add(item, 0, wxALL, FromDIP(4)); diff --git a/src/slic3r/GUI/SendToPrinter.cpp b/src/slic3r/GUI/SendToPrinter.cpp index c77ad3d..f2067bd 100644 --- a/src/slic3r/GUI/SendToPrinter.cpp +++ b/src/slic3r/GUI/SendToPrinter.cpp @@ -1557,7 +1557,7 @@ void SendToPrinterDialog::on_selection_changed(wxCommandEvent &event) // } // } -// if (obj && !obj->get_lan_mode_connection_state()) { +// if (obj) { // obj->command_get_version(); // obj->command_request_push_all(); // if (!dev->get_selected_machine()) { @@ -2447,7 +2447,8 @@ void SendToPrinterDialog::UploadFileRessultCallback(int res, int resp_ec, std::s if (ParseErrorCode(resp_ec) != "") update_print_status_msg(ParseErrorCode(resp_ec), false, true); else - update_print_status_msg("Sending failed, please try again!", false, true); + update_print_status_msg(_L("Sending failed, please try again!"), false, true); + m_filetransfer_uploadfile_job.reset(); m_filetransfer_uploadfile_job = nullptr; } diff --git a/src/slic3r/GUI/StatusPanel.cpp b/src/slic3r/GUI/StatusPanel.cpp index 33db81b..51cd2db 100644 --- a/src/slic3r/GUI/StatusPanel.cpp +++ b/src/slic3r/GUI/StatusPanel.cpp @@ -23,23 +23,28 @@ #include #include +#include "DeviceCore/DevAxis.h" #include "DeviceCore/DevBed.h" +#include "DeviceCore/DevChamber.h" #include "DeviceCore/DevCtrl.h" #include "DeviceCore/DevFan.h" #include "DeviceCore/DevFilaSystem.h" #include "DeviceCore/DevLamp.h" +#include "DeviceCore/DevNozzleSystem.h" #include "DeviceCore/DevStorage.h" #include "DeviceCore/DevConfig.h" +#include "DeviceCore/DevInfo.h" #include "DeviceCore/DevManager.h" #include "DeviceCore/DevPrintTaskInfo.h" +#include "DeviceTab/wgtDeviceNozzleRack.h" + #include "PrintOptionsDialog.hpp" #include "SafetyOptionsDialog.hpp" - #include "ThermalPreconditioningDialog.hpp" namespace Slic3r { namespace GUI { @@ -53,7 +58,7 @@ static const wxString TEMP_BLANK_STR = wxString("_"); static const wxFont SWITCH_FONT = Label::Body_10; /* const values */ -static const int bed_temp_range[2] = {20, 120}; +static const int bed_temp_range[2] = {20, 120}; static const int default_champer_temp_min = 20; static const int default_champer_temp_max = 60; @@ -68,16 +73,16 @@ static const wxColour BUTTON_PRESS_COL = wxColour(172, 172, 172); static const wxColour BUTTON_HOVER_COL = wxColour(68, 121, 251); // y96 static const wxColour DISCONNECT_TEXT_COL = wxColour(171, 172, 172); -static const wxColour NORMAL_TEXT_COL = wxColour(48,58,60); +static const wxColour NORMAL_TEXT_COL = wxColour(48, 58, 60); static const wxColour NORMAL_FAN_TEXT_COL = wxColour(107, 107, 107); static const wxColour WARNING_INFO_BG_COL = wxColour(255, 111, 0); -static const wxColour STAGE_TEXT_COL = wxColour(146, 146, 146); +static const wxColour STAGE_TEXT_COL = wxColour(107, 107, 107); static const wxColour GROUP_STATIC_LINE_COL = wxColour(206, 206, 206); /* font and foreground colors */ -static const wxFont PAGE_TITLE_FONT = Label::Body_14; -//static const wxFont GROUP_TITLE_FONT = Label::sysFont(17); +static const wxFont PAGE_TITLE_FONT = Label::Body_14; +// static const wxFont GROUP_TITLE_FONT = Label::sysFont(17); static wxColour PAGE_TITLE_FONT_COL = wxColour(107, 107, 107); static wxColour GROUP_TITLE_FONT_COL = wxColour(172, 172, 172); @@ -85,7 +90,6 @@ static wxColour TEXT_LIGHT_FONT_COL = wxColour(107, 107, 107); static wxImage fail_image; - /* size */ #define PAGE_TITLE_HEIGHT FromDIP(36) #define PAGE_TITLE_TEXT_WIDTH FromDIP(200) @@ -139,7 +143,7 @@ static void market_model_scoring_page(int design_id) Description:Extruder **************************************************/ -ExtruderImage::ExtruderImage(wxWindow* parent, wxWindowID id, int nozzle_num, const wxPoint& pos, const wxSize& size) +ExtruderImage::ExtruderImage(wxWindow *parent, wxWindowID id, int nozzle_num, const wxPoint &pos, const wxSize &size) { wxWindow::Create(parent, id, pos, wxSize(FromDIP(45), FromDIP(112))); SetBackgroundColour(*wxWHITE); @@ -148,27 +152,27 @@ ExtruderImage::ExtruderImage(wxWindow* parent, wxWindowID id, int nozzle_num, co SetMinSize(wxSize(FromDIP(45), FromDIP(112))); SetMaxSize(wxSize(FromDIP(45), FromDIP(112))); - m_pipe_filled_load = new ScalableBitmap(this, "pipe_of_loading_selected", 50); - m_pipe_filled_unload = new ScalableBitmap(this, "pipe_of_unloading_selected", 50); - m_pipe_empty_load = new ScalableBitmap(this, "pipe_of_empty", 50); - m_pipe_empty_unload = new ScalableBitmap(this, "pipe_of_empty", 50); - m_pipe_filled_load_unselected = new ScalableBitmap(this, "pipe_of_loading_unselected", 50); + m_pipe_filled_load = new ScalableBitmap(this, "pipe_of_loading_selected", 50); + m_pipe_filled_unload = new ScalableBitmap(this, "pipe_of_unloading_selected", 50); + m_pipe_empty_load = new ScalableBitmap(this, "pipe_of_empty", 50); + m_pipe_empty_unload = new ScalableBitmap(this, "pipe_of_empty", 50); + m_pipe_filled_load_unselected = new ScalableBitmap(this, "pipe_of_loading_unselected", 50); m_pipe_filled_unload_unselected = new ScalableBitmap(this, "pipe_of_unloading_unselected", 50); - m_pipe_empty_load_unselected = new ScalableBitmap(this, "pipe_of_empty", 50); - m_pipe_empty_unload_unselected = new ScalableBitmap(this, "pipe_of_empty", 50); + m_pipe_empty_load_unselected = new ScalableBitmap(this, "pipe_of_empty", 50); + m_pipe_empty_unload_unselected = new ScalableBitmap(this, "pipe_of_empty", 50); - m_left_extruder_active_filled = new ScalableBitmap(this, "left_extruder_active_filled", 62); - m_left_extruder_active_empty = new ScalableBitmap(this, "left_extruder_active_empty", 62); - m_left_extruder_unactive_filled = new ScalableBitmap(this, "left_extruder_unactive_filled", 62); - m_left_extruder_unactive_empty = new ScalableBitmap(this, "left_extruder_unactive_empty", 62); - m_right_extruder_active_filled = new ScalableBitmap(this, "right_extruder_active_filled", 62); - m_right_extruder_active_empty = new ScalableBitmap(this, "right_extruder_active_empty", 62); + m_left_extruder_active_filled = new ScalableBitmap(this, "left_extruder_active_filled", 62); + m_left_extruder_active_empty = new ScalableBitmap(this, "left_extruder_active_empty", 62); + m_left_extruder_unactive_filled = new ScalableBitmap(this, "left_extruder_unactive_filled", 62); + m_left_extruder_unactive_empty = new ScalableBitmap(this, "left_extruder_unactive_empty", 62); + m_right_extruder_active_filled = new ScalableBitmap(this, "right_extruder_active_filled", 62); + m_right_extruder_active_empty = new ScalableBitmap(this, "right_extruder_active_empty", 62); m_right_extruder_unactive_filled = new ScalableBitmap(this, "right_extruder_unactive_filled", 62); - m_right_extruder_unactive_empty = new ScalableBitmap(this, "right_extruder_unactive_empty", 62); + m_right_extruder_unactive_empty = new ScalableBitmap(this, "right_extruder_unactive_empty", 62); - m_extruder_single_nozzle_empty_load = new ScalableBitmap(this, "monitor_extruder_empty_load", 106); - m_extruder_single_nozzle_empty_unload = new ScalableBitmap(this, "monitor_extruder_empty_unload", 106); - m_extruder_single_nozzle_filled_load = new ScalableBitmap(this, "monitor_extruder_filled_load", 106); + m_extruder_single_nozzle_empty_load = new ScalableBitmap(this, "monitor_extruder_empty_load", 106); + m_extruder_single_nozzle_empty_unload = new ScalableBitmap(this, "monitor_extruder_empty_unload", 106); + m_extruder_single_nozzle_filled_load = new ScalableBitmap(this, "monitor_extruder_filled_load", 106); m_extruder_single_nozzle_filled_unload = new ScalableBitmap(this, "monitor_extruder_filled_unload", 106); Bind(wxEVT_PAINT, &ExtruderImage::paintEvent, this); @@ -178,9 +182,9 @@ ExtruderImage::~ExtruderImage() {} void ExtruderImage::msw_rescale() { - //m_ams_extruder.SetSize(AMS_EXTRUDER_BITMAP_SIZE); - //auto image = m_ams_extruder.ConvertToImage(); - //m_extruder_pipe = ScalableBitmap(this, "pipe_of_extruder_control", 50); + // m_ams_extruder.SetSize(AMS_EXTRUDER_BITMAP_SIZE); + // auto image = m_ams_extruder.ConvertToImage(); + // m_extruder_pipe = ScalableBitmap(this, "pipe_of_extruder_control", 50); m_pipe_filled_load->msw_rescale(); m_pipe_filled_unload->msw_rescale(); @@ -208,47 +212,39 @@ void ExtruderImage::msw_rescale() Refresh(); } -void ExtruderImage::setExtruderCount(int nozzle_num) -{ - m_nozzle_num = nozzle_num; -} +void ExtruderImage::setExtruderCount(int nozzle_num) { m_nozzle_num = nozzle_num; } void ExtruderImage::setExtruderUsed(std::string loc) { - //current_nozzle_idx = nozzle_id; - if (current_nozzle_loc == loc) - { - return; - } + // current_nozzle_idx = nozzle_id; + if (current_nozzle_loc == loc) { return; } current_nozzle_loc = loc; Refresh(); } -void ExtruderImage::update(ExtruderState single_state) -{ - m_single_ext_state = single_state; -} +void ExtruderImage::update(ExtruderState single_state) { m_single_ext_state = single_state; } -void ExtruderImage::update(ExtruderState right_state, ExtruderState left_state) { - m_left_ext_state = left_state; +void ExtruderImage::update(ExtruderState right_state, ExtruderState left_state) +{ + m_left_ext_state = left_state; m_right_ext_state = right_state; } -void ExtruderImage::paintEvent(wxPaintEvent& evt) +void ExtruderImage::paintEvent(wxPaintEvent &evt) { wxPaintDC dc(this); render(dc); } -void ExtruderImage::render(wxDC& dc) +void ExtruderImage::render(wxDC &dc) { #ifdef __WXMSW__ wxSize size = GetSize(); wxMemoryDC memdc; wxBitmap bmp(size.x, size.y); memdc.SelectObject(bmp); - memdc.Blit({ 0, 0 }, size, &dc, { 0, 0 }); + memdc.Blit({0, 0}, size, &dc, {0, 0}); { wxGCDC dc2(memdc); @@ -262,21 +258,20 @@ void ExtruderImage::render(wxDC& dc) #endif } -void ExtruderImage::doRender(wxDC& dc) +void ExtruderImage::doRender(wxDC &dc) { auto size = GetSize(); - //dc.DrawRectangle(0, FromDIP(5), size.x, size.y - FromDIP(5) - FromDIP(2)); + // dc.DrawRectangle(0, FromDIP(5), size.x, size.y - FromDIP(5) - FromDIP(2)); auto pot = wxPoint(size.x / 2, (size.y - m_pipe_filled_load->GetBmpSize().y - m_left_extruder_active_filled->GetBmpSize().y) / 2); - if (m_nozzle_num >= 2){ - ScalableBitmap* left_nozzle_bmp; - ScalableBitmap* right_nozzle_bmp; - ScalableBitmap* left_pipe_bmp; - ScalableBitmap* right_pipe_bmp; + if (m_nozzle_num >= 2) { + ScalableBitmap *left_nozzle_bmp; + ScalableBitmap *right_nozzle_bmp; + ScalableBitmap *left_pipe_bmp; + ScalableBitmap *right_pipe_bmp; - switch (m_right_ext_state) - { + switch (m_right_ext_state) { case Slic3r::GUI::FILLED_LOAD: right_pipe_bmp = current_nozzle_loc == "right" ? m_pipe_filled_load : m_pipe_filled_load_unselected; right_nozzle_bmp = current_nozzle_loc == "right" ? m_right_extruder_active_filled : m_right_extruder_unactive_filled; @@ -293,12 +288,10 @@ void ExtruderImage::doRender(wxDC& dc) right_pipe_bmp = current_nozzle_loc == "right" ? m_pipe_empty_unload : m_pipe_empty_unload_unselected; right_nozzle_bmp = current_nozzle_loc == "right" ? m_right_extruder_active_empty : m_right_extruder_unactive_empty; break; - default: - break; + default: break; } - switch (m_left_ext_state) - { + switch (m_left_ext_state) { case Slic3r::GUI::FILLED_LOAD: left_pipe_bmp = current_nozzle_loc == "left" ? m_pipe_filled_load : m_pipe_filled_load_unselected; left_nozzle_bmp = current_nozzle_loc == "left" ? m_left_extruder_active_filled : m_left_extruder_unactive_filled; @@ -315,46 +308,36 @@ void ExtruderImage::doRender(wxDC& dc) left_pipe_bmp = current_nozzle_loc == "left" ? m_pipe_empty_unload : m_pipe_empty_unload_unselected; left_nozzle_bmp = current_nozzle_loc == "left" ? m_left_extruder_active_empty : m_left_extruder_unactive_empty; break; - default: - break; + default: break; } - left_pipe_bmp = m_pipe_filled_load; + left_pipe_bmp = m_pipe_filled_load; right_pipe_bmp = m_pipe_filled_load; dc.DrawBitmap(left_pipe_bmp->bmp(), pot.x - left_nozzle_bmp->GetBmpWidth() / 2 - left_pipe_bmp->GetBmpWidth() / 2 + left_pipe_bmp->GetBmpWidth() / 5, pot.y); dc.DrawBitmap(left_nozzle_bmp->bmp(), pot.x - left_nozzle_bmp->GetBmpWidth(), pot.y + left_pipe_bmp->GetBmpSize().y); dc.DrawBitmap(right_pipe_bmp->bmp(), pot.x + right_nozzle_bmp->GetBmpWidth() / 2 - right_pipe_bmp->GetBmpWidth() / 2 - right_pipe_bmp->GetBmpWidth() / 5, pot.y); dc.DrawBitmap(right_nozzle_bmp->bmp(), pot.x, pot.y + right_pipe_bmp->GetBmpSize().y); - } - else{ - - ScalableBitmap* nozzle_bmp = nullptr; - switch (m_single_ext_state) - { - case Slic3r::GUI::FILLED_LOAD: nozzle_bmp = m_extruder_single_nozzle_filled_load; break; - case Slic3r::GUI::FILLED_UNLOAD: nozzle_bmp = m_extruder_single_nozzle_filled_unload; break; - case Slic3r::GUI::EMPTY_LOAD: nozzle_bmp = m_extruder_single_nozzle_empty_load; break; - case Slic3r::GUI::EMPTY_UNLOAD: nozzle_bmp = m_extruder_single_nozzle_empty_unload; break; - default: break; + } else { + ScalableBitmap *nozzle_bmp = nullptr; + switch (m_single_ext_state) { + case Slic3r::GUI::FILLED_LOAD: nozzle_bmp = m_extruder_single_nozzle_filled_load; break; + case Slic3r::GUI::FILLED_UNLOAD: nozzle_bmp = m_extruder_single_nozzle_filled_unload; break; + case Slic3r::GUI::EMPTY_LOAD: nozzle_bmp = m_extruder_single_nozzle_empty_load; break; + case Slic3r::GUI::EMPTY_UNLOAD: nozzle_bmp = m_extruder_single_nozzle_empty_unload; break; + default: break; } - if (nozzle_bmp) - { - dc.DrawBitmap(nozzle_bmp->bmp(), pot.x - nozzle_bmp->GetBmpWidth() / 2, (size.y - nozzle_bmp->GetBmpHeight()) / 2); - } + if (nozzle_bmp) { dc.DrawBitmap(nozzle_bmp->bmp(), pot.x - nozzle_bmp->GetBmpWidth() / 2, (size.y - nozzle_bmp->GetBmpHeight()) / 2); } } } #define SWITCHING_STATUS_BTN_SIZE wxSize(FromDIP(25), FromDIP(26)) -ExtruderSwithingStatus::ExtruderSwithingStatus(wxWindow *parent) - : wxPanel(parent) +ExtruderSwithingStatus::ExtruderSwithingStatus(wxWindow *parent) : wxPanel(parent) { m_switching_status_label = new Label(this); m_switching_status_label->SetFont(::Label::Body_13); - if (parent) - { m_switching_status_label->SetBackgroundColour(parent->GetBackgroundColour()); - } + if (parent) { m_switching_status_label->SetBackgroundColour(parent->GetBackgroundColour()); } StateColor e_ctrl_bg(std::pair(BUTTON_PRESS_COL, StateColor::Pressed), std::pair(BUTTON_NORMAL1_COL, StateColor::Normal)); StateColor e_ctrl_bd(std::pair(BUTTON_HOVER_COL, StateColor::Hovered), std::pair(BUTTON_NORMAL1_COL, StateColor::Normal)); @@ -379,7 +362,7 @@ ExtruderSwithingStatus::ExtruderSwithingStatus(wxWindow *parent) m_button_retry->SetBorderWidth(2); if (parent) { m_button_retry->SetBackgroundColour(parent->GetBackgroundColour()); } - wxBoxSizer *btn_sizer = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer *btn_sizer = new wxBoxSizer(wxHORIZONTAL); btn_sizer->Add(m_button_quit, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, FromDIP(10)); btn_sizer->Add(m_button_retry, 0, wxALIGN_CENTER_VERTICAL, 0); @@ -394,65 +377,49 @@ ExtruderSwithingStatus::ExtruderSwithingStatus(wxWindow *parent) void ExtruderSwithingStatus::updateBy(MachineObject *obj) { m_obj = obj; - if (!m_obj) - { + if (!m_obj) { Show(false); - } - else - { + } else { /*do not display while command sended in a mean while*/ - if ((time(nullptr) - m_last_ctrl_time) > HOLD_TIME_6SEC) - { - updateBy(obj->GetExtderSystem()); - } + if ((time(nullptr) - m_last_ctrl_time) > HOLD_TIME_6SEC) { updateBy(obj->GetExtderSystem()); } } } -void ExtruderSwithingStatus::updateBy(const DevExtderSystem* ext_system) +void ExtruderSwithingStatus::updateBy(const DevExtderSystem *ext_system) { Show(ext_system->GetTotalExtderCount() > 1); if (!IsShown()) { return; } auto state = ext_system->GetSwitchState(); { - if (state == DevExtderSwitchState::ES_SWITCHING) - { + if (state == DevExtderSwitchState::ES_SWITCHING) { m_switching_status_label->SetLabel(_L("Switching...")); m_switching_status_label->SetForegroundColour(StateColor::darkModeColorFor("#262E30")); m_switching_status_label->Show(true); - } - else if (state == DevExtderSwitchState::ES_SWITCHING_FAILED) - { + } else if (state == DevExtderSwitchState::ES_SWITCHING_FAILED) { m_switching_status_label->SetLabel(_L("Switching failed")); m_switching_status_label->SetForegroundColour(StateColor::darkModeColorFor(*wxRED)); m_switching_status_label->Show(true); - } - else - { + } else { m_switching_status_label->Show(false); } } - if (state != DevExtderSwitchState::ES_SWITCHING_FAILED) - { + if (state != DevExtderSwitchState::ES_SWITCHING_FAILED) { showQuitBtn(false); showRetryBtn(false); return; } /*can not quit if it's printing*/ - if (m_obj && !m_obj->is_in_printing() && !m_obj->is_in_printing_pause()) - { - showQuitBtn(true); - } + if (m_obj && !m_obj->is_in_printing() && !m_obj->is_in_printing_pause()) { showQuitBtn(true); } showRetryBtn(true); } void ExtruderSwithingStatus::showQuitBtn(bool show) { - if (m_button_quit->IsShown() != show) - { + if (m_button_quit->IsShown() != show) { m_button_quit->Show(show); Layout(); } @@ -487,8 +454,7 @@ void ExtruderSwithingStatus::on_quit(wxCommandEvent &event) { Show(false); - if (m_obj) - { + if (m_obj) { m_obj->command_ams_control("abort"); m_last_ctrl_time = time(nullptr); } @@ -498,17 +464,15 @@ void ExtruderSwithingStatus::on_retry(wxCommandEvent &event) { Show(false); - if (m_obj) - { + if (m_obj) { m_obj->command_ams_control("resume"); m_last_ctrl_time = time(nullptr); } } -PrintingTaskPanel::PrintingTaskPanel(wxWindow* parent, PrintingTaskType type) - : wxPanel(parent, wxID_ANY,wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL) +PrintingTaskPanel::PrintingTaskPanel(wxWindow *parent, PrintingTaskType type) : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL) { - m_type = type; + m_type = type; m_question_button = nullptr; create_panel(this); SetBackgroundColour(*wxWHITE); @@ -525,7 +489,7 @@ PrintingTaskPanel::~PrintingTaskPanel() } } -void PrintingTaskPanel::create_panel(wxWindow* parent) +void PrintingTaskPanel::create_panel(wxWindow *parent) { wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL); wxBoxSizer *bSizer_printing_title = new wxBoxSizer(wxHORIZONTAL); @@ -533,9 +497,9 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) m_panel_printing_title = new wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(-1, PAGE_TITLE_HEIGHT), wxTAB_TRAVERSAL); m_panel_printing_title->SetBackgroundColour(STATUS_TITLE_BG); - m_staticText_printing = new wxStaticText(m_panel_printing_title, wxID_ANY ,_L("Printing Progress")); + m_staticText_printing = new wxStaticText(m_panel_printing_title, wxID_ANY, _L("Printing Progress")); m_staticText_printing->Wrap(-1); - //m_staticText_printing->SetFont(PAGE_TITLE_FONT); + // m_staticText_printing->SetFont(PAGE_TITLE_FONT); m_staticText_printing->SetForegroundColour(PAGE_TITLE_FONT_COL); bSizer_printing_title->Add(m_staticText_printing, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, PAGE_TITLE_LEFT_MARGIN); @@ -549,19 +513,19 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) m_bitmap_thumbnail->SetMaxSize(TASK_THUMBNAIL_SIZE); m_bitmap_thumbnail->SetMinSize(TASK_THUMBNAIL_SIZE); - wxBoxSizer *bSizer_subtask_info = new wxBoxSizer(wxVERTICAL); - wxBoxSizer *bSizer_task_name = new wxBoxSizer(wxVERTICAL); + wxBoxSizer *bSizer_subtask_info = new wxBoxSizer(wxVERTICAL); + wxBoxSizer *bSizer_task_name = new wxBoxSizer(wxVERTICAL); wxBoxSizer *bSizer_task_name_hor = new wxBoxSizer(wxHORIZONTAL); - wxPanel* task_name_panel = new wxPanel(parent); + wxPanel *task_name_panel = new wxPanel(parent); m_staticText_subtask_value = new wxStaticText(task_name_panel, wxID_ANY, _L("N/A"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT | wxST_ELLIPSIZE_END); m_staticText_subtask_value->SetMaxSize(wxSize(FromDIP(600), -1)); m_staticText_subtask_value->Wrap(-1); - #ifdef __WXOSX_MAC__ +#ifdef __WXOSX_MAC__ m_staticText_subtask_value->SetFont(::Label::Body_13); - #else +#else m_staticText_subtask_value->SetFont(wxFont(13, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxT("HarmonyOS Sans SC"))); - #endif +#endif m_staticText_subtask_value->SetForegroundColour(wxColour(44, 44, 46)); m_bitmap_static_use_time = new wxStaticBitmap(task_name_panel, wxID_ANY, m_bitmap_use_time.bmp(), wxDefaultPosition, wxSize(FromDIP(16), FromDIP(16))); @@ -571,10 +535,8 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) m_staticText_consumption_of_time->SetForegroundColour(wxColour(0x68, 0x68, 0x68)); m_staticText_consumption_of_time->Wrap(-1); - m_bitmap_static_use_weight = new wxStaticBitmap(task_name_panel, wxID_ANY, m_bitmap_use_weight.bmp(), wxDefaultPosition, wxSize(FromDIP(16), FromDIP(16))); - m_staticText_consumption_of_weight = new wxStaticText(task_name_panel, wxID_ANY, "0g", wxDefaultPosition, wxDefaultSize, 0); m_staticText_consumption_of_weight->SetFont(::Label::Body_12); m_staticText_consumption_of_weight->SetForegroundColour(wxColour(0x68, 0x68, 0x68)); @@ -582,13 +544,12 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) bSizer_task_name_hor->Add(m_staticText_subtask_value, 1, wxALL | wxEXPAND, 0); bSizer_task_name_hor->Add(m_bitmap_static_use_time, 0, wxALIGN_CENTER_VERTICAL, 0); - bSizer_task_name_hor->Add(m_staticText_consumption_of_time, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, FromDIP(3)); + bSizer_task_name_hor->Add(m_staticText_consumption_of_time, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(3)); bSizer_task_name_hor->Add(0, 0, 0, wxLEFT, FromDIP(10)); bSizer_task_name_hor->Add(m_bitmap_static_use_weight, 0, wxALIGN_CENTER_VERTICAL, 0); bSizer_task_name_hor->Add(m_staticText_consumption_of_weight, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(3)); bSizer_task_name_hor->Add(0, 0, 0, wxRIGHT, FromDIP(10)); - task_name_panel->SetSizer(bSizer_task_name_hor); task_name_panel->Layout(); task_name_panel->Fit(); @@ -617,8 +578,8 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) bSizer_task_btn->Add(FromDIP(10), 0, 0); StateColor white_bg(std::pair(wxColour(255, 255, 255), StateColor::Disabled), std::pair(wxColour(255, 255, 255), StateColor::Pressed), - std::pair(wxColour(255, 255, 255), StateColor::Hovered), std::pair(wxColour(255, 255, 255), StateColor::Enabled), - std::pair(wxColour(255, 255, 255), StateColor::Normal)); + std::pair(wxColour(255, 255, 255), StateColor::Hovered), std::pair(wxColour(255, 255, 255), StateColor::Enabled), + std::pair(wxColour(255, 255, 255), StateColor::Normal)); m_button_partskip = new Button(progress_lr_panel, wxEmptyString, "print_control_partskip_disable", 0, 20, wxID_ANY); m_button_partskip->Enable(false); @@ -632,45 +593,34 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) m_button_partskip->Bind(wxEVT_ENTER_WINDOW, [this](auto &e) { m_button_partskip->SetIcon("print_control_partskip_hover"); }); m_button_partskip->Bind(wxEVT_LEAVE_WINDOW, [this](auto &e) { m_button_partskip->SetIcon("print_control_partskip"); }); - m_button_pause_resume = new ScalableButton(progress_lr_panel, wxID_ANY, "print_control_pause", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER,true); + m_button_pause_resume = new ScalableButton(progress_lr_panel, wxID_ANY, "print_control_pause", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, + true); m_button_pause_resume->Bind(wxEVT_ENTER_WINDOW, [this](auto &e) { - if (m_button_pause_resume->GetToolTipText() == _L("Pause")) { - m_button_pause_resume->SetBitmap_("print_control_pause_hover"); - } + if (m_button_pause_resume->GetToolTipText() == _L("Pause")) { m_button_pause_resume->SetBitmap_("print_control_pause_hover"); } - if (m_button_pause_resume->GetToolTipText() == _L("Resume")) { - m_button_pause_resume->SetBitmap_("print_control_resume_hover"); - } + if (m_button_pause_resume->GetToolTipText() == _L("Resume")) { m_button_pause_resume->SetBitmap_("print_control_resume_hover"); } }); m_button_pause_resume->Bind(wxEVT_LEAVE_WINDOW, [this](auto &e) { - auto buf = m_button_pause_resume->GetClientData(); - if (m_button_pause_resume->GetToolTipText() == _L("Pause")) { - m_button_pause_resume->SetBitmap_("print_control_pause"); - } + auto buf = m_button_pause_resume->GetClientData(); + if (m_button_pause_resume->GetToolTipText() == _L("Pause")) { m_button_pause_resume->SetBitmap_("print_control_pause"); } - if (m_button_pause_resume->GetToolTipText() == _L("Resume")) { - m_button_pause_resume->SetBitmap_("print_control_resume"); - } + if (m_button_pause_resume->GetToolTipText() == _L("Resume")) { m_button_pause_resume->SetBitmap_("print_control_resume"); } }); m_button_abort = new ScalableButton(progress_lr_panel, wxID_ANY, "print_control_stop", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, true); m_button_abort->SetToolTip(_L("Stop")); - m_button_abort->Bind(wxEVT_ENTER_WINDOW, [this](auto &e) { - m_button_abort->SetBitmap_("print_control_stop_hover"); - }); + m_button_abort->Bind(wxEVT_ENTER_WINDOW, [this](auto &e) { m_button_abort->SetBitmap_("print_control_stop_hover"); }); - m_button_abort->Bind(wxEVT_LEAVE_WINDOW, [this](auto &e) { - m_button_abort->SetBitmap_("print_control_stop"); } - ); + m_button_abort->Bind(wxEVT_LEAVE_WINDOW, [this](auto &e) { m_button_abort->SetBitmap_("print_control_stop"); }); - wxBoxSizer *bSizer_buttons = new wxBoxSizer(wxHORIZONTAL); - wxBoxSizer *bSizer_text = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer *bSizer_buttons = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer *bSizer_text = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer *bSizer_finish_time = new wxBoxSizer(wxHORIZONTAL); - wxPanel* penel_text = new wxPanel(progress_lr_panel); - wxPanel* penel_finish_time = new wxPanel(progress_lr_panel); + wxPanel *penel_text = new wxPanel(progress_lr_panel); + wxPanel *penel_finish_time = new wxPanel(progress_lr_panel); penel_text->SetBackgroundColour(*wxWHITE); penel_finish_time->SetBackgroundColour(*wxWHITE); @@ -678,10 +628,9 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) wxBoxSizer *sizer_percent = new wxBoxSizer(wxVERTICAL); sizer_percent->Add(0, 0, 1, wxEXPAND, 0); - wxBoxSizer *sizer_percent_icon = new wxBoxSizer(wxVERTICAL); + wxBoxSizer *sizer_percent_icon = new wxBoxSizer(wxVERTICAL); sizer_percent_icon->Add(0, 0, 1, wxEXPAND, 0); - m_staticText_progress_percent = new wxStaticText(penel_text, wxID_ANY, "0", wxDefaultPosition, wxDefaultSize, 0); m_staticText_progress_percent->SetFont(::Label::Head_18); m_staticText_progress_percent->SetMaxSize(wxSize(-1, FromDIP(20))); @@ -694,21 +643,20 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) sizer_percent->Add(m_staticText_progress_percent, 0, 0, 0); - #ifdef __WXOSX_MAC__ +#ifdef __WXOSX_MAC__ sizer_percent_icon->Add(m_staticText_progress_percent_icon, 0, wxBOTTOM, FromDIP(2)); - #else +#else sizer_percent_icon->Add(m_staticText_progress_percent_icon, 0, 0, 0); - #endif - +#endif m_staticText_progress_left = new wxStaticText(penel_text, wxID_ANY, L("N/A"), wxDefaultPosition, wxDefaultSize, 0); m_staticText_progress_left->Wrap(-1); m_staticText_progress_left->SetFont(wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxT("HarmonyOS Sans SC"))); - m_staticText_progress_left->SetForegroundColour(wxColour(146, 146, 146)); + m_staticText_progress_left->SetForegroundColour(wxColour(107, 107, 107)); m_staticText_layers = new wxStaticText(penel_text, wxID_ANY, _L("Layer: N/A")); m_staticText_layers->SetFont(wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxT("HarmonyOS Sans SC"))); - m_staticText_layers->SetForegroundColour(wxColour(146, 146, 146)); + m_staticText_layers->SetForegroundColour(wxColour(107, 107, 107)); m_staticText_layers->Hide(); bSizer_text->Add(sizer_percent, 0, wxEXPAND, 0); @@ -718,8 +666,8 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) bSizer_text->Add(0, 0, 0, wxLEFT, FromDIP(20)); bSizer_text->Add(m_staticText_progress_left, 0, wxALIGN_CENTER_VERTICAL | wxALL, 0); - m_printing_stage_panel = new wxPanel(penel_finish_time); - wxBoxSizer *printingstage_vertical_sizer = new wxBoxSizer(wxVERTICAL); + m_printing_stage_panel = new wxPanel(penel_finish_time); + wxBoxSizer *printingstage_vertical_sizer = new wxBoxSizer(wxVERTICAL); wxBoxSizer *printingstage_horizontal_sizer = new wxBoxSizer(wxHORIZONTAL); m_printing_stage_underline = new wxPanel(m_printing_stage_panel); @@ -741,7 +689,7 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) m_printing_stage_value->Bind(wxEVT_LEFT_UP, &PrintingTaskPanel::on_stage_clicked, this); m_printing_stage_value->Bind(wxEVT_ENTER_WINDOW, [this](wxMouseEvent &event) { - auto *dev_manager = wxGetApp().getDeviceManager(); + auto *dev_manager = wxGetApp().getDeviceManager(); MachineObject *obj = dev_manager ? dev_manager->get_selected_machine() : nullptr; if (obj && obj->stage_curr == 58) { m_printing_stage_value->SetCursor(wxCursor(wxCURSOR_HAND)); @@ -755,8 +703,8 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) event.Skip(); }); m_printing_stage_value->Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent &event) { - auto *dev_manager = wxGetApp().getDeviceManager(); - MachineObject *obj = dev_manager ? dev_manager->get_selected_machine() : nullptr; + auto *dev_manager = wxGetApp().getDeviceManager(); + MachineObject *obj = dev_manager ? dev_manager->get_selected_machine() : nullptr; if (obj && obj->stage_curr == 58) { m_printing_stage_value->SetCursor(wxCURSOR_ARROW); m_printing_stage_underline->Hide(); @@ -770,9 +718,9 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) penel_text->SetSizer(bSizer_text); penel_text->Layout(); - // Create question button - m_question_button = new ScalableButton(m_printing_stage_panel, wxID_ANY, "thermal_question", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, true); + m_question_button = new ScalableButton(m_printing_stage_panel, wxID_ANY, "thermal_question", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, + true); m_question_button->SetToolTip(_L("Click to view thermal preconditioning explanation")); m_question_button->SetBackgroundColour(wxColour(255, 255, 255)); m_question_button->Hide(); // Hide by default @@ -799,16 +747,16 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) printingstage_horizontal_sizer->Add(m_printing_stage_value, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL, 0); printingstage_horizontal_sizer->Add(m_question_button, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(5)); printingstage_vertical_sizer->Add(printingstage_horizontal_sizer, 0, wxALIGN_CENTER_VERTICAL, 0); - printingstage_vertical_sizer->Add(m_printing_stage_underline, 0, wxEXPAND |wxALIGN_TOP, 0); + printingstage_vertical_sizer->Add(m_printing_stage_underline, 0, wxEXPAND | wxALIGN_TOP, 0); m_printing_stage_panel->SetSizer(printingstage_vertical_sizer); m_staticText_finish_time = new Label(penel_finish_time); - const wxString& finish_time_str = _L("Estimated finish time: ") + "N/A"; + const wxString &finish_time_str = _L("Estimated finish time: ") + "N/A"; m_staticText_finish_time->SetLabel(finish_time_str); m_staticText_finish_time->Wrap(-1); m_staticText_finish_time->SetFont(Label::Body_14); - m_staticText_finish_time->SetForegroundColour(wxColour(146, 146, 146)); + m_staticText_finish_time->SetForegroundColour(wxColour(107, 107, 107)); m_staticText_finish_time->SetToolTip(_L("The estimated printing time for \nmulti-color models may be inaccurate.")); m_staticText_finish_day = new RectTextPanel(penel_finish_time); m_staticText_finish_day->SetMinSize(wxSize(20, 20)); @@ -821,27 +769,26 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) penel_finish_time->SetSizer(bSizer_finish_time); penel_finish_time->Layout(); - auto progress_lr_sizer = new wxBoxSizer(wxHORIZONTAL); - auto progress_left_sizer = new wxBoxSizer(wxVERTICAL); + auto progress_lr_sizer = new wxBoxSizer(wxHORIZONTAL); + auto progress_left_sizer = new wxBoxSizer(wxVERTICAL); auto progress_right_sizer = new wxBoxSizer(wxHORIZONTAL); progress_left_sizer->Add(penel_text, 0, wxEXPAND | wxALL, 0); progress_left_sizer->Add(m_gauge_progress, 0, wxEXPAND | wxTOP | wxBOTTOM, FromDIP(10)); - - progress_left_sizer->Add(penel_finish_time, 0, wxEXPAND |wxALL, 0); + progress_left_sizer->Add(penel_finish_time, 0, wxEXPAND | wxALL, 0); // progress_left_sizer->SetMaxSize(wxSize(FromDIP(600), -1)); progress_right_sizer->Add(0, 0, 0, wxEXPAND | wxLEFT, FromDIP(18)); - progress_right_sizer->Add(m_button_partskip, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(0));//5 + progress_right_sizer->Add(m_button_partskip, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(0)); // 5 progress_right_sizer->Add(0, 0, 0, wxEXPAND | wxLEFT, FromDIP(18)); progress_right_sizer->Add(m_button_pause_resume, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(0)); progress_right_sizer->Add(0, 0, 0, wxEXPAND | wxLEFT, FromDIP(18)); progress_right_sizer->Add(m_button_abort, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(0)); progress_right_sizer->Add(0, 0, 0, wxEXPAND | wxLEFT, FromDIP(18)); - progress_lr_sizer->Add(progress_left_sizer, 1, wxEXPAND | wxALL, 0); - progress_lr_sizer->Add(progress_right_sizer, 0, wxEXPAND | wxALL , 0); + progress_lr_sizer->Add(progress_left_sizer, 1, wxEXPAND | wxALL, 0); + progress_lr_sizer->Add(progress_right_sizer, 0, wxEXPAND | wxALL, 0); progress_lr_panel->SetSizer(progress_lr_sizer); progress_lr_panel->SetMaxSize(wxSize(FromDIP(720), -1)); @@ -850,7 +797,7 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) progress_lr_panel->Fit(); bSizer_subtask_info->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(14)); - bSizer_subtask_info->Add(bSizer_task_name, 0, wxEXPAND|wxRIGHT, FromDIP(18)); + bSizer_subtask_info->Add(bSizer_task_name, 0, wxEXPAND | wxRIGHT, FromDIP(18)); bSizer_subtask_info->Add(m_staticText_profile_value, 0, wxEXPAND | wxTOP, FromDIP(5)); bSizer_subtask_info->Add(progress_lr_panel, 0, wxEXPAND | wxTOP, FromDIP(5)); @@ -860,8 +807,8 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) m_printing_sizer->Add(FromDIP(8), 0, 0, wxEXPAND, 0); m_printing_sizer->Add(bSizer_subtask_info, 1, wxALL | wxEXPAND, 0); - m_staticline = new wxPanel( parent, wxID_ANY); - m_staticline->SetBackgroundColour(wxColour(238,238,238)); + m_staticline = new wxPanel(parent, wxID_ANY); + m_staticline->SetBackgroundColour(wxColour(238, 238, 238)); m_staticline->Layout(); m_staticline->Hide(); @@ -881,15 +828,14 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) StateColor clean_bd(std::pair(wxColour(144, 144, 144), StateColor::Disabled), std::pair(wxColour(38, 46, 48), StateColor::Enabled)); StateColor clean_text(std::pair(wxColour(144, 144, 144), StateColor::Disabled), std::pair(wxColour(38, 46, 48), StateColor::Enabled)); - m_button_clean->SetBackgroundColor(clean_bg); m_button_clean->SetBorderColor(clean_bd); m_button_clean->SetTextColor(clean_text); m_button_clean->SetFont(Label::Body_10); m_button_clean->SetMinSize(TASK_BUTTON_SIZE2); - static_text_sizer->Add( FromDIP(10), 0, 0, 0, 0 ); - static_text_sizer->Add(m_button_clean, 0, wxALIGN_CENTRE_VERTICAL|wxRIGHT, FromDIP(5)); + static_text_sizer->Add(FromDIP(10), 0, 0, 0, 0); + static_text_sizer->Add(m_button_clean, 0, wxALIGN_CENTRE_VERTICAL | wxRIGHT, FromDIP(5)); m_panel_error_txt->SetSizer(static_text_sizer); m_panel_error_txt->Hide(); @@ -908,10 +854,12 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) m_score_staticline->Hide(); sizer->Add(0, 0, 0, wxTOP, FromDIP(15)); sizer->Add(m_score_staticline, 0, wxEXPAND | wxALL, FromDIP(10)); - m_request_failed_panel = new wxPanel(parent, wxID_ANY); + m_request_failed_panel = new wxPanel(parent, wxID_ANY); m_request_failed_panel->SetBackgroundColour(*wxWHITE); wxBoxSizer *static_request_failed_panel_sizer = new wxBoxSizer(wxHORIZONTAL); - m_request_failed_info = new wxStaticText(m_request_failed_panel, wxID_ANY, _L("You have completed printing the mall model, \nbut the synchronization of rating information has failed."), wxDefaultPosition, wxDefaultSize, 0); + m_request_failed_info = new wxStaticText(m_request_failed_panel, wxID_ANY, + _L("You have completed printing the mall model, \nbut the synchronization of rating information has failed."), wxDefaultPosition, + wxDefaultSize, 0); m_request_failed_info->Wrap(-1); m_request_failed_info->SetForegroundColour(*wxRED); m_request_failed_info->SetFont(::Label::Body_10); @@ -933,15 +881,15 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) m_request_failed_panel->Hide(); sizer->Add(m_request_failed_panel, 0, wxEXPAND | wxALL, FromDIP(10)); - m_score_subtask_info = new wxPanel(parent, wxID_ANY); m_score_subtask_info->SetBackgroundColour(*wxWHITE); - wxBoxSizer * static_score_sizer = new wxBoxSizer(wxVERTICAL); + wxBoxSizer *static_score_sizer = new wxBoxSizer(wxVERTICAL); wxStaticText *static_score_text = new wxStaticText(m_score_subtask_info, wxID_ANY, _L("How do you like this printing file?"), wxDefaultPosition, wxDefaultSize, 0); static_score_text->Wrap(-1); static_score_sizer->Add(static_score_text, 1, wxEXPAND | wxALL, FromDIP(10)); - m_has_rated_prompt = new wxStaticText(m_score_subtask_info, wxID_ANY, _L("(The model has already been rated. Your rating will overwrite the previous rating.)"), wxDefaultPosition, wxDefaultSize, 0); + m_has_rated_prompt = new wxStaticText(m_score_subtask_info, wxID_ANY, _L("(The model has already been rated. Your rating will overwrite the previous rating.)"), + wxDefaultPosition, wxDefaultSize, 0); m_has_rated_prompt->Wrap(-1); m_has_rated_prompt->SetForegroundColour(*wxBLACK); m_has_rated_prompt->SetFont(::Label::Body_10); @@ -1007,22 +955,18 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) parent->Fit(); } -void PrintingTaskPanel::paint(wxPaintEvent&) +void PrintingTaskPanel::paint(wxPaintEvent &) { wxPaintDC dc(m_bitmap_thumbnail); if (wxGetApp().dark_mode()) { if (m_brightness_value > 0 && m_brightness_value < SHOW_BACKGROUND_BITMAP_PIXEL_THRESHOLD) { dc.DrawBitmap(m_bitmap_background.bmp(), 0, 0); dc.SetTextForeground(*wxBLACK); - } - else + } else dc.SetTextForeground(*wxWHITE); - } - else + } else dc.SetTextForeground(*wxBLACK); - if (m_thumbnail_bmp_display.IsOk()) { - dc.DrawBitmap(m_thumbnail_bmp_display, wxPoint(0, 0)); - } + if (m_thumbnail_bmp_display.IsOk()) { dc.DrawBitmap(m_thumbnail_bmp_display, wxPoint(0, 0)); } dc.SetFont(Label::Body_12); if (m_plate_index >= 0) { @@ -1039,14 +983,13 @@ void PrintingTaskPanel::set_has_reted_text(bool has_rated) m_has_rated_prompt->Hide(); } Layout(); - Fit(); } void PrintingTaskPanel::msw_rescale() { m_panel_printing_title->SetSize(wxSize(-1, FromDIP(PAGE_TITLE_HEIGHT))); m_printing_sizer->SetMinSize(wxSize(PAGE_MIN_WIDTH, -1)); - //m_staticText_printing->SetMinSize(wxSize(PAGE_TITLE_TEXT_WIDTH, PAGE_TITLE_HEIGHT)); + // m_staticText_printing->SetMinSize(wxSize(PAGE_TITLE_TEXT_WIDTH, PAGE_TITLE_HEIGHT)); m_gauge_progress->SetHeight(PROGRESSBAR_HEIGHT); m_gauge_progress->Rescale(); m_staticText_finish_day->Rescale(); @@ -1073,9 +1016,9 @@ void PrintingTaskPanel::msw_rescale() void PrintingTaskPanel::init_bitmaps() { - m_thumbnail_placeholder = ScalableBitmap(this, "monitor_placeholder", 120); - m_bitmap_use_time = ScalableBitmap(this, "print_info_time", 16); - m_bitmap_use_weight = ScalableBitmap(this, "print_info_weight", 16); + m_thumbnail_placeholder = ScalableBitmap(this, "monitor_placeholder", 120); + m_bitmap_use_time = ScalableBitmap(this, "print_info_time", 16); + m_bitmap_use_weight = ScalableBitmap(this, "print_info_weight", 16); } void PrintingTaskPanel::init_scaled_buttons() @@ -1107,19 +1050,17 @@ void PrintingTaskPanel::reset_printing_value() this->set_plate_index(-1); } -void PrintingTaskPanel::enable_partskip_button(MachineObject* obj, bool enable) +void PrintingTaskPanel::enable_partskip_button(MachineObject *obj, bool enable) { - int stage = 0; + int stage = 0; bool in_calibration_mode = false; - if( obj && (obj->print_type == "system" || CalibUtils::get_calib_mode_by_name(obj->subtask_name, stage) != CalibMode::Calib_None)){ - in_calibration_mode = true; - } + if (obj && (obj->print_type == "system" || CalibUtils::get_calib_mode_by_name(obj->subtask_name, stage) != CalibMode::Calib_None)) { in_calibration_mode = true; } if (!enable || in_calibration_mode) { m_button_partskip->Enable(false); m_button_partskip->SetLabel(""); m_button_partskip->SetIcon("print_control_partskip_disable"); - }else if(obj && obj->is_support_brtc){ + } else if (obj && obj->is_support_brtc) { m_button_partskip->Enable(true); m_button_partskip->SetIcon("print_control_partskip"); } @@ -1132,20 +1073,17 @@ void PrintingTaskPanel::enable_pause_resume_button(bool enable, std::string type if (type == "pause_disable") { m_button_pause_resume->SetBitmap_("print_control_pause_disable"); - } - else if (type == "resume_disable") { + } else if (type == "resume_disable") { m_button_pause_resume->SetBitmap_("print_control_resume_disable"); } - } - else { + } else { m_button_pause_resume->Enable(true); if (type == "resume") { - m_button_pause_resume->SetBitmap_("print_control_resume"); - if (m_button_pause_resume->GetToolTipText() != _L("Resume")) { m_button_pause_resume->SetToolTip(_L("Resume")); } - } - else if (type == "pause") { - m_button_pause_resume->SetBitmap_("print_control_pause"); - if (m_button_pause_resume->GetToolTipText() != _L("Pause")) { m_button_pause_resume->SetToolTip(_L("Pause")); } + m_button_pause_resume->SetBitmap_("print_control_resume"); + if (m_button_pause_resume->GetToolTipText() != _L("Resume")) { m_button_pause_resume->SetToolTip(_L("Resume")); } + } else if (type == "pause") { + m_button_pause_resume->SetBitmap_("print_control_pause"); + if (m_button_pause_resume->GetToolTipText() != _L("Pause")) { m_button_pause_resume->SetToolTip(_L("Pause")); } } } } @@ -1155,8 +1093,7 @@ void PrintingTaskPanel::enable_abort_button(bool enable) if (!enable) { m_button_abort->Enable(false); m_button_abort->SetBitmap_("print_control_stop_disable"); - } - else { + } else { m_button_abort->Enable(true); m_button_abort->SetBitmap_("print_control_stop"); } @@ -1164,10 +1101,7 @@ void PrintingTaskPanel::enable_abort_button(bool enable) void PrintingTaskPanel::update_subtask_name(wxString name) { - if (m_staticText_subtask_value->GetLabelText() != name) - { - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": " << name; - } + if (m_staticText_subtask_value->GetLabelText() != name) { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": " << name; } m_staticText_subtask_value->SetLabelText(name); } @@ -1194,13 +1128,13 @@ void PrintingTaskPanel::update_stage_value_with_machine(wxString stage, int val, void PrintingTaskPanel::on_stage_clicked(wxMouseEvent &event) { - auto *dev_manager = wxGetApp().getDeviceManager(); - MachineObject *obj = dev_manager ? dev_manager->get_selected_machine() : nullptr; + auto *dev_manager = wxGetApp().getDeviceManager(); + MachineObject *obj = dev_manager ? dev_manager->get_selected_machine() : nullptr; if (obj && obj->stage_curr == 58) { - wxWindow *top = wxGetTopLevelParent(this); - ThermalPreconditioningDialog m_thermal_dialog(top ? top : this, obj->get_dev_id() , "Calculating..."); - m_thermal_dialog.ShowModal(); + wxWindow *top = wxGetTopLevelParent(this); + ThermalPreconditioningDialog m_thermal_dialog(top ? top : this, obj->get_dev_id(), "Calculating..."); + m_thermal_dialog.ShowModal(); } event.Skip(); @@ -1212,26 +1146,22 @@ void PrintingTaskPanel::update_progress_percent(wxString percent, wxString icon) m_staticText_progress_percent_icon->SetLabelText(icon); } -void PrintingTaskPanel::update_left_time(wxString time) -{ - m_staticText_progress_left->SetLabelText(time); -} +void PrintingTaskPanel::update_left_time(wxString time) { m_staticText_progress_left->SetLabelText(time); } void PrintingTaskPanel::update_finish_time(wxString finish_time) { if (finish_time == "Finished") { m_staticText_finish_time->SetLabelText(_L("Finished")); if (m_staticText_finish_day->IsShown()) m_staticText_finish_day->Hide(); - } - else { + } else { if (!finish_time.Contains('+')) { if (m_staticText_finish_day->IsShown()) m_staticText_finish_day->Hide(); } else { - int index = finish_time.find_last_of('+'); + int index = finish_time.find_last_of('+'); wxString day = finish_time.Mid(index); finish_time = finish_time.Mid(0, index); m_staticText_finish_day->setText(day); - if (!day.empty()) { m_staticText_finish_day->Show();} + if (!day.empty()) { m_staticText_finish_day->Show(); } } wxString finish_time_str = _L("Estimated finish time: ") + finish_time; @@ -1240,8 +1170,7 @@ void PrintingTaskPanel::update_finish_time(wxString finish_time) finish_time_str += '\0'; /*github#5028 the problem occurs stable on BODY_13. Maybe this is a BUG of some fonts or windows OS. Add '0' will walk around it. FIXME*/ #endif - if (m_staticText_finish_time->GetLabelText() != finish_time_str) - { + if (m_staticText_finish_time->GetLabelText() != finish_time_str) { m_staticText_finish_time->SetLabelText(finish_time_str); m_staticText_finish_time->Wrap(-1); BOOST_LOG_TRIVIAL(info) << "PrintingTaskPanel::update_finish_time: " << finish_time_str << " Result: " << m_staticText_finish_time->GetLabelText(); @@ -1258,10 +1187,9 @@ void PrintingTaskPanel::update_left_time(int mc_left_time) wxString left_time_text = NA_STR; try { - left_time = get_qdt_monitor_time_dhm(mc_left_time); + left_time = get_qdt_monitor_time_dhm(mc_left_time); right_time = get_qdt_finish_time_dhm(mc_left_time); - } - catch (...) { + } catch (...) { ; } @@ -1270,8 +1198,7 @@ void PrintingTaskPanel::update_left_time(int mc_left_time) update_finish_time(right_time); static int s_mc_left_time = 0; - if (s_mc_left_time != mc_left_time) - { + if (s_mc_left_time != mc_left_time) { BOOST_LOG_TRIVIAL(info) << "PrintingTaskPanel::update_left_time: " << mc_left_time << ", " << left_time_text << ": " << right_time; s_mc_left_time = mc_left_time; } @@ -1279,16 +1206,12 @@ void PrintingTaskPanel::update_left_time(int mc_left_time) void PrintingTaskPanel::update_layers_num(bool show, wxString num) { - if ((show == m_staticText_layers->IsShown()) && (num == m_staticText_layers->GetLabelText())) - { - return; - } + if ((show == m_staticText_layers->IsShown()) && (num == m_staticText_layers->GetLabelText())) { return; } if (show) { m_staticText_layers->Show(true); m_staticText_layers->SetLabelText(num); - } - else { + } else { m_staticText_layers->Show(false); m_staticText_layers->SetLabelText(num); } @@ -1309,8 +1232,7 @@ void PrintingTaskPanel::show_priting_use_info(bool show, wxString time /*= wxEmp m_staticText_consumption_of_time->SetLabelText(time); m_staticText_consumption_of_weight->SetLabelText(weight); - } - else { + } else { m_staticText_consumption_of_time->SetLabelText("0m"); m_staticText_consumption_of_weight->SetLabelText("0g"); if (m_staticText_consumption_of_time->IsShown()) { @@ -1321,17 +1243,16 @@ void PrintingTaskPanel::show_priting_use_info(bool show, wxString time /*= wxEmp if (m_staticText_consumption_of_weight->IsShown()) { m_bitmap_static_use_weight->Hide(); m_staticText_consumption_of_weight->Hide(); - } } + } + } } - void PrintingTaskPanel::show_profile_info(bool show, wxString profile /*= wxEmptyString*/) { if (show) { if (!m_staticText_profile_value->IsShown()) { m_staticText_profile_value->Show(); } m_staticText_profile_value->SetLabelText(profile); - } - else { + } else { m_staticText_profile_value->SetLabelText(wxEmptyString); m_staticText_profile_value->Hide(); } @@ -1339,21 +1260,16 @@ void PrintingTaskPanel::show_profile_info(bool show, wxString profile /*= wxEmpt // the API will buff the bmp and bmp_name // when bmp_name is empty, the API will replace the image on force -void PrintingTaskPanel::set_thumbnail_img(const wxBitmap& bmp, const std::string& bmp_name) +void PrintingTaskPanel::set_thumbnail_img(const wxBitmap &bmp, const std::string &bmp_name) { - if (!bmp_name.empty() && m_thumbnail_bmp_display_name == bmp_name) { - return; - } + if (!bmp_name.empty() && m_thumbnail_bmp_display_name == bmp_name) { return; } m_thumbnail_bmp_display_name = bmp_name; - m_thumbnail_bmp_display = bmp; + m_thumbnail_bmp_display = bmp; Refresh(); } -void PrintingTaskPanel::set_plate_index(int plate_idx) -{ - m_plate_index = plate_idx; -} +void PrintingTaskPanel::set_plate_index(int plate_idx) { m_plate_index = plate_idx; } void PrintingTaskPanel::market_scoring_show(bool show) { @@ -1361,6 +1277,11 @@ void PrintingTaskPanel::market_scoring_show(bool show) m_score_subtask_info->Show(show); } +bool PrintingTaskPanel::is_market_scoring_show() +{ + return m_score_subtask_info->IsShown(); +} + void PrintingTaskPanel::set_star_count(int star_count) { m_star_count = star_count; @@ -1380,7 +1301,7 @@ StatusBasePanel::StatusBasePanel(wxWindow *parent, wxWindowID id, const wxPoint : wxScrolledWindow(parent, id, pos, size, wxHSCROLL | wxVSCROLL) { this->SetScrollRate(25, 25); - Slic3r::DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager(); + Slic3r::DeviceManager *dev = Slic3r::GUI::wxGetApp().getDeviceManager(); if (!dev) return; obj = dev->get_selected_machine(); @@ -1416,12 +1337,12 @@ StatusBasePanel::StatusBasePanel(wxWindow *parent, wxWindowID id, const wxPoint m_project_task_panel = new PrintingTaskPanel(this, PrintingTaskType::PRINGINT); m_project_task_panel->init_bitmaps(); - m_monitoring_sizer->Add(m_project_task_panel, 0, wxALL | wxEXPAND , 0); + m_monitoring_sizer->Add(m_project_task_panel, 0, wxALL | wxEXPAND, 0); -// auto m_panel_separotor2 = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); -// m_panel_separotor2->SetBackgroundColour(STATUS_PANEL_BG); -// m_panel_separotor2->SetMinSize(wxSize(-1, PAGE_SPACING)); -// bSizer_left->Add(m_panel_separotor2, 1, wxEXPAND, 0); + // auto m_panel_separotor2 = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + // m_panel_separotor2->SetBackgroundColour(STATUS_PANEL_BG); + // m_panel_separotor2->SetMinSize(wxSize(-1, PAGE_SPACING)); + // bSizer_left->Add(m_panel_separotor2, 1, wxEXPAND, 0); bSizer_status_below->Add(bSizer_left, 1, wxALL | wxEXPAND, 0); @@ -1456,10 +1377,7 @@ StatusBasePanel::StatusBasePanel(wxWindow *parent, wxWindowID id, const wxPoint this->Layout(); } -StatusBasePanel::~StatusBasePanel() -{ - delete m_media_play_ctrl; -} +StatusBasePanel::~StatusBasePanel() { delete m_media_play_ctrl; } void StatusBasePanel::init_bitmaps() { @@ -1475,24 +1393,23 @@ void StatusBasePanel::init_bitmaps() m_bitmap_speed = ScalableBitmap(this, "monitor_speed", 24); m_bitmap_speed_active = ScalableBitmap(this, "monitor_speed_active", 24); - m_thumbnail_brokenimg = ScalableBitmap(this, "monitor_brokenimg", 120); - m_thumbnail_sdcard = ScalableBitmap(this, "monitor_sdcard_thumbnail", 120); - //m_bitmap_camera = create_scaled_bitmap("monitor_camera", nullptr, 18); - m_bitmap_extruder_empty_load = *cache.load_png("monitor_extruder_empty_load", FromDIP(28), FromDIP(70), false, false); - m_bitmap_extruder_filled_load = *cache.load_png("monitor_extruder_filled_load", FromDIP(28), FromDIP(70), false, false); - m_bitmap_extruder_empty_unload = *cache.load_png("monitor_extruder_empty_unload", FromDIP(28), FromDIP(70), false, false); - m_bitmap_extruder_filled_unload = *cache.load_png("monitor_extruder_filled_unload", FromDIP(28), FromDIP(70), false, false); + m_thumbnail_brokenimg = ScalableBitmap(this, "monitor_brokenimg", 120); + m_thumbnail_sdcard = ScalableBitmap(this, "monitor_sdcard_thumbnail", 120); + // m_bitmap_camera = create_scaled_bitmap("monitor_camera", nullptr, 18); + m_bitmap_extruder_empty_load = *cache.load_png("monitor_extruder_empty_load", FromDIP(28), FromDIP(70), false, false); + m_bitmap_extruder_filled_load = *cache.load_png("monitor_extruder_filled_load", FromDIP(28), FromDIP(70), false, false); + m_bitmap_extruder_empty_unload = *cache.load_png("monitor_extruder_empty_unload", FromDIP(28), FromDIP(70), false, false); + m_bitmap_extruder_filled_unload = *cache.load_png("monitor_extruder_filled_unload", FromDIP(28), FromDIP(70), false, false); m_bitmap_sdcard_state_abnormal = ScalableBitmap(this, wxGetApp().dark_mode() ? "sdcard_state_abnormal_dark" : "sdcard_state_abnormal", 20); - m_bitmap_sdcard_state_normal = ScalableBitmap(this, wxGetApp().dark_mode() ? "sdcard_state_normal_dark" : "sdcard_state_normal", 20); - m_bitmap_sdcard_state_no = ScalableBitmap(this, wxGetApp().dark_mode() ? "sdcard_state_no_dark" : "sdcard_state_no", 20); - m_bitmap_recording_on = ScalableBitmap(this, wxGetApp().dark_mode() ? "monitor_recording_on_dark" : "monitor_recording_on", 20); - m_bitmap_recording_off = ScalableBitmap(this, wxGetApp().dark_mode() ? "monitor_recording_off_dark" : "monitor_recording_off", 20); - m_bitmap_timelapse_on = ScalableBitmap(this, wxGetApp().dark_mode() ? "monitor_timelapse_on_dark" : "monitor_timelapse_on", 20); - m_bitmap_timelapse_off = ScalableBitmap(this, wxGetApp().dark_mode() ? "monitor_timelapse_off_dark" : "monitor_timelapse_off", 20); - m_bitmap_vcamera_on = ScalableBitmap(this, wxGetApp().dark_mode() ? "monitor_vcamera_on_dark" : "monitor_vcamera_on", 20); - m_bitmap_vcamera_off = ScalableBitmap(this, wxGetApp().dark_mode() ? "monitor_vcamera_off_dark" : "monitor_vcamera_off", 20); - + m_bitmap_sdcard_state_normal = ScalableBitmap(this, wxGetApp().dark_mode() ? "sdcard_state_normal_dark" : "sdcard_state_normal", 20); + m_bitmap_sdcard_state_no = ScalableBitmap(this, wxGetApp().dark_mode() ? "sdcard_state_no_dark" : "sdcard_state_no", 20); + m_bitmap_recording_on = ScalableBitmap(this, wxGetApp().dark_mode() ? "monitor_recording_on_dark" : "monitor_recording_on", 20); + m_bitmap_recording_off = ScalableBitmap(this, wxGetApp().dark_mode() ? "monitor_recording_off_dark" : "monitor_recording_off", 20); + m_bitmap_timelapse_on = ScalableBitmap(this, wxGetApp().dark_mode() ? "monitor_timelapse_on_dark" : "monitor_timelapse_on", 20); + m_bitmap_timelapse_off = ScalableBitmap(this, wxGetApp().dark_mode() ? "monitor_timelapse_off_dark" : "monitor_timelapse_off", 20); + m_bitmap_vcamera_on = ScalableBitmap(this, wxGetApp().dark_mode() ? "monitor_vcamera_on_dark" : "monitor_vcamera_on", 20); + m_bitmap_vcamera_off = ScalableBitmap(this, wxGetApp().dark_mode() ? "monitor_vcamera_off_dark" : "monitor_vcamera_off", 20); } wxBoxSizer *StatusBasePanel::create_monitoring_page() @@ -1507,11 +1424,10 @@ wxBoxSizer *StatusBasePanel::create_monitoring_page() m_staticText_monitoring = new Label(m_panel_monitoring_title, _L("Camera")); m_staticText_monitoring->Wrap(-1); - //m_staticText_monitoring->SetFont(PAGE_TITLE_FONT); + // m_staticText_monitoring->SetFont(PAGE_TITLE_FONT); m_staticText_monitoring->SetForegroundColour(PAGE_TITLE_FONT_COL); bSizer_monitoring_title->Add(m_staticText_monitoring, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, PAGE_TITLE_LEFT_MARGIN); - bSizer_monitoring_title->Add(FromDIP(13), 0, 0, 0); bSizer_monitoring_title->AddStretchSpacer(); @@ -1572,9 +1488,9 @@ wxBoxSizer *StatusBasePanel::create_monitoring_page() bSizer_monitoring_title->Fit(m_panel_monitoring_title); sizer->Add(m_panel_monitoring_title, 0, wxEXPAND | wxALL, 0); -// media_ctrl_panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize); -// media_ctrl_panel->SetBackgroundColour(*wxBLACK); -// wxBoxSizer *bSizer_monitoring = new wxBoxSizer(wxVERTICAL); + // media_ctrl_panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize); + // media_ctrl_panel->SetBackgroundColour(*wxBLACK); + // wxBoxSizer *bSizer_monitoring = new wxBoxSizer(wxVERTICAL); m_media_ctrl = new wxMediaCtrl3(this); m_media_ctrl->SetMinSize(wxSize(PAGE_MIN_WIDTH, FromDIP(288))); @@ -1582,10 +1498,10 @@ wxBoxSizer *StatusBasePanel::create_monitoring_page() sizer->Add(m_media_ctrl, 1, wxEXPAND | wxALL, 0); sizer->Add(m_media_play_ctrl, 0, wxEXPAND | wxALL, 0); -// media_ctrl_panel->SetSizer(bSizer_monitoring); -// media_ctrl_panel->Layout(); -// -// sizer->Add(media_ctrl_panel, 1, wxEXPAND | wxALL, 1); + // media_ctrl_panel->SetSizer(bSizer_monitoring); + // media_ctrl_panel->Layout(); + // + // sizer->Add(media_ctrl_panel, 1, wxEXPAND | wxALL, 1); return sizer; } @@ -1597,9 +1513,9 @@ wxBoxSizer *StatusBasePanel::create_machine_control_page(wxWindow *parent) m_panel_control_title->SetBackgroundColour(STATUS_TITLE_BG); wxBoxSizer *bSizer_control_title = new wxBoxSizer(wxHORIZONTAL); - m_staticText_control = new Label(m_panel_control_title,_L("Control")); + m_staticText_control = new Label(m_panel_control_title, _L("Control")); m_staticText_control->Wrap(-1); - //m_staticText_control->SetFont(PAGE_TITLE_FONT); + // m_staticText_control->SetFont(PAGE_TITLE_FONT); m_staticText_control->SetForegroundColour(PAGE_TITLE_FONT_COL); // y96 StateColor btn_bg_blue(std::pair(AMS_CONTROL_DISABLE_COLOUR, StateColor::Disabled), std::pair(wxColour(95, 82, 253), StateColor::Pressed), @@ -1633,7 +1549,6 @@ wxBoxSizer *StatusBasePanel::create_machine_control_page(wxWindow *parent) m_calibration_btn->SetTextColor(wxColour("#FFFFFE")); m_calibration_btn->SetSize(wxSize(FromDIP(128), FromDIP(26))); m_calibration_btn->SetMinSize(wxSize(-1, FromDIP(26))); - m_calibration_btn->EnableTooltipEvenDisabled(); m_options_btn->Hide(); @@ -1653,16 +1568,29 @@ wxBoxSizer *StatusBasePanel::create_machine_control_page(wxWindow *parent) wxBoxSizer *bSizer_control = new wxBoxSizer(wxVERTICAL); - auto temp_axis_ctrl_sizer = create_temp_axis_group(parent); - auto m_ams_ctrl_sizer = create_ams_group(parent); + auto temp_axis_ctrl_sizer = create_temp_axis_group(parent); auto m_filament_load_sizer = create_filament_group(parent); + /* ams or rack*/ + wxSizer *ams_rack_sizer = new wxBoxSizer(wxHORIZONTAL); + ams_rack_sizer->Add(create_ams_group(parent), 0, wxEXPAND | wxLEFT); + + m_panel_nozzle_rack = new wgtDeviceNozzleRack(parent); + m_panel_nozzle_rack->Show(false); + ams_rack_sizer->Add(m_panel_nozzle_rack, 0, wxEXPAND | wxLEFT); + + m_ams_rack_switch = new SwitchBoard(parent, _L("Filament"), _L("Hotends"), wxSize(FromDIP(126), FromDIP(26))); + m_ams_rack_switch->updateState("left"); + m_ams_rack_switch->Hide(); + m_ams_rack_switch->Bind(wxCUSTOMEVT_SWITCH_POS, &StatusBasePanel::on_ams_rack_switch, this); + bSizer_control->Add(0, 0, 0, wxTOP, FromDIP(8)); - bSizer_control->Add(temp_axis_ctrl_sizer, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT, FromDIP(8)); + bSizer_control->Add(temp_axis_ctrl_sizer, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, FromDIP(8)); + bSizer_control->Add(m_ams_rack_switch, 0, wxALIGN_CENTRE | wxTOP, FromDIP(6)); bSizer_control->Add(0, 0, 0, wxTOP, FromDIP(6)); - bSizer_control->Add(m_ams_ctrl_sizer, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT, FromDIP(8)); + bSizer_control->Add(ams_rack_sizer, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, FromDIP(8)); bSizer_control->Add(0, 0, 0, wxTOP, FromDIP(6)); - bSizer_control->Add(m_filament_load_sizer, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT, FromDIP(8)); + bSizer_control->Add(m_filament_load_sizer, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, FromDIP(8)); bSizer_control->Add(0, 0, 0, wxTOP, FromDIP(4)); bSizer_right->Add(bSizer_control, 1, wxEXPAND | wxALL, 0); @@ -1672,8 +1600,8 @@ wxBoxSizer *StatusBasePanel::create_machine_control_page(wxWindow *parent) wxBoxSizer *StatusBasePanel::create_temp_axis_group(wxWindow *parent) { - auto sizer = new wxBoxSizer(wxVERTICAL); - auto box = new StaticBox(parent); + auto sizer = new wxBoxSizer(wxVERTICAL); + auto box = new StaticBox(parent); StateColor box_colour(std::pair(*wxWHITE, StateColor::Normal)); StateColor box_border_colour(std::pair(STATUS_PANEL_BG, StateColor::Normal)); @@ -1688,30 +1616,22 @@ wxBoxSizer *StatusBasePanel::create_temp_axis_group(wxWindow *parent) wxBoxSizer *content_sizer = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer *m_temp_ctrl = create_temp_control(box); - m_temp_temp_line = new wxPanel(box); m_temp_temp_line->SetMaxSize(wxSize(FromDIP(1), -1)); m_temp_temp_line->SetMinSize(wxSize(FromDIP(1), -1)); m_temp_temp_line->SetBackgroundColour(STATIC_BOX_LINE_COL); - auto m_axis_sizer = create_axis_control(box); - auto bedPanel = create_bed_control(box); + auto bedPanel = create_bed_control(box); - wxBoxSizer *extruder_sizer = create_extruder_control(box); - wxBoxSizer* axis_and_bed_control_sizer = new wxBoxSizer(wxVERTICAL); + wxBoxSizer *extruder_sizer = create_extruder_control(box); + wxBoxSizer *axis_and_bed_control_sizer = new wxBoxSizer(wxVERTICAL); axis_and_bed_control_sizer->Add(m_axis_sizer, 0, wxEXPAND | wxALL, 0); axis_and_bed_control_sizer->Add(bedPanel, 0, wxALIGN_CENTER, 0); content_sizer->Add(m_temp_ctrl, 0, wxEXPAND | wxALL, FromDIP(5)); content_sizer->Add(m_temp_temp_line, 0, wxEXPAND, 1); - //content_sizer->Add(FromDIP(9), 0, 0, wxEXPAND, 1); - /*content_sizer->Add(0, 0, 0, wxLEFT, FromDIP(18)); - content_sizer->Add(m_axis_sizer, 0, wxALIGN_CENTER_VERTICAL | wxALL, 0); - content_sizer->Add(0, 0, 0, wxLEFT, FromDIP(18)); - content_sizer->Add(bed_sizer, 0, wxEXPAND | wxLEFT | wxTOP | wxBOTTOM, FromDIP(12));*/ content_sizer->Add(axis_and_bed_control_sizer, 1, wxALIGN_CENTER, 0); - //content_sizer->Add(0, 0, 0, wxLEFT, FromDIP(18)); m_temp_extruder_line = new wxPanel(box); m_temp_extruder_line->SetMaxSize(wxSize(FromDIP(1), -1)); @@ -1719,7 +1639,7 @@ wxBoxSizer *StatusBasePanel::create_temp_axis_group(wxWindow *parent) m_temp_extruder_line->SetBackgroundColour(STATIC_BOX_LINE_COL); content_sizer->Add(m_temp_extruder_line, 0, wxEXPAND, 1); - content_sizer->Add(extruder_sizer, 0, wxEXPAND | wxTOP | wxBOTTOM, FromDIP(12)); + content_sizer->Add(extruder_sizer, 0, wxEXPAND | wxTOP | wxBOTTOM, FromDIP(12)); content_sizer->Add(0, 0, 0, wxRIGHT, FromDIP(3)); box->SetSizer(content_sizer); @@ -1733,7 +1653,7 @@ wxBoxSizer *StatusBasePanel::create_temp_control(wxWindow *parent) wxWindowID nozzle_id = wxWindow::NewControlId(); m_tempCtrl_nozzle = new TempInput(parent, nozzle_id, TEMP_BLANK_STR, TempInputType::TEMP_OF_NORMAL_TYPE, TEMP_BLANK_STR, wxString("monitor_nozzle_temp"), - wxString("monitor_nozzle_temp_active"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER); + wxString("monitor_nozzle_temp_active"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER); m_tempCtrl_nozzle->SetMinSize(TEMP_CTRL_MIN_SIZE_ALIGN_TWO_ICON); m_tempCtrl_nozzle->AddTemp(0); // zero is default temp m_tempCtrl_nozzle->SetMinTemp(20); @@ -1741,14 +1661,14 @@ wxBoxSizer *StatusBasePanel::create_temp_control(wxWindow *parent) m_tempCtrl_nozzle->SetBorderWidth(FromDIP(2)); StateColor tempinput_text_colour(std::make_pair(DISCONNECT_TEXT_COL, (int) StateColor::Disabled), std::make_pair(NORMAL_TEXT_COL, (int) StateColor::Normal)); - StateColor tempinput_border_colour(std::make_pair(*wxWHITE, (int)StateColor::Disabled), std::make_pair(BUTTON_HOVER_COL, (int)StateColor::Focused), - std::make_pair(BUTTON_HOVER_COL, (int)StateColor::Hovered), std::make_pair(*wxWHITE, (int)StateColor::Normal)); + StateColor tempinput_border_colour(std::make_pair(*wxWHITE, (int) StateColor::Disabled), std::make_pair(BUTTON_HOVER_COL, (int) StateColor::Focused), + std::make_pair(BUTTON_HOVER_COL, (int) StateColor::Hovered), std::make_pair(*wxWHITE, (int) StateColor::Normal)); m_tempCtrl_nozzle->SetTextColor(tempinput_text_colour); m_tempCtrl_nozzle->SetBorderColor(tempinput_border_colour); - m_tempCtrl_nozzle_deputy = new TempInput(parent, nozzle_id, TEMP_BLANK_STR, TempInputType::TEMP_OF_NORMAL_TYPE, TEMP_BLANK_STR, wxString("monitor_nozzle_temp"), wxString("monitor_nozzle_temp_active"), - wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER); + m_tempCtrl_nozzle_deputy = new TempInput(parent, nozzle_id, TEMP_BLANK_STR, TempInputType::TEMP_OF_NORMAL_TYPE, TEMP_BLANK_STR, wxString("monitor_nozzle_temp"), + wxString("monitor_nozzle_temp_active"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER); m_tempCtrl_nozzle_deputy->SetMinSize(TEMP_CTRL_MIN_SIZE_ALIGN_TWO_ICON); m_tempCtrl_nozzle_deputy->AddTemp(0); // zero is default temp m_tempCtrl_nozzle_deputy->SetMinTemp(20); @@ -1769,7 +1689,7 @@ wxBoxSizer *StatusBasePanel::create_temp_control(wxWindow *parent) wxWindowID bed_id = wxWindow::NewControlId(); m_tempCtrl_bed = new TempInput(parent, bed_id, TEMP_BLANK_STR, TempInputType::TEMP_OF_NORMAL_TYPE, TEMP_BLANK_STR, wxString("monitor_bed_temp"), - wxString("monitor_bed_temp_active"), wxDefaultPosition,wxDefaultSize, wxALIGN_CENTER); + wxString("monitor_bed_temp_active"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER); m_tempCtrl_bed->AddTemp(0); // zero is default temp m_tempCtrl_bed->SetMinTemp(bed_temp_range[0]); m_tempCtrl_bed->SetMaxTemp(bed_temp_range[1]); @@ -1784,8 +1704,8 @@ wxBoxSizer *StatusBasePanel::create_temp_control(wxWindow *parent) sizer->Add(line, 0, wxEXPAND | wxLEFT | wxRIGHT, 12); wxWindowID frame_id = wxWindow::NewControlId(); - m_tempCtrl_chamber = new TempInput(parent, frame_id, TEMP_BLANK_STR, TempInputType::TEMP_OF_NORMAL_TYPE, TEMP_BLANK_STR, wxString("monitor_frame_temp"), - wxString("monitor_frame_temp_active"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER); + m_tempCtrl_chamber = new TempInput(parent, frame_id, TEMP_BLANK_STR, TempInputType::TEMP_OF_NORMAL_TYPE, TEMP_BLANK_STR, wxString("monitor_frame_temp"), + wxString("monitor_frame_temp_active"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER); m_tempCtrl_chamber->AddTemp(0); // zero is default temp m_tempCtrl_chamber->SetReadOnly(true); m_tempCtrl_chamber->SetMinTemp(default_champer_temp_min); @@ -1835,7 +1755,7 @@ wxBoxSizer *StatusBasePanel::create_misc_control(wxWindow *parent) m_switch_lamp->SetTextColor(StateColor(std::make_pair(DISCONNECT_TEXT_COL, (int) StateColor::Disabled), std::make_pair(NORMAL_TEXT_COL, (int) StateColor::Normal))); line_sizer->Add(m_switch_lamp, 1, wxALIGN_CENTER | wxALL, 0); - //sizer->Add(line_sizer, 0, wxEXPAND, FromDIP(5)); + // sizer->Add(line_sizer, 0, wxEXPAND, FromDIP(5)); line = new StaticLine(parent); line->SetLineColour(STATIC_BOX_LINE_COL); sizer->Add(line, 0, wxEXPAND | wxLEFT | wxRIGHT, 12); @@ -1847,8 +1767,8 @@ wxBoxSizer *StatusBasePanel::create_misc_control(wxWindow *parent) m_fan_panel->SetBorderWidth(0); m_fan_panel->SetCornerRadius(0); - auto fan_line_sizer = new wxBoxSizer(wxHORIZONTAL); - m_switch_fan = new FanSwitchButton(m_fan_panel, m_bitmap_fan_on, m_bitmap_fan_off); + auto fan_line_sizer = new wxBoxSizer(wxHORIZONTAL); + m_switch_fan = new FanSwitchButton(m_fan_panel, m_bitmap_fan_on, m_bitmap_fan_off); m_switch_fan->SetValue(false); m_switch_fan->SetMinSize(MISC_BUTTON_1FAN_SIZE); m_switch_fan->SetMaxSize(MISC_BUTTON_1FAN_SIZE); @@ -1857,18 +1777,13 @@ wxBoxSizer *StatusBasePanel::create_misc_control(wxWindow *parent) m_switch_fan->SetCornerRadius(0); m_switch_fan->SetFont(::Label::Body_10); m_switch_fan->UseTextFan(); - m_switch_fan->SetTextColor( - StateColor(std::make_pair(DISCONNECT_TEXT_COL, (int)StateColor::Disabled), std::make_pair(NORMAL_FAN_TEXT_COL, (int)StateColor::Normal))); + m_switch_fan->SetTextColor(StateColor(std::make_pair(DISCONNECT_TEXT_COL, (int) StateColor::Disabled), std::make_pair(NORMAL_FAN_TEXT_COL, (int) StateColor::Normal))); - m_switch_fan->Bind(wxEVT_ENTER_WINDOW, [this](auto& e) { - m_fan_panel->SetBackgroundColor(wxColour(68, 121, 251)); - }); + m_switch_fan->Bind(wxEVT_ENTER_WINDOW, [this](auto &e) { m_fan_panel->SetBackgroundColor(wxColour(0, 174, 66)); }); - m_switch_fan->Bind(wxEVT_LEAVE_WINDOW, [this, parent](auto& e) { - m_fan_panel->SetBackgroundColor(parent->GetBackgroundColour()); - }); + m_switch_fan->Bind(wxEVT_LEAVE_WINDOW, [this, parent](auto &e) { m_fan_panel->SetBackgroundColor(parent->GetBackgroundColour()); }); - fan_line_sizer->Add(m_switch_fan, 1, wxEXPAND|wxALL, FromDIP(2)); + fan_line_sizer->Add(m_switch_fan, 1, wxEXPAND | wxALL, FromDIP(2)); m_fan_panel->SetSizer(fan_line_sizer); m_fan_panel->Layout(); @@ -1960,7 +1875,7 @@ wxPanel *StatusBasePanel::create_bed_control(wxWindow *parent) m_bpButton_z_1->SetSize(Z_BUTTON_SIZE); m_bpButton_z_1->SetTextColor(StateColor(std::make_pair(DISCONNECT_TEXT_COL, (int) StateColor::Disabled), std::make_pair(NORMAL_TEXT_COL, (int) StateColor::Normal))); - //bSizer_z_ctrl->Add(0, FromDIP(6), 0, wxEXPAND, 0); + // bSizer_z_ctrl->Add(0, FromDIP(6), 0, wxEXPAND, 0); m_staticText_z_tip = new wxStaticText(panel, wxID_ANY, _L("Bed"), wxDefaultPosition, wxDefaultSize, 0); m_staticText_z_tip->SetFont(::Label::Body_12); @@ -2000,9 +1915,9 @@ wxPanel *StatusBasePanel::create_bed_control(wxWindow *parent) wxBoxSizer *StatusBasePanel::create_extruder_control(wxWindow *parent) { - wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL); + wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL); wxBoxSizer *bSizer_e_ctrl = new wxBoxSizer(wxVERTICAL); - auto panel = new wxPanel(parent,wxID_ANY); + auto panel = new wxPanel(parent, wxID_ANY); panel->SetBackgroundColour(*wxWHITE); panel->SetSize(wxSize(FromDIP(143), -1)); @@ -2062,42 +1977,33 @@ wxBoxSizer *StatusBasePanel::create_extruder_control(wxWindow *parent) return sizer; } -wxBoxSizer *StatusBasePanel::create_ams_group(wxWindow *parent) +StaticBox *StatusBasePanel::create_ams_group(wxWindow *parent) { - auto sizer = new wxBoxSizer(wxVERTICAL); - auto sizer_box = new wxBoxSizer(wxVERTICAL); - - m_ams_control_box = new StaticBox(parent); - StateColor box_colour(std::pair(*wxWHITE, StateColor::Normal)); StateColor box_border_colour(std::pair(STATUS_PANEL_BG, StateColor::Normal)); + m_ams_control_box = new StaticBox(parent); m_ams_control_box->SetBackgroundColor(box_colour); m_ams_control_box->SetBorderColor(box_border_colour); m_ams_control_box->SetCornerRadius(5); m_ams_control_box->SetMinSize(wxSize(FromDIP(586), -1)); m_ams_control_box->SetBackgroundColour(*wxWHITE); -#if !QDT_RELEASE_TO_PUBLIC - m_ams_debug = new wxStaticText(m_ams_control_box, wxID_ANY, _L("Debug Info"), wxDefaultPosition, wxDefaultSize, 0); - sizer_box->Add(m_ams_debug, 0, wxALIGN_CENTER_HORIZONTAL, 0); - m_ams_debug->Hide(); -#endif m_ams_control = new AMSControl(m_ams_control_box, wxID_ANY); - //m_ams_control->SetMinSize(wxSize(FromDIP(510), FromDIP(286))); m_ams_control->SetDoubleBuffered(true); + + auto sizer_box = new wxBoxSizer(wxVERTICAL); sizer_box->Add(m_ams_control, 0, wxALIGN_CENTER_HORIZONTAL | wxALL, FromDIP(3)); m_ams_control_box->SetBackgroundColour(*wxWHITE); m_ams_control_box->SetSizer(sizer_box); m_ams_control_box->Layout(); m_ams_control_box->Fit(); - sizer->Add(m_ams_control_box, 0, wxALIGN_CENTER_HORIZONTAL | wxALL, FromDIP(0)); - return sizer; + return m_ams_control_box; } -wxBoxSizer* StatusBasePanel::create_filament_group(wxWindow* parent) +wxBoxSizer *StatusBasePanel::create_filament_group(wxWindow *parent) { auto sizer = new wxBoxSizer(wxVERTICAL); @@ -2112,7 +2018,8 @@ wxBoxSizer* StatusBasePanel::create_filament_group(wxWindow* parent) m_title_filament_loading->SetForegroundColour(wxColour(27, 136, 68)); m_title_filament_loading->SetFont(::Label::Body_14); - m_img_filament_loading = new wxStaticBitmap(m_scale_panel, wxID_ANY, create_scaled_bitmap("filament_load_fold", this, 24), wxDefaultPosition, wxSize(FromDIP(24), FromDIP(24)), 0); + m_img_filament_loading = new wxStaticBitmap(m_scale_panel, wxID_ANY, create_scaled_bitmap("filament_load_fold", this, 24), wxDefaultPosition, + wxSize(FromDIP(24), FromDIP(24)), 0); sizer_scale_panel->Add(0, 0, 0, wxLEFT, FromDIP(20)); sizer_scale_panel->Add(m_title_filament_loading, 0, wxALIGN_CENTER, 0); @@ -2122,11 +2029,9 @@ wxBoxSizer* StatusBasePanel::create_filament_group(wxWindow* parent) m_scale_panel->Fit(); m_scale_panel->Hide(); - m_title_filament_loading->Bind(wxEVT_LEFT_DOWN, &StatusBasePanel::expand_filament_loading, this); m_scale_panel->Bind(wxEVT_LEFT_DOWN, &StatusBasePanel::expand_filament_loading, this); - auto sizer_box = new wxBoxSizer(wxVERTICAL); StateColor box_colour(std::pair(*wxWHITE, StateColor::Normal)); @@ -2167,14 +2072,13 @@ wxBoxSizer* StatusBasePanel::create_filament_group(wxWindow* parent) m_button_retry->SetTextColor(btn_text_white); m_button_retry->SetMinSize(wxSize(FromDIP(80), FromDIP(31))); m_button_retry->SetBackgroundColor(btn_bg_white); - //m_button_retry->Hide(); + // m_button_retry->Hide(); m_button_retry->Bind(wxEVT_BUTTON, [this](wxCommandEvent &e) { BOOST_LOG_TRIVIAL(info) << "on_ams_retry"; if (obj) { obj->command_ams_control("resume"); } }); - sizer_box->Add(steps_sizer, 0, wxEXPAND | wxALIGN_LEFT | wxTOP, FromDIP(5)); sizer_box->Add(m_button_retry, 0, wxLEFT, FromDIP(28)); sizer_box->Add(0, 0, 0, wxTOP, FromDIP(5)); @@ -2187,7 +2091,7 @@ wxBoxSizer* StatusBasePanel::create_filament_group(wxWindow* parent) return sizer; } -void StatusBasePanel::expand_filament_loading(wxMouseEvent& e) +void StatusBasePanel::expand_filament_loading(wxMouseEvent &e) { auto tag_show = false; if (m_filament_load_box->IsShown()) { @@ -2198,45 +2102,39 @@ void StatusBasePanel::expand_filament_loading(wxMouseEvent& e) m_img_filament_loading->SetBitmap(create_scaled_bitmap("filament_load_expand", this, 24)); } - if (obj) - { + if (obj) { static int load_img_size = 215; - if (obj->is_series_n()) - { + if (obj->is_series_n()) { m_filament_load_img->SetBitmap(create_scaled_bitmap("filament_load_n_series", this, load_img_size)); - } - else if (obj->is_series_x()) - { + } else if (obj->is_series_x()) { m_filament_load_img->SetBitmap(create_scaled_bitmap("filament_load_x_series", this, load_img_size)); - } - else if (obj->is_series_p()) - { + } else if (obj->is_series_p()) { m_filament_load_img->SetBitmap(create_scaled_bitmap("filament_load_p_series", this, load_img_size)); - } - else if (obj->is_series_o()) - { - const auto& ext_system = obj->GetExtderSystem(); - if (ext_system->GetTotalExtderCount() == 2) - { + } else if (obj->is_series_o()) { + const auto &ext_system = obj->GetExtderSystem(); + if (ext_system->GetTotalExtderCount() == 2) { int cur_extder_id = ext_system->GetCurrentExtderId(); - if (cur_extder_id == MAIN_EXTRUDER_ID) - { - m_filament_load_img->SetBitmap(create_scaled_bitmap("filament_load_o_series_right", this, load_img_size)); - } - else if (cur_extder_id == DEPUTY_EXTRUDER_ID) - { - m_filament_load_img->SetBitmap(create_scaled_bitmap("filament_load_o_series_left", this, load_img_size)); + if (cur_extder_id == MAIN_EXTRUDER_ID) { + if (obj->GetNozzleSystem()->GetNozzleRack()->IsSupported()) + m_filament_load_img->SetBitmap(create_scaled_bitmap("filament_load_o1c_series_right", this, load_img_size)); + else + m_filament_load_img->SetBitmap(create_scaled_bitmap("filament_load_o_series_right", this, load_img_size)); + } else if (cur_extder_id == DEPUTY_EXTRUDER_ID) { + if (obj->GetNozzleSystem()->GetNozzleRack()->IsSupported()) + m_filament_load_img->SetBitmap(create_scaled_bitmap("filament_load_o1c_series_left", this, load_img_size)); + else + m_filament_load_img->SetBitmap(create_scaled_bitmap("filament_load_o_series_left", this, load_img_size)); } } - else - { + + else { m_filament_load_img->SetBitmap(create_scaled_bitmap("filament_load_o_series", this, load_img_size)); } } } m_filament_load_box->Show(tag_show); - ///m_button_retry->Show(tag_show); + /// m_button_retry->Show(tag_show); m_filament_step->Show(tag_show); Layout(); Fit(); @@ -2255,6 +2153,8 @@ void StatusBasePanel::show_ams_group(bool show) wxGetApp().mainframe->m_monitor->Layout(); } + if (show && m_ams_rack_switch->IsShown() && (m_ams_rack_switch->switch_left != true)) { return; } + if (m_ams_control_box->IsShown() != show) { m_ams_control_box->Show(show); m_ams_control->Layout(); @@ -2275,9 +2175,7 @@ void StatusBasePanel::show_filament_load_group(bool show) } // m_scale_panel control the display of m_filament_load_box - if (!show && m_filament_load_box->IsShown()) { - m_filament_load_box->Show(false); - } + if (!show && m_filament_load_box->IsShown()) { m_filament_load_box->Show(false); } auto cur_ext = obj->GetExtderSystem()->GetCurrentExtder(); m_filament_step->SetupSteps(cur_ext ? cur_ext->HasFilamentInExt() : false); @@ -2290,11 +2188,36 @@ void StatusBasePanel::show_filament_load_group(bool show) } } -void StatusPanel::update_camera_state(MachineObject* obj) +void StatusBasePanel::jump_to_Rack() +{ + if (obj && obj->GetNozzleRack()->IsSupported()) { + m_ams_rack_switch->updateState("right"); + m_ams_control_box->Show(false); + m_panel_nozzle_rack->Show(true); + Layout(); + } +} + +void StatusBasePanel::on_ams_rack_switch(wxCommandEvent &e) +{ + if (!m_ams_control_box->IsShown() && e.GetInt() == 1) { + m_ams_control_box->Show(e.GetInt() == 1); + m_panel_nozzle_rack->Show(e.GetInt() == 0); + Layout(); + } else if (!m_panel_nozzle_rack->IsShown() && e.GetInt() == 0) { + m_ams_control_box->Show(e.GetInt() == 1); + m_panel_nozzle_rack->Show(e.GetInt() == 0); + Layout(); + } + + e.Skip(); +} + +void StatusPanel::update_camera_state(MachineObject *obj) { if (!obj) return; - //sdcard + // sdcard auto sdcard_state = obj->GetStorage()->get_sdcard_state(); if (m_last_sdcard != sdcard_state) { if (sdcard_state == DevStorage::NO_SDCARD) { @@ -2314,12 +2237,11 @@ void StatusPanel::update_camera_state(MachineObject* obj) m_panel_monitoring_title->Layout(); } - //recording + // recording if (m_last_recording != (obj->is_recording() ? 1 : 0)) { if (obj->is_recording()) { m_bitmap_recording_img->SetBitmap(m_bitmap_recording_on.bmp()); - } - else { + } else { m_bitmap_recording_img->SetBitmap(m_bitmap_recording_off.bmp()); } m_last_recording = obj->is_recording() ? 1 : 0; @@ -2333,9 +2255,9 @@ void StatusPanel::update_camera_state(MachineObject* obj) /*if (m_bitmap_recording_img->IsShown()) m_bitmap_recording_img->Hide();*/ - //timelapse + // timelapse if (obj->is_support_timelapse) { - if (m_last_timelapse != (obj->is_timelapse() ? 1: 0)) { + if (m_last_timelapse != (obj->is_timelapse() ? 1 : 0)) { if (obj->is_timelapse()) { m_bitmap_timelapse_img->SetBitmap(m_bitmap_timelapse_on.bmp()); } else { @@ -2355,9 +2277,9 @@ void StatusPanel::update_camera_state(MachineObject* obj) } } - //vcamera + // vcamera if (obj->virtual_camera) { - if (m_last_vcamera != (m_media_play_ctrl->IsStreaming() ? 1: 0)) { + if (m_last_vcamera != (m_media_play_ctrl->IsStreaming() ? 1 : 0)) { if (m_media_play_ctrl->IsStreaming()) { m_bitmap_vcamera_img->SetBitmap(m_bitmap_vcamera_on.bmp()); } else { @@ -2377,7 +2299,7 @@ void StatusPanel::update_camera_state(MachineObject* obj) } } - //camera setting + // camera setting if (m_camera_popup && m_camera_popup->IsShown()) { bool show_vcamera = m_media_play_ctrl->IsStreaming(); m_camera_popup->update(show_vcamera); @@ -2395,26 +2317,25 @@ StatusPanel::StatusPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, co m_buttons.push_back(m_bpButton_e_10); m_buttons.push_back(m_bpButton_e_down_10); - obj = nullptr; - m_score_data = new ScoreData; + obj = nullptr; + m_score_data = new ScoreData; m_score_data->rating_id = -1; /* set default values */ m_switch_lamp->SetValue(false); /*m_switch_printing_fan->SetValue(false); m_switch_nozzle_fan->SetValue(false); m_switch_cham_fan->SetValue(false);*/ - //m_switch_fan->SetValue(false); + // m_switch_fan->SetValue(false); /* set default enable state */ m_project_task_panel->enable_partskip_button(nullptr, false); m_project_task_panel->enable_pause_resume_button(false, "resume_disable"); m_project_task_panel->enable_abort_button(false); - Bind(wxEVT_WEBREQUEST_STATE, &StatusPanel::on_webrequest_state, this); Bind(wxCUSTOMEVT_SET_TEMP_FINISH, [this](wxCommandEvent e) { - int id = e.GetInt(); + int id = e.GetInt(); if (id == m_tempCtrl_bed->GetType()) { on_set_bed_temp(); } else if (id == m_tempCtrl_nozzle->GetType()) { @@ -2423,7 +2344,7 @@ StatusPanel::StatusPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, co } else if (e.GetString() == wxString::Format("%d", DEPUTY_EXTRUDER_ID)) { on_set_nozzle_temp(DEPUTY_EXTRUDER_ID); } else { - on_set_nozzle_temp(UNIQUE_EXTRUDER_ID);//there is only one nozzle + on_set_nozzle_temp(UNIQUE_EXTRUDER_ID); // there is only one nozzle } } else if (id == m_tempCtrl_chamber->GetType()) { if (!m_tempCtrl_chamber->IsOnChanging()) { @@ -2434,7 +2355,6 @@ StatusPanel::StatusPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, co } }); - // Connect Events m_project_task_panel->get_bitmap_thumbnail()->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(StatusPanel::refresh_thumbnail_webrequest), NULL, this); m_project_task_panel->get_partskip_button()->Connect(wxEVT_LEFT_DOWN, wxCommandEventHandler(StatusPanel::on_subtask_partskip), NULL, this); @@ -2455,13 +2375,13 @@ StatusPanel::StatusPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, co m_tempCtrl_chamber->Connect(wxEVT_KILL_FOCUS, wxFocusEventHandler(StatusPanel::on_cham_temp_kill_focus), NULL, this); m_tempCtrl_chamber->Connect(wxEVT_SET_FOCUS, wxFocusEventHandler(StatusPanel::on_cham_temp_set_focus), NULL, this); m_switch_lamp->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_lamp_switch), NULL, this); - //m_switch_nozzle_fan->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this); // TODO - //m_switch_printing_fan->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this); - //m_switch_cham_fan->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this); + // m_switch_nozzle_fan->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this); // TODO + // m_switch_printing_fan->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this); + // m_switch_cham_fan->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this); m_switch_fan->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this); // TODO - //m_switch_fan->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this); - //m_switch_fan->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this); + // m_switch_fan->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this); + // m_switch_fan->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this); m_bpButton_xy->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_xy), NULL, this); // TODO m_bpButton_z_10->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_z_up_10), NULL, this); @@ -2482,12 +2402,14 @@ StatusPanel::StatusPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, co Bind(EVT_AMS_ON_SELECTED, &StatusPanel::on_ams_selected, this); Bind(EVT_AMS_ON_FILAMENT_EDIT, &StatusPanel::on_filament_edit, this); Bind(EVT_VAMS_ON_FILAMENT_EDIT, &StatusPanel::on_ext_spool_edit, this); - //Bind(EVT_VAMS_ON_FILAMENT_EDIT, &StatusPanel::on_filament_edit, this); + // Bind(EVT_VAMS_ON_FILAMENT_EDIT, &StatusPanel::on_filament_edit, this); Bind(EVT_AMS_GUIDE_WIKI, &StatusPanel::on_ams_guide, this); Bind(EVT_AMS_RETRY, &StatusPanel::on_ams_retry, this); Bind(EVT_FAN_CHANGED, &StatusPanel::on_fan_changed, this); Bind(EVT_SECONDARY_CHECK_RESUME, &StatusPanel::on_subtask_pause_resume, this); - Bind(EVT_SECONDARY_CHECK_RETRY, [this](auto &e) { if (m_ams_control) { m_ams_control->on_retry(); }}); + Bind(EVT_SECONDARY_CHECK_RETRY, [this](auto &e) { + if (m_ams_control) { m_ams_control->on_retry(); } + }); m_switch_speed->Connect(wxEVT_LEFT_DOWN, wxCommandEventHandler(StatusPanel::on_switch_speed), NULL, this); m_calibration_btn->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_start_calibration), NULL, this); @@ -2522,8 +2444,8 @@ StatusPanel::~StatusPanel() m_switch_printing_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this); m_switch_cham_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this);*/ - //m_switch_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this); - //m_switch_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this); + // m_switch_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this); + // m_switch_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this); m_switch_fan->Disconnect(wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_nozzle_fan_switch), NULL, this); m_bpButton_xy->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_axis_ctrl_xy), NULL, this); @@ -2541,18 +2463,13 @@ StatusPanel::~StatusPanel() m_parts_btn->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_show_parts_options), NULL, this); // remove warning dialogs - if (abort_dlg != nullptr) - delete abort_dlg; + if (abort_dlg != nullptr) delete abort_dlg; - if (ctrl_e_hint_dlg != nullptr) - delete ctrl_e_hint_dlg; + if (ctrl_e_hint_dlg != nullptr) delete ctrl_e_hint_dlg; - if (sdcard_hint_dlg != nullptr) - delete sdcard_hint_dlg; + if (sdcard_hint_dlg != nullptr) delete sdcard_hint_dlg; - if (m_score_data != nullptr) { - delete m_score_data; - } + if (m_score_data != nullptr) { delete m_score_data; } } void StatusPanel::init_scaled_buttons() @@ -2572,13 +2489,14 @@ void StatusPanel::init_scaled_buttons() m_bpButton_e_down_10->SetCornerRadius(FromDIP(12)); } -void StatusPanel::on_market_scoring(wxCommandEvent &event) { +void StatusPanel::on_market_scoring(wxCommandEvent &event) +{ if (obj && obj->is_makeworld_subtask() && obj->rating_info && obj->rating_info->request_successful) { // model is mall model and has rating_id - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": on_market_scoring" ; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": on_market_scoring"; if (m_score_data && m_score_data->rating_id == obj->rating_info->rating_id) { // current score data for model is same as mall model if (m_score_data->star_count != m_project_task_panel->get_star_count()) m_score_data->star_count = m_project_task_panel->get_star_count(); ScoreDialog m_score_dlg(this, m_score_data); - int ret = m_score_dlg.ShowModal(); + int ret = m_score_dlg.ShowModal(); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": old data"; if (ret == wxID_OK) { @@ -2595,7 +2513,7 @@ void StatusPanel::on_market_scoring(wxCommandEvent &event) { m_score_data = new ScoreData(m_score_dlg.get_score_data()); // when user do not submit score, store the data for next opening the score dialog m_project_task_panel->set_star_count(m_score_data->star_count); } else { - int star_count = m_project_task_panel->get_star_count_dirty() ? m_project_task_panel->get_star_count() : obj->rating_info->start_count; + int star_count = m_project_task_panel->get_star_count_dirty() ? m_project_task_panel->get_star_count() : obj->rating_info->start_count; bool success_print = obj->rating_info->success_printed; ScoreDialog m_score_dlg(this, obj->get_modeltask()->design_id, obj->get_modeltask()->model_id, obj->get_modeltask()->profile_id, obj->rating_info->rating_id, success_print, star_count); @@ -2630,34 +2548,33 @@ void StatusPanel::on_market_scoring(wxCommandEvent &event) { void StatusPanel::on_market_retry(wxCommandEvent &event) { if (obj) { - obj->get_model_mall_result_need_retry = true; + obj->get_model_mall_result_need_retry = true; } else { - BOOST_LOG_TRIVIAL(info)<< __FUNCTION__ << "retury failed"; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "retury failed"; } } -void StatusPanel::update_partskip_button(MachineObject *obj) { +void StatusPanel::update_partskip_button(MachineObject *obj) +{ if (!obj) return; auto partskip_button = m_project_task_panel->get_partskip_button(); - if( obj->is_support_partskip ){ + if (obj->is_support_partskip) { partskip_button->Show(); - }else{ + } else { partskip_button->Hide(); } - BOOST_LOG_TRIVIAL(info) << "part skip: is_support_partskip: "<< obj->is_support_partskip; + BOOST_LOG_TRIVIAL(info) << "part skip: is_support_partskip: " << obj->is_support_partskip; } void StatusPanel::on_subtask_partskip(wxCommandEvent &event) { - if (m_partskip_dlg == nullptr) { - m_partskip_dlg = new PartSkipDialog(this->GetParent()); - } + if (m_partskip_dlg == nullptr) { m_partskip_dlg = new PartSkipDialog(this->GetParent()); } auto dm = GUI::wxGetApp().getDeviceManager(); m_partskip_dlg->InitSchedule(dm->get_selected_machine()); BOOST_LOG_TRIVIAL(info) << "part skip: initial part skip dialog."; - if(m_partskip_dlg->ShowModal() == wxID_OK){ + if (m_partskip_dlg->ShowModal() == wxID_OK) { int cnt = m_partskip_dlg->GetAllSkippedPartsNum(); m_project_task_panel->set_part_skipped_count(cnt); m_project_task_panel->set_part_skipped_dirty(5); @@ -2671,8 +2588,7 @@ void StatusPanel::on_subtask_pause_resume(wxCommandEvent &event) if (obj->can_resume()) { BOOST_LOG_TRIVIAL(info) << "monitor: resume current print task dev_id =" << QDTCrossTalk::Crosstalk_DevId(obj->get_dev_id()); obj->command_task_resume(); - } - else { + } else { BOOST_LOG_TRIVIAL(info) << "monitor: pause current print task dev_id =" << QDTCrossTalk::Crosstalk_DevId(obj->get_dev_id()); obj->command_task_pause(); } @@ -2705,10 +2621,7 @@ void StatusPanel::on_subtask_abort(wxCommandEvent &event) abort_dlg->Raise(); } -void StatusPanel::error_info_reset() -{ - m_project_task_panel->error_info_reset(); -} +void StatusPanel::error_info_reset() { m_project_task_panel->error_info_reset(); } void StatusPanel::on_print_error_clean(wxCommandEvent &event) { @@ -2725,10 +2638,11 @@ void StatusPanel::on_webrequest_state(wxWebRequestEvent &evt) BOOST_LOG_TRIVIAL(trace) << "monitor: monitor_panel web request state = " << evt.GetState(); switch (evt.GetState()) { case wxWebRequest::State_Completed: { - if (m_current_print_mode != PrintingTaskType::CALIBRATION ||(m_calib_mode == CalibMode::Calib_Flow_Rate && m_calib_method == CalibrationMethod::CALI_METHOD_MANUAL)) { + if (m_current_print_mode != PrintingTaskType::CALIBRATION || (m_calib_mode == CalibMode::Calib_Flow_Rate && m_calib_method == CalibrationMethod::CALI_METHOD_MANUAL)) { wxImage img(*evt.GetResponse().GetStream()); img_list.insert(std::make_pair(m_request_url, img)); - wxImage resize_img = img.Scale(m_project_task_panel->get_bitmap_thumbnail()->GetSize().x, m_project_task_panel->get_bitmap_thumbnail()->GetSize().y, wxIMAGE_QUALITY_HIGH); + wxImage resize_img = img.Scale(m_project_task_panel->get_bitmap_thumbnail()->GetSize().x, m_project_task_panel->get_bitmap_thumbnail()->GetSize().y, + wxIMAGE_QUALITY_HIGH); m_project_task_panel->set_thumbnail_img(resize_img, ""); m_project_task_panel->set_brightness_value(get_brightness_value(resize_img)); } @@ -2754,7 +2668,7 @@ void StatusPanel::on_webrequest_state(wxWebRequestEvent &evt) } } -void StatusPanel::refresh_thumbnail_webrequest(wxMouseEvent& event) +void StatusPanel::refresh_thumbnail_webrequest(wxMouseEvent &event) { if (!obj) return; if (task_thumbnail_state != ThumbnailState::BROKEN_IMG) return; @@ -2764,26 +2678,20 @@ void StatusPanel::refresh_thumbnail_webrequest(wxMouseEvent& event) if (!m_request_url.IsEmpty()) { web_request = wxWebSession::GetDefault().CreateRequest(this, m_request_url); BOOST_LOG_TRIVIAL(trace) << "monitor: create new webrequest, state = " << web_request.GetState(); - if (web_request.GetState() == wxWebRequest::State_Idle) - web_request.Start(); + if (web_request.GetState() == wxWebRequest::State_Idle) web_request.Start(); BOOST_LOG_TRIVIAL(trace) << "monitor: start new webrequest, state = " << web_request.GetState(); } } } - -bool StatusPanel::is_task_changed(MachineObject* obj) +bool StatusPanel::is_task_changed(MachineObject *obj) { - if (!obj) - return false; + if (!obj) return false; - if (last_subtask != obj->subtask_ - || last_profile_id != obj->profile_id_ - || last_task_id != obj->task_id_ - ) { - last_subtask = obj->subtask_; - last_profile_id = obj->profile_id_; - last_task_id = obj->task_id_; + if (last_subtask != obj->subtask_ || last_profile_id != obj->profile_id_ || last_task_id != obj->task_id_) { + last_subtask = obj->subtask_; + last_profile_id = obj->profile_id_; + last_task_id = obj->task_id_; request_model_info_flag = false; m_project_task_panel->set_star_count_dirty(false); return true; @@ -2793,15 +2701,14 @@ bool StatusPanel::is_task_changed(MachineObject* obj) void StatusPanel::update(MachineObject *obj) { - if (!obj || !obj->is_info_ready()) - { + if (!obj || !obj->is_info_ready()) { m_nozzle_btn_panel->Disable(); return; } - //m_project_task_panel->Freeze(); + // m_project_task_panel->Freeze(); update_subtask(obj); - //m_project_task_panel->Thaw(); + // m_project_task_panel->Thaw(); #if !QDT_RELEASE_TO_PUBLIC auto delay1 = std::chrono::duration_cast(obj->last_utc_time - std::chrono::system_clock::now()).count(); @@ -2828,9 +2735,7 @@ void StatusPanel::update(MachineObject *obj) } /*STUDIO-12573*/ - if (!obj->is_fdm_type()) { - m_switch_lamp->Enable(false); - } + if (!obj->GetInfo()->IsFdmMode()) { m_switch_lamp->Enable(false); } update_temp_ctrl(obj); update_misc_ctrl(obj); @@ -2838,9 +2743,11 @@ void StatusPanel::update(MachineObject *obj) update_ams(obj); update_cali(obj); + update_rack(obj); + if (obj) { - //nozzle ui - //m_button_left_of_extruder->SetSelected(); + // nozzle ui + // m_button_left_of_extruder->SetSelected(); // update extrusion calibration if (m_extrusion_cali_dlg) { @@ -2855,9 +2762,9 @@ void StatusPanel::update(MachineObject *obj) } std::string current_printer_type = obj->printer_type; - bool supports_safety = DevPrinterConfigUtil::support_safety_options(current_printer_type); + bool supports_safety = DevPrinterConfigUtil::support_safety_options(current_printer_type); - DevConfig* config = obj->GetConfig(); + DevConfig *config = obj->GetConfig(); if (config->SupportFirstLayerInspect() || config->SupportAIMonitor() || obj->is_support_build_plate_marker_detect || obj->is_support_auto_recovery_step_loss) { m_options_btn->Show(); @@ -2869,7 +2776,6 @@ void StatusPanel::update(MachineObject *obj) m_options_btn->Hide(); } - if (obj->support_door_open_check()) { if (supports_safety) { m_safety_btn->Show(); @@ -2886,26 +2792,18 @@ void StatusPanel::update(MachineObject *obj) m_parts_btn->Show(); + if (m_panel_control_title) { m_panel_control_title->Layout(); } - if (m_panel_control_title) { - m_panel_control_title->Layout(); - } - - if (!obj->dev_connection_type.empty()) { + if (!obj->connection_type().empty()) { auto iter_connect_type = m_print_connect_types.find(obj->get_dev_id()); if (iter_connect_type != m_print_connect_types.end()) { - if (iter_connect_type->second != obj->dev_connection_type) { + if (iter_connect_type->second != obj->connection_type()) { + if (iter_connect_type->second == "lan" && obj->connection_type() == "cloud") { m_print_connect_types[obj->get_dev_id()] = obj->connection_type(); } - if (iter_connect_type->second == "lan" && obj->dev_connection_type == "cloud") { - m_print_connect_types[obj->get_dev_id()] = obj->dev_connection_type; - } - - if (iter_connect_type->second == "cloud" && obj->dev_connection_type == "lan") { - m_print_connect_types[obj->get_dev_id()] = obj->dev_connection_type; - } + if (iter_connect_type->second == "cloud" && obj->connection_type() == "lan") { m_print_connect_types[obj->get_dev_id()] = obj->connection_type(); } } } - m_print_connect_types[obj->get_dev_id()] = obj->dev_connection_type; + m_print_connect_types[obj->get_dev_id()] = obj->connection_type(); } update_error_message(); @@ -2913,16 +2811,17 @@ void StatusPanel::update(MachineObject *obj) update_camera_state(obj); - //m_machine_ctrl_panel->Thaw(); + // m_machine_ctrl_panel->Thaw(); } -void StatusPanel::show_recenter_dialog() { +void StatusPanel::show_recenter_dialog() +{ RecenterDialog dlg(this); - if (dlg.ShowModal() == wxID_OK) - obj->command_go_home(); + if (dlg.ShowModal() == wxID_OK) { + if (obj) { obj->GetAxis()->Ctrl_GoHome(); } + } } - void StatusPanel::update_error_message() { if (!obj) return; @@ -2936,12 +2835,12 @@ void StatusPanel::update_error_message() if (m_print_error_dlg) { delete m_print_error_dlg; } /* show device error message*/ - m_print_error_dlg = new DeviceErrorDialog(obj, this); + m_print_error_dlg = new DeviceErrorDialog(obj, this); wxString error_msg = m_print_error_dlg->show_error_code(obj->print_error); - BOOST_LOG_TRIVIAL(info) << "print error: device error code = "<< obj->print_error; + BOOST_LOG_TRIVIAL(info) << "print error: device error code = " << obj->print_error; /* show error message on task panel */ - if(!error_msg.IsEmpty()) { m_project_task_panel->show_error_msg(error_msg); } + if (!error_msg.IsEmpty()) { m_project_task_panel->show_error_msg(error_msg); } } last_error = obj->print_error; @@ -2958,10 +2857,10 @@ void StatusPanel::show_printing_status(bool ctrl_area, bool temp_area) m_bpButton_e_10->Enable(false); m_bpButton_e_down_10->Enable(false); - m_bpButton_z_10->SetIcon("monitor_bed_up_disable"); - m_bpButton_z_1->SetIcon("monitor_bed_up_disable"); - m_bpButton_z_down_1->SetIcon("monitor_bed_down_disable"); - m_bpButton_z_down_10->SetIcon("monitor_bed_down_disable"); + m_bpButton_z_10->SetIcon("monitor_bed_up_disable"); + m_bpButton_z_1->SetIcon("monitor_bed_up_disable"); + m_bpButton_z_down_1->SetIcon("monitor_bed_down_disable"); + m_bpButton_z_down_10->SetIcon("monitor_bed_down_disable"); m_bpButton_e_10->SetIcon("monitor_extruder_up_disable"); m_bpButton_e_down_10->SetIcon("monitor_extrduer_down_disable"); @@ -3018,15 +2917,13 @@ void StatusPanel::update_temp_ctrl(MachineObject *obj) { if (!obj) return; - DevBed* bed = obj->GetBed(); - int bed_cur_temp = bed->GetBedTemp(); - int bed_target_temp = bed->GetBedTempTarget(); + DevBed *bed = obj->GetBed(); + int bed_cur_temp = bed->GetBedTemp(); + int bed_target_temp = bed->GetBedTempTarget(); m_tempCtrl_bed->SetCurrTemp((int) bed_cur_temp); auto limit = obj->get_bed_temperature_limit(); - if (obj->bed_temp_range.size() > 1) { - limit = obj->bed_temp_range[1]; - } + if (obj->bed_temp_range.size() > 1) { limit = obj->bed_temp_range[1]; } m_tempCtrl_bed->SetMaxTemp(limit); if (obj->nozzle_temp_range.size() >= 2) { @@ -3051,9 +2948,8 @@ void StatusPanel::update_temp_ctrl(MachineObject *obj) } bool to_update_layout = false; - int nozzle_num = obj->GetExtderSystem()->GetTotalExtderCount(); - if (nozzle_num == 1) - { + int nozzle_num = obj->GetExtderSystem()->GetTotalExtderCount(); + if (nozzle_num == 1) { m_tempCtrl_nozzle->SetCurrTemp(obj->GetExtderSystem()->GetNozzleTempCurrent(MAIN_EXTRUDER_ID)); m_tempCtrl_nozzle->SetCurrType(TEMP_OF_NORMAL_TYPE); @@ -3061,14 +2957,11 @@ void StatusPanel::update_temp_ctrl(MachineObject *obj) m_tempCtrl_nozzle_deputy->SetLabel(TEMP_BLANK_STR); m_tempCtrl_nozzle_deputy->Hide(); - if (m_tempCtrl_nozzle->GetMinSize() != TEMP_CTRL_MIN_SIZE_ALIGN_ONE_ICON) - { + if (m_tempCtrl_nozzle->GetMinSize() != TEMP_CTRL_MIN_SIZE_ALIGN_ONE_ICON) { to_update_layout = true; m_tempCtrl_nozzle->SetMinSize(TEMP_CTRL_MIN_SIZE_ALIGN_ONE_ICON); } - } - else if (nozzle_num == 2) - { + } else if (nozzle_num == 2) { m_tempCtrl_nozzle->SetCurrType(TEMP_OF_MAIN_NOZZLE_TYPE); m_tempCtrl_nozzle->SetCurrTemp(obj->GetExtderSystem()->GetNozzleTempCurrent(MAIN_EXTRUDER_ID)); m_tempCtrl_nozzle->Show(); @@ -3077,8 +2970,7 @@ void StatusPanel::update_temp_ctrl(MachineObject *obj) m_tempCtrl_nozzle_deputy->SetCurrTemp(obj->GetExtderSystem()->GetNozzleTempCurrent(DEPUTY_EXTRUDER_ID)); m_tempCtrl_nozzle_deputy->Show(); - if (m_tempCtrl_nozzle->GetMinSize() != TEMP_CTRL_MIN_SIZE_ALIGN_TWO_ICON) - { + if (m_tempCtrl_nozzle->GetMinSize() != TEMP_CTRL_MIN_SIZE_ALIGN_TWO_ICON) { to_update_layout = true; m_tempCtrl_nozzle->SetMinSize(TEMP_CTRL_MIN_SIZE_ALIGN_TWO_ICON); } @@ -3089,16 +2981,12 @@ void StatusPanel::update_temp_ctrl(MachineObject *obj) } else { if (!nozzle_temp_input) { auto main_extder = obj->GetExtderSystem()->GetExtderById(MAIN_EXTRUDER_ID); - if (main_extder) - { + if (main_extder) { m_tempCtrl_nozzle->SetTagTemp(main_extder->GetTargetTemp()); - m_tempCtrl_nozzle->SetCurrTemp((int)main_extder->GetCurrentTemp()); - if (main_extder->GetTargetTemp() - main_extder->GetCurrentTemp() > TEMP_THRESHOLD_VAL) - { + m_tempCtrl_nozzle->SetCurrTemp((int) main_extder->GetCurrentTemp()); + if (main_extder->GetTargetTemp() - main_extder->GetCurrentTemp() > TEMP_THRESHOLD_VAL) { m_tempCtrl_nozzle->SetIconActive(); - } - else - { + } else { m_tempCtrl_nozzle->SetIconNormal(); } } @@ -3107,20 +2995,15 @@ void StatusPanel::update_temp_ctrl(MachineObject *obj) if (m_temp_nozzle_deputy_timeout > 0) { m_temp_nozzle_deputy_timeout--; - } - else { + } else { if (!nozzle_temp_input && nozzle_num >= 2) { auto deputy_extder = obj->GetExtderSystem()->GetExtderById(DEPUTY_EXTRUDER_ID); - if (deputy_extder) - { + if (deputy_extder) { m_tempCtrl_nozzle_deputy->SetTagTemp(deputy_extder->GetTargetTemp()); - m_tempCtrl_nozzle_deputy->SetCurrTemp((int)deputy_extder->GetCurrentTemp()); - if (deputy_extder->GetTargetTemp() - deputy_extder->GetCurrentTemp() > TEMP_THRESHOLD_VAL) - { + m_tempCtrl_nozzle_deputy->SetCurrTemp((int) deputy_extder->GetCurrentTemp()); + if (deputy_extder->GetTargetTemp() - deputy_extder->GetCurrentTemp() > TEMP_THRESHOLD_VAL) { m_tempCtrl_nozzle_deputy->SetIconActive(); - } - else - { + } else { m_tempCtrl_nozzle_deputy->SetIconNormal(); } } @@ -3128,39 +3011,30 @@ void StatusPanel::update_temp_ctrl(MachineObject *obj) } // support current temp for chamber - if (obj->get_printer_series() == PrinterSeries::SERIES_X1) - { - m_tempCtrl_chamber->SetCurrTemp(obj->chamber_temp); - } - else - { + const auto &chamber = obj->GetChamber(); + if (chamber->SupportChamberTempDisplay()) { + m_tempCtrl_chamber->SetCurrTemp(chamber->GetChamberTemp()); + } else { m_tempCtrl_chamber->SetCurrTemp(TEMP_BLANK_STR); } // support edit chamber temp - DevConfig* config = obj->GetConfig(); - if (config->SupportChamberEdit()) - { + if (chamber->SupportChamberEdit()) { m_tempCtrl_chamber->SetReadOnly(false); m_tempCtrl_chamber->Enable(); - m_tempCtrl_chamber->SetMinTemp(config->GetChamberTempEditMin()); - m_tempCtrl_chamber->SetMaxTemp(config->GetChamberTempEditMax()); + m_tempCtrl_chamber->SetMinTemp(chamber->GetChamberTempEditMin()); + m_tempCtrl_chamber->SetMaxTemp(chamber->GetChamberTempEditMax()); m_tempCtrl_chamber->AddTemp(0); // zero is default temp wxCursor cursor(wxCURSOR_IBEAM); m_tempCtrl_chamber->GetTextCtrl()->SetCursor(cursor); - if (m_temp_chamber_timeout > 0) - { + if (m_temp_chamber_timeout > 0) { m_temp_chamber_timeout--; - } - else - { + } else { /*update temprature if not input temp target*/ - if (!cham_temp_input) { m_tempCtrl_chamber->SetTagTemp(obj->chamber_temp_target); } + if (!cham_temp_input) { m_tempCtrl_chamber->SetTagTemp(chamber->GetChamberTempTarget()); } } - } - else - { + } else { m_tempCtrl_chamber->SetReadOnly(true); m_tempCtrl_chamber->SetTagTemp(TEMP_BLANK_STR); @@ -3168,23 +3042,18 @@ void StatusPanel::update_temp_ctrl(MachineObject *obj) m_tempCtrl_chamber->GetTextCtrl()->SetCursor(cursor); } - if ((obj->chamber_temp_target - obj->chamber_temp) >= TEMP_THRESHOLD_VAL) { + if ((chamber->GetChamberTempTarget() - chamber->GetChamberTemp()) >= TEMP_THRESHOLD_VAL) { m_tempCtrl_chamber->SetIconActive(); - } - else { + } else { m_tempCtrl_chamber->SetIconNormal(); } - if (to_update_layout) - { - this->Layout(); - } + if (to_update_layout) { this->Layout(); } } void StatusPanel::update_misc_ctrl(MachineObject *obj) { - auto get_extder_shown_state = [](bool ext_has_filament) -> ExtruderState - { + auto get_extder_shown_state = [](bool ext_has_filament) -> ExtruderState { // no data to distinguish ExtruderState::UNLOAD or LOAD, use LOAD png as default return ext_has_filament ? ExtruderState::FILLED_LOAD : ExtruderState::EMPTY_LOAD; }; @@ -3193,8 +3062,8 @@ void StatusPanel::update_misc_ctrl(MachineObject *obj) /*extder*/ auto extder_system = obj->GetExtderSystem(); - m_nozzle_num = extder_system->GetTotalExtderCount(); - int select_index = m_nozzle_num - 1; + m_nozzle_num = extder_system->GetTotalExtderCount(); + int select_index = m_nozzle_num - 1; if (m_nozzle_num >= 2) { m_extruder_book->SetSelection(m_nozzle_num); @@ -3203,26 +3072,20 @@ void StatusPanel::update_misc_ctrl(MachineObject *obj) m_nozzle_btn_panel->Show(); m_extruderImage[select_index]->setExtruderCount(m_nozzle_num); - if (obj->GetExtderSystem()->GetTotalExtderSize() > 1) - { + if (obj->GetExtderSystem()->GetTotalExtderSize() > 1) { m_extruderImage[select_index]->update(get_extder_shown_state(obj->GetExtderSystem()->HasFilamentInExt(0)), get_extder_shown_state(obj->GetExtderSystem()->HasFilamentInExt(1))); } /*current*/ /*update when extder position changed or the machine changed*/ - if (obj->GetExtderSystem()->GetCurrentExtderId() == 0xf) - { + if (obj->GetExtderSystem()->GetCurrentExtderId() == 0xf) { m_extruderImage[select_index]->setExtruderUsed(""); m_nozzle_btn_panel->updateState(""); - } - else if (obj->GetExtderSystem()->GetCurrentExtderId() == MAIN_EXTRUDER_ID) - { + } else if (obj->GetExtderSystem()->GetCurrentExtderId() == MAIN_EXTRUDER_ID) { m_extruderImage[select_index]->setExtruderUsed("right"); m_nozzle_btn_panel->updateState("right"); - } - else if (obj->GetExtderSystem()->GetCurrentExtderId() == DEPUTY_EXTRUDER_ID) - { + } else if (obj->GetExtderSystem()->GetCurrentExtderId() == DEPUTY_EXTRUDER_ID) { m_extruderImage[select_index]->setExtruderUsed("left"); m_nozzle_btn_panel->updateState("left"); } @@ -3231,14 +3094,10 @@ void StatusPanel::update_misc_ctrl(MachineObject *obj) /*enable status*/ /* Can do switch while printing pause STUDIO-9789*/ - if ((obj->is_in_printing() && !obj->is_in_printing_pause()) || - obj->ams_status_main == AMS_STATUS_MAIN_FILAMENT_CHANGE || - obj->targ_nozzle_id_from_pc != INVALID_EXTRUDER_ID) - { + if ((obj->is_in_printing() && !obj->is_in_printing_pause()) || obj->ams_status_main == AMS_STATUS_MAIN_FILAMENT_CHANGE || + obj->targ_nozzle_id_from_pc != INVALID_EXTRUDER_ID) { m_nozzle_btn_panel->Disable(); - } - else - { + } else { m_nozzle_btn_panel->Enable(); } } else { @@ -3246,8 +3105,7 @@ void StatusPanel::update_misc_ctrl(MachineObject *obj) m_extruder_book->SetSelection(m_nozzle_num); m_extruderImage[select_index]->setExtruderCount(m_nozzle_num); - if (extder_system->GetTotalExtderSize() > 0) - { + if (extder_system->GetTotalExtderSize() > 0) { ExtruderState shown_state = get_extder_shown_state(extder_system->HasFilamentInExt(0)); m_extruderImage[select_index]->update(shown_state); } @@ -3255,10 +3113,10 @@ void StatusPanel::update_misc_ctrl(MachineObject *obj) /*switch extder*/ m_extruder_switching_status->updateBy(obj); - m_extruder_label->Show(!m_extruder_switching_status->has_content_shown());/*hide the label if there are shown infos from m_extruder_switching_status*/ + m_extruder_label->Show(!m_extruder_switching_status->has_content_shown()); /*hide the label if there are shown infos from m_extruder_switching_status*/ /*other*/ - if (obj->is_core_xy()) { + if (obj->GetAxis()->IsArchCoreXY()) { m_staticText_z_tip->SetLabel(_L("Bed")); } else { m_staticText_z_tip->SetLabel("Z"); @@ -3267,24 +3125,20 @@ void StatusPanel::update_misc_ctrl(MachineObject *obj) // update extruder icon update_extruder_status(obj); - if (obj->is_fdm_type()) { - if (!m_fan_panel->IsShown()) - m_fan_panel->Show(); + if (obj->GetInfo()->IsFdmMode()) { + if (!m_fan_panel->IsShown()) m_fan_panel->Show(); bool is_suppt_part_fun = true; bool is_suppt_aux_fun = obj->GetFan()->GetSupportAuxFanData(); bool is_suppt_cham_fun = obj->GetFan()->GetSupportChamberFan(); if (m_fan_control_popup) { m_fan_control_popup->update_fan_data(obj); } } else { - if (m_fan_panel->IsShown()) { - m_fan_panel->Hide(); - } - if (m_fan_control_popup && m_fan_control_popup->IsShown()) - m_fan_control_popup->Hide(); + if (m_fan_panel->IsShown()) { m_fan_panel->Hide(); } + if (m_fan_control_popup && m_fan_control_popup->IsShown()) m_fan_control_popup->Hide(); } obj->is_series_o() ? m_switch_fan->UseTextAirCondition() : m_switch_fan->UseTextFan(); - //update cham fan + // update cham fan /*other*/ bool light_on = obj->GetLamp()->IsChamberLightOn(); @@ -3301,13 +3155,13 @@ void StatusPanel::update_misc_ctrl(MachineObject *obj) speed_lvl_timeout--; else { // update speed - this->speed_lvl = obj->GetPrintingSpeedLevel(); - wxString text_speed = wxString::Format("%d%%", obj->printing_speed_mag); - m_switch_speed->SetLabels(text_speed, text_speed); + this->speed_lvl = obj->GetPrintingSpeedLevel(); + wxString text_speed = wxString::Format("%d%%", obj->printing_speed_mag); + m_switch_speed->SetLabels(text_speed, text_speed); } } -void StatusPanel::update_extruder_status(MachineObject* obj) +void StatusPanel::update_extruder_status(MachineObject *obj) { if (!obj) return; } @@ -3315,9 +3169,7 @@ void StatusPanel::update_extruder_status(MachineObject* obj) void StatusPanel::update_ams(MachineObject *obj) { // update obj in sub dlg - if (m_ams_setting_dlg && m_ams_setting_dlg->IsShown()) { - m_ams_setting_dlg->UpdateByObj(obj); - } + if (m_ams_setting_dlg && m_ams_setting_dlg->IsShown()) { m_ams_setting_dlg->UpdateByObj(obj); } if (m_filament_setting_dlg) { m_filament_setting_dlg->obj = obj; } if (obj && (obj->last_cali_version != obj->cali_version) && obj->is_security_control_ready()) { @@ -3329,12 +3181,10 @@ void StatusPanel::update_ams(MachineObject *obj) CalibUtils::emit_get_PA_calib_infos(cali_info); } - bool is_support_virtual_tray = obj->ams_support_virtual_tray; - bool is_support_filament_backup = obj->is_support_filament_backup; + bool is_support_virtual_tray = obj->ams_support_virtual_tray; + bool is_support_filament_backup = obj->is_support_filament_backup; - if (obj && obj->is_security_control_ready()) { - obj->check_ams_filament_valid(); - } + if (obj && obj->is_security_control_ready()) { obj->check_ams_filament_valid(); } AMSModel ams_mode = AMSModel::GENERIC_AMS; if ((obj->is_enable_np || obj->is_enable_ams_np) && obj->GetFilaSystem()->GetAmsList().size() > 0) { @@ -3360,7 +3210,7 @@ void StatusPanel::update_ams(MachineObject *obj) m_ams_control->SetAmsModel(ams_mode, ams_mode); m_filament_step->SetAmsModel(ams_mode, ams_mode); show_ams_group(true); - //show_filament_load_group(true); + // show_filament_load_group(true); if (obj->GetFilaSystem()->GetAmsList().empty() || obj->ams_exist_bits == 0) { m_ams_control->show_auto_refill(false); @@ -3369,16 +3219,15 @@ void StatusPanel::update_ams(MachineObject *obj) } } - //if (is_support_virtual_tray) m_ams_control->update_vams_kn_value(obj->vt_slot[0], obj); + // if (is_support_virtual_tray) m_ams_control->update_vams_kn_value(obj->vt_slot[0], obj); if (m_filament_setting_dlg) m_filament_setting_dlg->update(); - std::vector ams_info; - const auto& ams_list = obj->GetFilaSystem()->GetAmsList(); + const auto &ams_list = obj->GetFilaSystem()->GetAmsList(); for (auto ams = ams_list.begin(); ams != ams_list.end(); ams++) { AMSinfo info; info.ams_id = ams->first; - if (ams->second->IsExist() && info.parse_ams_info(obj, ams->second, false, obj->is_support_ams_humidity)) { + if (ams->second->IsExist() && info.parse_ams_info(obj, ams->second, obj->GetFilaSystem()->IsDetectRemainEnabled(), obj->is_support_ams_humidity)) { ams_info.push_back(info); } } @@ -3408,9 +3257,10 @@ void StatusPanel::update_ams(MachineObject *obj) if (obj->GetExtderSystem()->GetCurrentAmsId() == std::to_string(VIRTUAL_TRAY_MAIN_ID)) is_vt_tray = true; // set segment 1, 2 - //if (!obj->is_enable_np) { + // if (!obj->is_enable_np) { // if (obj->m_tray_now == std::to_string(255) || obj->m_tray_now == std::to_string(254)) { - // m_ams_control->SetAmsStep(obj->m_extder_data.extders[MAIN_NOZZLE_ID].snow.ams_id, obj->m_extder_data.extders[MAIN_NOZZLE_ID].snow.slot_id, AMSPassRoadType::AMS_ROAD_TYPE_UNLOAD, AMSPassRoadSTEP::AMS_ROAD_STEP_NONE); + // m_ams_control->SetAmsStep(obj->m_extder_data.extders[MAIN_NOZZLE_ID].snow.ams_id, obj->m_extder_data.extders[MAIN_NOZZLE_ID].snow.slot_id, + // AMSPassRoadType::AMS_ROAD_TYPE_UNLOAD, AMSPassRoadSTEP::AMS_ROAD_STEP_NONE); // } else { // /*if (obj->m_tray_now != "255" && obj->is_filament_at_extruder() && !obj->m_tray_id.empty()) { // m_ams_control->SetAmsStep(obj->m_extder_data.extders[MAIN_NOZZLE_ID].snow.ams_id, obj->m_extder_data.extders[MAIN_NOZZLE_ID].snow.slot_id, @@ -3429,14 +3279,15 @@ void StatusPanel::update_ams(MachineObject *obj) // m_ams_control->SetExtruder(obj->is_filament_at_extruder(), obj->m_extder_data.extders[MAIN_NOZZLE_ID].snow.ams_id, obj->m_extder_data.extders[MAIN_NOZZLE_ID].snow.slot_id); //} else { - /*right*/ + /*right*/ if (obj->GetExtderSystem()->GetTotalExtderCount() > 0) { auto ext = obj->GetExtderSystem()->GetExtderById(MAIN_EXTRUDER_ID); if (ext->HasFilamentInExt()) { if (ext->GetSlotNow().ams_id == std::to_string(VIRTUAL_TRAY_MAIN_ID) || ext->GetSlotNow().ams_id == std::to_string(VIRTUAL_TRAY_DEPUTY_ID)) { m_ams_control->SetAmsStep(ext->GetSlotNow().ams_id, "0", AMSPassRoadType::AMS_ROAD_TYPE_LOAD, AMSPassRoadSTEP::AMS_ROAD_STEP_COMBO_LOAD_STEP3); } else { - m_ams_control->SetAmsStep(ext->GetSlotNow().ams_id, ext->GetSlotNow().slot_id, AMSPassRoadType::AMS_ROAD_TYPE_LOAD, AMSPassRoadSTEP::AMS_ROAD_STEP_COMBO_LOAD_STEP2); + m_ams_control->SetAmsStep(ext->GetSlotNow().ams_id, ext->GetSlotNow().slot_id, AMSPassRoadType::AMS_ROAD_TYPE_LOAD, + AMSPassRoadSTEP::AMS_ROAD_STEP_COMBO_LOAD_STEP2); } } else { m_ams_control->SetAmsStep(ext->GetSlotNow().ams_id, ext->GetSlotNow().slot_id, AMSPassRoadType::AMS_ROAD_TYPE_UNLOAD, AMSPassRoadSTEP::AMS_ROAD_STEP_NONE); @@ -3451,7 +3302,8 @@ void StatusPanel::update_ams(MachineObject *obj) if (ext->GetSlotNow().ams_id == std::to_string(VIRTUAL_TRAY_MAIN_ID) || ext->GetSlotNow().ams_id == std::to_string(VIRTUAL_TRAY_DEPUTY_ID)) { m_ams_control->SetAmsStep(ext->GetSlotNow().ams_id, "0", AMSPassRoadType::AMS_ROAD_TYPE_LOAD, AMSPassRoadSTEP::AMS_ROAD_STEP_COMBO_LOAD_STEP3); } else { - m_ams_control->SetAmsStep(ext->GetSlotNow().ams_id, ext->GetSlotNow().slot_id, AMSPassRoadType::AMS_ROAD_TYPE_LOAD, AMSPassRoadSTEP::AMS_ROAD_STEP_COMBO_LOAD_STEP2); + m_ams_control->SetAmsStep(ext->GetSlotNow().ams_id, ext->GetSlotNow().slot_id, AMSPassRoadType::AMS_ROAD_TYPE_LOAD, + AMSPassRoadSTEP::AMS_ROAD_STEP_COMBO_LOAD_STEP2); } } else { m_ams_control->SetAmsStep(ext->GetSlotNow().ams_id, ext->GetSlotNow().slot_id, AMSPassRoadType::AMS_ROAD_TYPE_UNLOAD, AMSPassRoadSTEP::AMS_ROAD_STEP_NONE); @@ -3461,7 +3313,7 @@ void StatusPanel::update_ams(MachineObject *obj) update_filament_loading_panel(obj); - const auto& amslist = obj->GetFilaSystem()->GetAmsList(); + const auto &amslist = obj->GetFilaSystem()->GetAmsList(); for (auto ams_it = amslist.begin(); ams_it != amslist.end(); ams_it++) { std::string ams_id = ams_it->first; try { @@ -3496,10 +3348,10 @@ void StatusPanel::update_ams_control_state(std::string ams_id, std::string slot_ wxString load_error_info, unload_error_info; if (obj->is_in_printing() && !obj->can_resume()) { - load_error_info = _L("The printer is busy on other print job"); + load_error_info = _L("The printer is busy on other print job"); unload_error_info = _L("The printer is busy on other print job"); } else if (obj->can_resume() && !devPrinterUtil::IsVirtualSlot(ams_id)) { - load_error_info = _L("When printing is paused, filament loading and unloading are only supported for external slots."); + load_error_info = _L("When printing is paused, filament loading and unloading are only supported for external slots."); unload_error_info = _L("When printing is paused, filament loading and unloading are only supported for external slots."); } else { /*switch now*/ @@ -3512,7 +3364,7 @@ void StatusPanel::update_ams_control_state(std::string ams_id, std::string slot_ } if (in_switch_filament) { - load_error_info = _L("Current extruder is busy changing filament"); + load_error_info = _L("Current extruder is busy changing filament"); unload_error_info = _L("Current extruder is busy changing filament"); } @@ -3521,34 +3373,22 @@ void StatusPanel::update_ams_control_state(std::string ams_id, std::string slot_ unload_error_info = _L("Choose an BOX slot then press \"Load\" or \"Unload\" button to automatically load or unload filaments."); } else if (ams_id == std::to_string(VIRTUAL_TRAY_MAIN_ID) || ams_id == std::to_string(VIRTUAL_TRAY_DEPUTY_ID)) { for (auto ext : obj->GetExtderSystem()->GetExtruders()) { - if (ext.GetSlotNow().ams_id == ams_id && ext.GetSlotNow().slot_id == slot_id) - { - load_error_info = _L("Current slot has alread been loaded"); - } + if (ext.GetSlotNow().ams_id == ams_id && ext.GetSlotNow().slot_id == slot_id) { load_error_info = _L("Current slot has alread been loaded"); } } } else { for (auto ext : obj->GetExtderSystem()->GetExtruders()) { - if (ext.GetSlotNow().ams_id == ams_id && ext.GetSlotNow().slot_id == slot_id) - { - load_error_info = _L("Current slot has alread been loaded"); - } + if (ext.GetSlotNow().ams_id == ams_id && ext.GetSlotNow().slot_id == slot_id) { load_error_info = _L("Current slot has alread been loaded"); } } /*empty*/ auto ams_item = obj->GetFilaSystem()->GetAmsById(ams_id); - if (!ams_item) - { + if (!ams_item) { load_error_info = _L("Choose an AMS slot then press \"Load\" or \"Unload\" button to automatically load or unload filaments."); - } - else - { + } else { auto tray_item = ams_item->GetTray(slot_id); - if (!tray_item) - { + if (!tray_item) { load_error_info = _L("Choose an AMS slot then press \"Load\" or \"Unload\" button to automatically load or unload filaments."); - } - else if (!tray_item->is_exists) - { + } else if (!tray_item->is_exists) { load_error_info = _L("The selected slot is empty."); } } @@ -3556,7 +3396,7 @@ void StatusPanel::update_ams_control_state(std::string ams_id, std::string slot_ } m_ams_control->EnableLoadFilamentBtn(load_error_info.empty(), ams_id, slot_id, load_error_info); - m_ams_control->EnableUnLoadFilamentBtn(unload_error_info.empty(), ams_id, slot_id,unload_error_info); + m_ams_control->EnableUnLoadFilamentBtn(unload_error_info.empty(), ams_id, slot_id, unload_error_info); } void StatusPanel::update_cali(MachineObject *obj) @@ -3564,7 +3404,7 @@ void StatusPanel::update_cali(MachineObject *obj) if (!obj) return; // disable calibration button in 2D - if (!obj->is_fdm_type()) { + if (!obj->GetInfo()->IsFdmMode()) { m_calibration_btn->SetToolTip(_L("Printer 2D mode does not support 3D calibration")); m_calibration_btn->SetLabel(_L("Calibration")); m_calibration_btn->Disable(); @@ -3593,14 +3433,24 @@ void StatusPanel::update_cali(MachineObject *obj) } } -void StatusPanel::update_calib_bitmap() { - m_current_print_mode = PrintingTaskType::NOT_CLEAR; //printing task might be changed when updating. +void StatusPanel::update_calib_bitmap() +{ + m_current_print_mode = PrintingTaskType::NOT_CLEAR; // printing task might be changed when updating. if (calib_bitmap != nullptr) { delete calib_bitmap; calib_bitmap = nullptr; } } +void StatusPanel::update_market_scoring(bool show) +{ + if (m_project_task_panel->is_market_scoring_show() != show) + { + m_project_task_panel->market_scoring_show(show); + Layout(); + } +} + void StatusPanel::update_basic_print_data(bool def) { if (def) { @@ -3610,8 +3460,7 @@ void StatusPanel::update_basic_print_data(bool def) wxString weight = wxString::Format("%.2fg", obj->slice_info->weight); m_project_task_panel->show_priting_use_info(true, prediction, weight); - } - else { + } else { m_project_task_panel->show_priting_use_info(false, "0m", "0g"); } } @@ -3620,13 +3469,10 @@ void StatusPanel::update_model_info() { auto get_subtask_fn = [this](QDTModelTask* subtask) { CallAfter([this, subtask]() { - if (obj && obj->subtask_id_ == subtask->task_id) { - obj->set_modeltask(subtask); - } + if (obj && obj->subtask_id_ == subtask->task_id) { obj->set_modeltask(subtask); } }); }; - if (wxGetApp().getAgent() && obj) { QDTSubTask* curr_task = obj->get_subtask(); if (curr_task) { @@ -3634,10 +3480,8 @@ void StatusPanel::update_model_info() if (!curr_model_task && !request_model_info_flag) { curr_model_task = new QDTModelTask(); curr_model_task->task_id = curr_task->task_id; - request_model_info_flag = true; - if (!curr_model_task->task_id.empty() && curr_model_task->task_id.compare("0") != 0) { - wxGetApp().getAgent()->get_subtask(curr_model_task, get_subtask_fn); - } + request_model_info_flag = true; + if (!curr_model_task->task_id.empty() && curr_model_task->task_id.compare("0") != 0) { wxGetApp().getAgent()->get_subtask(curr_model_task, get_subtask_fn); } } } } @@ -3649,20 +3493,19 @@ void StatusPanel::update_subtask(MachineObject *obj) if (m_current_print_mode != PRINGINT) { if (calib_bitmap == nullptr) { m_calib_mode = get_obj_calibration_mode(obj, m_calib_method, cali_stage); - if (m_calib_mode == CalibMode::Calib_None) - m_current_print_mode = PRINGINT; + if (m_calib_mode == CalibMode::Calib_None) m_current_print_mode = PRINGINT; // the printing task is calibrattion, not normal printing. else if (m_calib_mode != CalibMode::Calib_None) { m_current_print_mode = CALIBRATION; - auto get_bitmap = [](wxString& png_path, int width, int height) { + auto get_bitmap = [](wxString &png_path, int width, int height) { wxImage image(width, height); image.LoadFile(png_path, wxBITMAP_TYPE_PNG); image = image.Scale(width, height, wxIMAGE_QUALITY_NORMAL); return wxBitmap(image); }; wxString png_path = ""; - int width = m_project_task_panel->get_bitmap_thumbnail()->GetSize().x; - int height = m_project_task_panel->get_bitmap_thumbnail()->GetSize().y; + int width = m_project_task_panel->get_bitmap_thumbnail()->GetSize().x; + int height = m_project_task_panel->get_bitmap_thumbnail()->GetSize().y; if (m_calib_method == CALI_METHOD_AUTO || m_calib_method == CalibrationMethod::CALI_METHOD_NEW_AUTO) { std::string image_name = obj->get_auto_pa_cali_thumbnail_img_str(); if (m_calib_mode == CalibMode::Calib_PA_Line) { @@ -3670,36 +3513,31 @@ void StatusPanel::update_subtask(MachineObject *obj) int cur_ext_id = obj->GetExtderSystem()->GetCurrentExtderId(); if (cur_ext_id == 0) { image_name += "_right"; - } - else { + } else { image_name += "_left"; } } png_path = (boost::format("%1%/images/%2%.png") % resources_dir() % image_name).str(); - } - else if (m_calib_mode == CalibMode::Calib_Flow_Rate) { + } else if (m_calib_mode == CalibMode::Calib_Flow_Rate) { png_path = (boost::format("%1%/images/flow_rate_calibration_auto.png") % resources_dir()).str(); } - } - else if (m_calib_method == CALI_METHOD_MANUAL) { - if (m_calib_mode== CalibMode::Calib_PA_Line) { - if (cali_stage == 0) { // Line mode + } else if (m_calib_method == CALI_METHOD_MANUAL) { + if (m_calib_mode == CalibMode::Calib_PA_Line) { + if (cali_stage == 0) { // Line mode png_path = (boost::format("%1%/images/fd_calibration_manual.png") % resources_dir()).str(); - } - else if (cali_stage == 1) { // Pattern mode + } else if (cali_stage == 1) { // Pattern mode png_path = (boost::format("%1%/images/fd_pattern_manual_device.png") % resources_dir()).str(); } } } if (png_path != "") { - calib_bitmap = new wxBitmap; + calib_bitmap = new wxBitmap; *calib_bitmap = get_bitmap(png_path, width, height); } } } - if (calib_bitmap != nullptr) - m_project_task_panel->set_thumbnail_img(*calib_bitmap, ""); + if (calib_bitmap != nullptr) m_project_task_panel->set_thumbnail_img(*calib_bitmap, ""); } m_project_task_panel->show_layers_num(obj->is_support_layer_num); @@ -3711,7 +3549,6 @@ void StatusPanel::update_subtask(MachineObject *obj) if (obj->is_system_printing() || obj->is_in_calibration()) { reset_printing_values(); } else if (obj->is_in_printing() || obj->print_status == "FINISH") { - m_project_task_panel->update_subtask_name(wxString::Format("%s", GUI::from_u8(obj->subtask_name))); if (obj->get_modeltask() && obj->get_modeltask()->design_id > 0) { @@ -3738,12 +3575,11 @@ void StatusPanel::update_subtask(MachineObject *obj) m_project_task_panel->enable_abort_button(false); m_project_task_panel->enable_pause_resume_button(false, "pause_disable"); wxString prepare_text; - bool show_percent = true; + bool show_percent = true; if (obj->is_in_prepare()) { prepare_text = wxString::Format(_L("Downloading...")); - } - else if (obj->print_status == "SLICING") { + } else if (obj->print_status == "SLICING") { if (obj->queue_number <= 0) { prepare_text = wxString::Format(_L("Cloud Slicing...")); } else { @@ -3762,11 +3598,9 @@ void StatusPanel::update_subtask(MachineObject *obj) m_project_task_panel->update_layers_num(true, wxString::Format(_L("Layer: %s"), NA_STR)); m_project_task_panel->update_subtask_name(wxString::Format("%s", GUI::from_u8(obj->subtask_name))); - if (obj->get_modeltask() && obj->get_modeltask()->design_id > 0) { m_project_task_panel->show_profile_info(true, wxString::FromUTF8(obj->get_modeltask()->profile_name)); - } - else { + } else { m_project_task_panel->show_profile_info(false); } update_basic_print_data(false); @@ -3774,7 +3608,7 @@ void StatusPanel::update_subtask(MachineObject *obj) if (obj->can_resume()) { m_project_task_panel->enable_pause_resume_button(true, "resume"); } else { - m_project_task_panel->enable_pause_resume_button(true, "pause"); + m_project_task_panel->enable_pause_resume_button(true, "pause"); } m_project_task_panel->enable_partskip_button(obj, true); // update printing stage @@ -3816,9 +3650,7 @@ void StatusPanel::update_subtask(MachineObject *obj) m_project_task_panel->set_has_reted_text(false); } } - - m_project_task_panel->market_scoring_show(true); - + update_market_scoring(true); } else if (obj && obj->rating_info && !obj->rating_info->request_successful) { BOOST_LOG_TRIVIAL(info) << "model mall result request failed"; if (403 != obj->rating_info->http_code) { @@ -3830,7 +3662,7 @@ void StatusPanel::update_subtask(MachineObject *obj) } } } else { - m_project_task_panel->market_scoring_show(false); + update_market_scoring(false); } } else { // model printing is not finished, hide scoring page m_project_task_panel->enable_abort_button(true); @@ -3843,18 +3675,19 @@ void StatusPanel::update_subtask(MachineObject *obj) } } -void StatusPanel::update_partskip_subtask(MachineObject *obj){ +void StatusPanel::update_partskip_subtask(MachineObject *obj) +{ if (!obj) return; if (!obj->subtask_) return; auto partskip_button = m_project_task_panel->get_partskip_button(); if (partskip_button) { int part_cnt = 0; - if(m_project_task_panel->get_part_skipped_dirty() > 0){ + if (m_project_task_panel->get_part_skipped_dirty() > 0) { m_project_task_panel->set_part_skipped_dirty(m_project_task_panel->get_part_skipped_dirty() - 1); part_cnt = m_project_task_panel->get_part_skipped_count(); BOOST_LOG_TRIVIAL(info) << "part skip: stop recv printer dirty data."; - }else{ + } else { part_cnt = obj->m_partskip_ids.size(); BOOST_LOG_TRIVIAL(info) << "part skip: recv printer normal data."; } @@ -3864,9 +3697,7 @@ void StatusPanel::update_partskip_subtask(MachineObject *obj){ partskip_button->SetLabel(""); } - if(m_partskip_dlg && m_partskip_dlg->IsShown()) { - m_partskip_dlg->UpdatePartsStateFromPrinter(obj); - } + if (m_partskip_dlg && m_partskip_dlg->IsShown()) { m_partskip_dlg->UpdatePartsStateFromPrinter(obj); } } void StatusPanel::update_cloud_subtask(MachineObject *obj) @@ -3894,8 +3725,9 @@ void StatusPanel::update_cloud_subtask(MachineObject *obj) wxImage img; std::map::iterator it = img_list.find(m_request_url); if (it != img_list.end()) { - if (m_current_print_mode != PrintingTaskType::CALIBRATION ||(m_calib_mode == CalibMode::Calib_Flow_Rate && m_calib_method == CalibrationMethod::CALI_METHOD_MANUAL)) { - img = it->second; + if (m_current_print_mode != PrintingTaskType::CALIBRATION || + (m_calib_mode == CalibMode::Calib_Flow_Rate && m_calib_method == CalibrationMethod::CALI_METHOD_MANUAL)) { + img = it->second; wxImage resize_img = img.Scale(m_project_task_panel->get_bitmap_thumbnail()->GetSize().x, m_project_task_panel->get_bitmap_thumbnail()->GetSize().y); m_project_task_panel->set_thumbnail_img(resize_img, ""); m_project_task_panel->set_brightness_value(get_brightness_value(resize_img)); @@ -3931,7 +3763,7 @@ void StatusPanel::update_sdcard_subtask(MachineObject *obj) m_project_task_panel->get_bitmap_thumbnail()->SetBitmap(m_thumbnail_sdcard.bmp()); m_project_task_panel->set_thumbnail_img(m_thumbnail_sdcard.bmp(), m_thumbnail_sdcard.name()); } - task_thumbnail_state = ThumbnailState::SDCARD_THUMBNAIL; + task_thumbnail_state = ThumbnailState::SDCARD_THUMBNAIL; m_load_sdcard_thumbnail = true; } } @@ -3944,9 +3776,9 @@ void StatusPanel::reset_printing_values() m_project_task_panel->reset_printing_value(); m_project_task_panel->update_subtask_name(NA_STR); m_project_task_panel->show_profile_info(false); - // m_project_task_panel->update_stage_value_with_machine(wxEmptyString, 0, obj); + // m_project_task_panel->update_stage_value_with_machine(wxEmptyString, 0, obj); m_project_task_panel->update_stage_value_with_machine(wxEmptyString, 0, obj); - //obj->get_curr_stage() + // obj->get_curr_stage() m_project_task_panel->update_progress_percent(NA_STR, wxEmptyString); m_project_task_panel->market_scoring_show(false); @@ -3957,57 +3789,64 @@ void StatusPanel::reset_printing_values() m_project_task_panel->update_layers_num(true, wxString::Format(_L("Layer: %s"), NA_STR)); update_calib_bitmap(); - task_thumbnail_state = ThumbnailState::PLACE_HOLDER; + task_thumbnail_state = ThumbnailState::PLACE_HOLDER; m_start_loading_thumbnail = false; m_load_sdcard_thumbnail = false; - skip_print_error = 0; + skip_print_error = 0; } void StatusPanel::on_axis_ctrl_xy(wxCommandEvent &event) { if (!obj) return; - //check is at home - static std::unordered_set s_x_ctrl_idxes { 1, 3, 5, 7}; - static std::unordered_set s_y_ctrl_idxes{ 0, 2, 4, 6 }; - if (s_x_ctrl_idxes.count(event.GetInt()) != 0 && !obj->is_axis_at_home("X")) - { + // check is at home + static std::unordered_set s_x_ctrl_idxes{1, 3, 5, 7}; + static std::unordered_set s_y_ctrl_idxes{0, 2, 4, 6}; + if (s_x_ctrl_idxes.count(event.GetInt()) != 0 && !obj->GetAxis()->IsAxisAtHomeX()) { BOOST_LOG_TRIVIAL(info) << "axis x is not at home"; show_recenter_dialog(); return; - } - else if (s_y_ctrl_idxes.count(event.GetInt()) != 0 && !obj->is_axis_at_home("Y")) - { + } else if (s_y_ctrl_idxes.count(event.GetInt()) != 0 && !obj->GetAxis()->IsAxisAtHomeY()) { BOOST_LOG_TRIVIAL(info) << "axis y is not at home"; show_recenter_dialog(); return; } - if (event.GetInt() == 0) { obj->command_axis_control("Y", 1.0, 10.0f, 3000); } - else if (event.GetInt() == 1) { obj->command_axis_control("X", 1.0, -10.0f, 3000); } - else if (event.GetInt() == 2) { obj->command_axis_control("Y", 1.0, -10.0f, 3000); } - else if (event.GetInt() == 3) { obj->command_axis_control("X", 1.0, 10.0f, 3000); } - else if (event.GetInt() == 4) { obj->command_axis_control("Y", 1.0, 1.0f, 3000); } - else if (event.GetInt() == 5) { obj->command_axis_control("X", 1.0, -1.0f, 3000); } - else if (event.GetInt() == 6) { obj->command_axis_control("Y", 1.0, -1.0f, 3000); } - else if (event.GetInt() == 7) { obj->command_axis_control("X", 1.0, 1.0f, 3000); } - else if (event.GetInt() == 8) { + if (event.GetInt() == 0) { + obj->GetAxis()->Ctrl_Axis("Y", 1.0, 10.0f, 3000); + } else if (event.GetInt() == 1) { + obj->GetAxis()->Ctrl_Axis("X", 1.0, -10.0f, 3000); + } else if (event.GetInt() == 2) { + obj->GetAxis()->Ctrl_Axis("Y", 1.0, -10.0f, 3000); + } else if (event.GetInt() == 3) { + obj->GetAxis()->Ctrl_Axis("X", 1.0, 10.0f, 3000); + } else if (event.GetInt() == 4) { + obj->GetAxis()->Ctrl_Axis("Y", 1.0, 1.0f, 3000); + } else if (event.GetInt() == 5) { + obj->GetAxis()->Ctrl_Axis("X", 1.0, -1.0f, 3000); + } else if (event.GetInt() == 6) { + obj->GetAxis()->Ctrl_Axis("Y", 1.0, -1.0f, 3000); + } else if (event.GetInt() == 7) { + obj->GetAxis()->Ctrl_Axis("X", 1.0, 1.0f, 3000); + } else if (event.GetInt() == 8) { if (axis_go_home_dlg == nullptr) { axis_go_home_dlg = new SecondaryCheckDialog(this->GetParent(), wxID_ANY, _L("Auto homing")); axis_go_home_dlg->update_text(_L("Are you sure you want to trigger auto homing?")); axis_go_home_dlg->m_button_ok->SetLabel(_L("Homing")); - axis_go_home_dlg->Bind(EVT_SECONDARY_CHECK_CONFIRM, [this](wxCommandEvent& e) { - if (obj) { obj->command_go_home(); } + axis_go_home_dlg->Bind(EVT_SECONDARY_CHECK_CONFIRM, [this](wxCommandEvent &e) { + if (obj) { + obj->GetAxis()->Ctrl_GoHome(); + } }); } axis_go_home_dlg->on_show(); } } -bool StatusPanel::check_axis_z_at_home(MachineObject* obj) +bool StatusPanel::check_axis_z_at_home(MachineObject *obj) { if (obj) { - if (!obj->is_axis_at_home("Z")) { + if (!obj->GetAxis()->IsAxisAtHomeZ()) { BOOST_LOG_TRIVIAL(info) << "axis z is not at home"; show_recenter_dialog(); return false; @@ -4020,36 +3859,32 @@ bool StatusPanel::check_axis_z_at_home(MachineObject* obj) void StatusPanel::on_axis_ctrl_z_up_10(wxCommandEvent &event) { if (obj) { - obj->command_axis_control("Z", 1.0, -10.0f, 900); - if (!check_axis_z_at_home(obj)) - return; + obj->GetAxis()->Ctrl_Axis("Z", 1.0, -10.0f, 900); + if (!check_axis_z_at_home(obj)) return; } } void StatusPanel::on_axis_ctrl_z_up_1(wxCommandEvent &event) { if (obj) { - obj->command_axis_control("Z", 1.0, -1.0f, 900); - if (!check_axis_z_at_home(obj)) - return; + obj->GetAxis()->Ctrl_Axis("Z", 1.0, -1.0f, 900); + if (!check_axis_z_at_home(obj)) return; } } void StatusPanel::on_axis_ctrl_z_down_1(wxCommandEvent &event) { if (obj) { - obj->command_axis_control("Z", 1.0, 1.0f, 900); - if (!check_axis_z_at_home(obj)) - return; + obj->GetAxis()->Ctrl_Axis("Z", 1.0, 1.0f, 900); + if (!check_axis_z_at_home(obj)) return; } } void StatusPanel::on_axis_ctrl_z_down_10(wxCommandEvent &event) { if (obj) { - obj->command_axis_control("Z", 1.0, 10.0f, 900); - if (!check_axis_z_at_home(obj)) - return; + obj->GetAxis()->Ctrl_Axis("Z", 1.0, 10.0f, 900); + if (!check_axis_z_at_home(obj)) return; } } @@ -4063,46 +3898,49 @@ void StatusPanel::axis_ctrl_e_hint(bool up_down) ctrl_e_hint_dlg->m_staticText_release_note->SetMaxSize(wxSize(FromDIP(360), -1)); ctrl_e_hint_dlg->m_staticText_release_note->SetMinSize(wxSize(FromDIP(360), -1)); ctrl_e_hint_dlg->Fit();*/ - ctrl_e_hint_dlg = new MessageDialog(this, _L("Please heat the nozzle to above 170 degree before loading or unloading filament."), wxString(_L("Warning")), wxOK | wxCENTER); + ctrl_e_hint_dlg = new MessageDialog(this, _L("Please heat the nozzle to above 170 degree before loading or unloading filament."), wxString(_L("Warning")), + wxOK | wxCENTER); } - ctrl_e_hint_dlg->ShowModal(); - // ctrl_e_hint_dlg->on_show(); + ctrl_e_hint_dlg->ShowModal(); + // ctrl_e_hint_dlg->on_show(); } -void StatusPanel::on_axis_ctrl_e_up_10(wxCommandEvent &event) +void StatusPanel::on_axis_ctrl_e_up_10(wxCommandEvent& event) { if (obj) { auto ext = obj->GetExtderSystem()->GetCurrentExtder(); - if (ext && ext->GetCurrentTemp() >= TEMP_THRESHOLD_ALLOW_E_CTRL) + if (ext && ext->GetCurrentTemp() >= TEMP_THRESHOLD_ALLOW_E_CTRL) { if (obj->is_enable_np) { obj->command_extruder_control(ext->GetExtId(), -10.0f); } else { - obj->command_axis_control("E", 1.0, -10.0f, 900); + obj->GetAxis()->Ctrl_Axis("E", 1.0, -10.0f, 900); } - - else + } else { axis_ctrl_e_hint(true); + } } } -void StatusPanel::on_axis_ctrl_e_down_10(wxCommandEvent &event) +void StatusPanel::on_axis_ctrl_e_down_10(wxCommandEvent& event) { if (obj) { auto ext = obj->GetExtderSystem()->GetCurrentExtder(); if (ext && ext->GetCurrentTemp() >= TEMP_THRESHOLD_ALLOW_E_CTRL) + { if (obj->is_enable_np) { obj->command_extruder_control(ext->GetExtId(), 10.0f); } else { - obj->command_axis_control("E", 1.0, 10.0f, 900); + obj->GetAxis()->Ctrl_Axis("E", 1.0, 10.0f, 900); } - else + } else { axis_ctrl_e_hint(false); + } } } void StatusPanel::on_set_bed_temp() { - if (!obj) {return;} + if (!obj) { return; } wxString str = m_tempCtrl_bed->GetTextCtrl()->GetValue(); try { @@ -4111,9 +3949,7 @@ void StatusPanel::on_set_bed_temp() set_hold_count(m_temp_bed_timeout); int limit = obj->get_bed_temperature_limit(); - if (obj->bed_temp_range.size() > 1) { - limit = obj->bed_temp_range[1]; - } + if (obj->bed_temp_range.size() > 1) { limit = obj->bed_temp_range[1]; } if (bed_temp >= limit) { BOOST_LOG_TRIVIAL(info) << "can not set over limit = " << limit << ", set temp = " << bed_temp; @@ -4130,11 +3966,23 @@ void StatusPanel::on_set_bed_temp() void StatusPanel::on_set_nozzle_temp(int nozzle_id) { - if (!obj) {return;} + if (!obj) { return; } try { long nozzle_temp; + const auto& extder = obj->GetExtderSystem()->GetExtderById(nozzle_id); + if (!extder) { + return; + } + + if (!extder->HasNozzleInstalled()) { + // en: The extruder hotend not detected. Cannot set nozzle temperature. + MessageDialog msg_dlg(this, _L("Right extruder hotend not detected. Cannot set nozzle temperature."), _L("Warning"), wxICON_WARNING | wxOK); + msg_dlg.ShowModal(); + return; + } + if (nozzle_id == MAIN_EXTRUDER_ID) { wxString str = m_tempCtrl_nozzle->GetTextCtrl()->GetValue(); if (str.ToLong(&nozzle_temp) && obj) { @@ -4171,7 +4019,7 @@ void StatusPanel::on_set_nozzle_temp(int nozzle_id) void StatusPanel::on_set_chamber_temp() { - if (!obj) {return;} + if (!obj) { return; } wxString str = m_tempCtrl_chamber->GetTextCtrl()->GetValue(); try { @@ -4184,8 +4032,7 @@ void StatusPanel::on_set_chamber_temp() m_tempCtrl_chamber->Warning(false); } - if(obj->is_in_printing() && obj->GetFan()->GetSupportAirduct() && obj->GetFan()->is_at_cooling_mode()) - { + if (obj->is_in_printing() && obj->GetFan()->GetSupportAirduct() && obj->GetFan()->is_at_cooling_mode()) { #ifndef __APPLE__ MessageDialog champer_switch_head_dlg(this, _L("Chamber temperature cannot be changed in cooling mode while printing."), wxEmptyString, wxICON_WARNING | wxOK); #else @@ -4193,24 +4040,25 @@ void StatusPanel::on_set_chamber_temp() #endif champer_switch_head_dlg.ShowModal(); return; - } - else if (!obj->GetFan()->is_at_heating_mode() && chamber_temp >= obj->GetConfig()->GetChamberTempSwitchHeat()) - { + } else if (!obj->GetFan()->is_at_heating_mode() && chamber_temp >= obj->GetConfig()->GetChamberTempSwitchHeat()) { #ifndef __APPLE__ - MessageDialog champer_switch_head_dlg(this, _L("If the chamber temperature exceeds 40\u2103, the system will automatically switch to heating mode. " - "Please confirm whether to switch."), wxEmptyString, wxICON_WARNING | wxOK | wxCANCEL); + MessageDialog champer_switch_head_dlg(this, + _L("If the chamber temperature exceeds 40\u2103, the system will automatically switch to heating mode. " + "Please confirm whether to switch."), + wxEmptyString, wxICON_WARNING | wxOK | wxCANCEL); #else /*STUDIO-10386 MessageDialog here may cause block in macOS, use wxMessageDialog*/ - wxMessageDialog champer_switch_head_dlg(this, _L("If the chamber temperature exceeds 40\u2103, the system will automatically switch to heating mode. " - "Please confirm whether to switch."), wxEmptyString, wxICON_WARNING | wxOK | wxCANCEL); + wxMessageDialog champer_switch_head_dlg(this, + _L("If the chamber temperature exceeds 40\u2103, the system will automatically switch to heating mode. " + "Please confirm whether to switch."), + wxEmptyString, wxICON_WARNING | wxOK | wxCANCEL); #endif if (champer_switch_head_dlg.ShowModal() != wxID_OK) { return; } } - obj->command_set_chamber(chamber_temp); + obj->GetChamber()->CtrlSetChamberTemp(chamber_temp); } - } - catch (...) { + } catch (...) { ; } } @@ -4225,8 +4073,7 @@ void StatusPanel::update_load_with_temp() { if (!obj->is_filament_at_extruder()) { m_is_load_with_temp = true; - } - else { + } else { m_is_load_with_temp = false; } } @@ -4234,24 +4081,18 @@ void StatusPanel::update_load_with_temp() void StatusPanel::on_ams_load_curr() { if (obj) { - std::string curr_ams_id = m_ams_control->GetCurentAms(); - std::string curr_can_id = m_ams_control->GetCurrentCan(curr_ams_id); - + std::string curr_ams_id = m_ams_control->GetCurentAms(); + std::string curr_can_id = m_ams_control->GetCurrentCan(curr_ams_id); update_load_with_temp(); - //virtual tray - if (curr_ams_id.compare(std::to_string(VIRTUAL_TRAY_MAIN_ID)) == 0 || - curr_ams_id.compare(std::to_string(VIRTUAL_TRAY_DEPUTY_ID)) == 0) - { + // virtual tray + if (curr_ams_id.compare(std::to_string(VIRTUAL_TRAY_MAIN_ID)) == 0 || curr_ams_id.compare(std::to_string(VIRTUAL_TRAY_DEPUTY_ID)) == 0) { int vt_slot_idx = 0; - if (curr_ams_id.compare(std::to_string(VIRTUAL_TRAY_DEPUTY_ID)) == 0) - { - vt_slot_idx = 1; - } + if (curr_ams_id.compare(std::to_string(VIRTUAL_TRAY_DEPUTY_ID)) == 0) { vt_slot_idx = 1; } - int old_temp = -1; - int new_temp = -1; - DevAmsTray* curr_tray = &obj->vt_slot[vt_slot_idx]; + int old_temp = -1; + int new_temp = -1; + DevAmsTray *curr_tray = &obj->vt_slot[vt_slot_idx]; if (!curr_tray) return; @@ -4260,23 +4101,20 @@ void StatusPanel::on_ams_load_curr() old_temp = (atoi(curr_tray->nozzle_temp_min.c_str()) + atoi(curr_tray->nozzle_temp_max.c_str())) / 2; if (!curr_tray->nozzle_temp_max.empty() && !curr_tray->nozzle_temp_min.empty()) new_temp = (atoi(curr_tray->nozzle_temp_min.c_str()) + atoi(curr_tray->nozzle_temp_max.c_str())) / 2; - } - catch (...) { + } catch (...) { ; } if (obj->is_enable_np || obj->is_enable_ams_np) { try { - if (!curr_ams_id.empty() && !curr_can_id.empty()) { - obj->command_ams_change_filament(true, curr_ams_id, "0", old_temp, new_temp); - } + if (!curr_ams_id.empty() && !curr_can_id.empty()) { obj->command_ams_change_filament(true, curr_ams_id, "0", old_temp, new_temp); } } catch (...) {} } else { obj->command_ams_change_filament(true, "254", "0", old_temp, new_temp); } } - std::map::iterator it = obj->GetFilaSystem()->GetAmsList().find(curr_ams_id); + std::map::iterator it = obj->GetFilaSystem()->GetAmsList().find(curr_ams_id); if (it == obj->GetFilaSystem()->GetAmsList().end()) { BOOST_LOG_TRIVIAL(trace) << "ams: find " << curr_ams_id << " failed"; return; @@ -4286,8 +4124,8 @@ void StatusPanel::on_ams_load_curr() BOOST_LOG_TRIVIAL(trace) << "ams: find " << curr_can_id << " failed"; return; } - DevAmsTray* curr_tray = obj->get_curr_tray(); - DevAmsTray* targ_tray = obj->get_ams_tray(curr_ams_id, curr_can_id); + DevAmsTray *curr_tray = obj->get_curr_tray(); + DevAmsTray *targ_tray = obj->get_ams_tray(curr_ams_id, curr_can_id); int old_temp = -1; int new_temp = -1; @@ -4307,18 +4145,16 @@ void StatusPanel::on_ams_load_curr() if (obj->is_enable_np) { try { - if (!curr_ams_id.empty() && !curr_can_id.empty()) { - obj->command_ams_change_filament(true, curr_ams_id, curr_can_id, old_temp, new_temp); - } - } - catch (...){} + if (!curr_ams_id.empty() && !curr_can_id.empty()) { obj->command_ams_change_filament(true, curr_ams_id, curr_can_id, old_temp, new_temp); } + } catch (...) {} } else { obj->command_ams_change_filament(true, curr_ams_id, curr_can_id, old_temp, new_temp); } } } -void StatusPanel::on_ams_load_vams(wxCommandEvent& event) { +void StatusPanel::on_ams_load_vams(wxCommandEvent &event) +{ BOOST_LOG_TRIVIAL(info) << "on_ams_load_vams_tray"; m_ams_control->SwitchAms(std::to_string(VIRTUAL_TRAY_MAIN_ID)); @@ -4327,8 +4163,7 @@ void StatusPanel::on_ams_load_vams(wxCommandEvent& event) { void StatusPanel::on_ams_switch(SimpleEvent &event) { - if(obj){ - + if (obj) { /*right*/ if (obj->GetExtderSystem()->GetTotalExtderCount() > 0) { auto ext = obj->GetExtderSystem()->GetExtderById(MAIN_EXTRUDER_ID); @@ -4336,7 +4171,8 @@ void StatusPanel::on_ams_switch(SimpleEvent &event) if (ext->GetSlotNow().ams_id == std::to_string(VIRTUAL_TRAY_MAIN_ID) || ext->GetSlotNow().ams_id == std::to_string(VIRTUAL_TRAY_DEPUTY_ID)) { m_ams_control->SetAmsStep(ext->GetSlotNow().ams_id, "0", AMSPassRoadType::AMS_ROAD_TYPE_LOAD, AMSPassRoadSTEP::AMS_ROAD_STEP_COMBO_LOAD_STEP3); } else { - m_ams_control->SetAmsStep(ext->GetSlotNow().ams_id, ext->GetSlotNow().slot_id, AMSPassRoadType::AMS_ROAD_TYPE_LOAD, AMSPassRoadSTEP::AMS_ROAD_STEP_COMBO_LOAD_STEP2); + m_ams_control->SetAmsStep(ext->GetSlotNow().ams_id, ext->GetSlotNow().slot_id, AMSPassRoadType::AMS_ROAD_TYPE_LOAD, + AMSPassRoadSTEP::AMS_ROAD_STEP_COMBO_LOAD_STEP2); } } else { m_ams_control->SetAmsStep(ext->GetSlotNow().ams_id, ext->GetSlotNow().slot_id, AMSPassRoadType::AMS_ROAD_TYPE_UNLOAD, AMSPassRoadSTEP::AMS_ROAD_STEP_NONE); @@ -4351,7 +4187,8 @@ void StatusPanel::on_ams_switch(SimpleEvent &event) if (ext.GetSlotNow().ams_id == std::to_string(VIRTUAL_TRAY_MAIN_ID) || ext.GetSlotNow().ams_id == std::to_string(VIRTUAL_TRAY_DEPUTY_ID)) { m_ams_control->SetAmsStep(ext.GetSlotNow().ams_id, "0", AMSPassRoadType::AMS_ROAD_TYPE_LOAD, AMSPassRoadSTEP::AMS_ROAD_STEP_COMBO_LOAD_STEP3); } else { - m_ams_control->SetAmsStep(ext.GetSlotNow().ams_id, ext.GetSlotNow().slot_id, AMSPassRoadType::AMS_ROAD_TYPE_LOAD, AMSPassRoadSTEP::AMS_ROAD_STEP_COMBO_LOAD_STEP2); + m_ams_control->SetAmsStep(ext.GetSlotNow().ams_id, ext.GetSlotNow().slot_id, AMSPassRoadType::AMS_ROAD_TYPE_LOAD, + AMSPassRoadSTEP::AMS_ROAD_STEP_COMBO_LOAD_STEP2); } } else { m_ams_control->SetAmsStep(ext.GetSlotNow().ams_id, ext.GetSlotNow().slot_id, AMSPassRoadType::AMS_ROAD_TYPE_UNLOAD, AMSPassRoadSTEP::AMS_ROAD_STEP_NONE); @@ -4379,10 +4216,10 @@ void StatusPanel::on_ams_unload(SimpleEvent &event) } } -void StatusPanel::on_ams_filament_backup(SimpleEvent& event) +void StatusPanel::on_ams_filament_backup(SimpleEvent &event) { if (obj) { - AmsReplaceMaterialDialog* m_replace_material_popup = new AmsReplaceMaterialDialog(this); + AmsReplaceMaterialDialog *m_replace_material_popup = new AmsReplaceMaterialDialog(this); m_replace_material_popup->update_machine_obj(obj); m_replace_material_popup->ShowModal(); } @@ -4391,9 +4228,7 @@ void StatusPanel::on_ams_filament_backup(SimpleEvent& event) void StatusPanel::on_ams_setting_click(SimpleEvent &event) { if (obj) { - if (!m_ams_setting_dlg) { - m_ams_setting_dlg = new AMSSetting((wxWindow*)this, wxID_ANY); - } + if (!m_ams_setting_dlg) { m_ams_setting_dlg = new AMSSetting((wxWindow *) this, wxID_ANY); } m_ams_setting_dlg->UpdateByObj(obj); m_ams_setting_dlg->Show(); @@ -4402,13 +4237,12 @@ void StatusPanel::on_ams_setting_click(SimpleEvent &event) void StatusPanel::on_filament_extrusion_cali(wxCommandEvent &event) { - if (!m_extrusion_cali_dlg) - m_extrusion_cali_dlg = new ExtrusionCalibration((wxWindow*)this, wxID_ANY); + if (!m_extrusion_cali_dlg) m_extrusion_cali_dlg = new ExtrusionCalibration((wxWindow *) this, wxID_ANY); if (obj) { m_extrusion_cali_dlg->obj = obj; - std::string ams_id = m_ams_control->GetCurentAms(); - std::string tray_id = m_ams_control->GetCurrentCan(ams_id); + std::string ams_id = m_ams_control->GetCurentAms(); + std::string tray_id = m_ams_control->GetCurrentCan(ams_id); if (tray_id.empty() && ams_id.compare(std::to_string(VIRTUAL_TRAY_MAIN_ID)) != 0) { wxString txt = _L("Please select an BOX slot before calibration"); MessageDialog msg_dlg(nullptr, txt, wxEmptyString, wxICON_WARNING | wxOK); @@ -4422,11 +4256,10 @@ void StatusPanel::on_filament_extrusion_cali(wxCommandEvent &event) // set ams_filament id is is qdt filament if (ams_id.compare(std::to_string(VIRTUAL_TRAY_MAIN_ID)) == 0) { - tray_id_int = VIRTUAL_TRAY_MAIN_ID; + tray_id_int = VIRTUAL_TRAY_MAIN_ID; m_extrusion_cali_dlg->ams_filament_id = ""; - } - else { - ams_id_int = atoi(ams_id.c_str()); + } else { + ams_id_int = atoi(ams_id.c_str()); tray_id_int = atoi(tray_id.c_str()); auto tray = obj->GetFilaSystem()->GetAmsTray(ams_id, tray_id); @@ -4439,11 +4272,11 @@ void StatusPanel::on_filament_extrusion_cali(wxCommandEvent &event) } try { - m_extrusion_cali_dlg->ams_id = ams_id_int; + m_extrusion_cali_dlg->ams_id = ams_id_int; m_extrusion_cali_dlg->tray_id = tray_id_int; m_extrusion_cali_dlg->SetPosition(m_staticText_control->GetScreenPosition()); m_extrusion_cali_dlg->Popup(); - } catch(...) { + } catch (...) { ; } } @@ -4454,15 +4287,15 @@ void StatusPanel::on_filament_edit(wxCommandEvent &event) // update params if (!m_filament_setting_dlg) m_filament_setting_dlg = new AMSMaterialsSetting((wxWindow *) this, wxID_ANY); - int current_position_x = m_ams_control->GetScreenPosition().x; - int current_position_y = m_ams_control->GetScreenPosition().y - FromDIP(40); - auto drect = wxDisplay(GetParent()).GetGeometry().GetHeight() - FromDIP(50); + int current_position_x = m_ams_control->GetScreenPosition().x; + int current_position_y = m_ams_control->GetScreenPosition().y - FromDIP(40); + auto drect = wxDisplay(GetParent()).GetGeometry().GetHeight() - FromDIP(50); current_position_y = current_position_y + m_filament_setting_dlg->GetSize().GetHeight() > drect ? drect - m_filament_setting_dlg->GetSize().GetHeight() : current_position_y; if (obj) { m_filament_setting_dlg->obj = obj; - int ams_id = event.GetInt(); + int ams_id = event.GetInt(); int slot_id = event.GetString().IsEmpty() ? 0 : std::stoi(event.GetString().ToStdString()); try { @@ -4477,10 +4310,9 @@ void StatusPanel::on_filament_edit(wxCommandEvent &event) wxString n_val; auto tray = obj->GetFilaSystem()->GetAmsTray(std::to_string(ams_id), std::to_string(slot_id)); - if (tray) - { - k_val = wxString::Format("%.3f", tray->k); - n_val = wxString::Format("%.3f", tray->n); + if (tray) { + k_val = wxString::Format("%.3f", tray->k); + n_val = wxString::Format("%.3f", tray->n); wxColor color = DevAmsTray::decode_color(tray->color); // m_filament_setting_dlg->set_color(color); @@ -4489,23 +4321,19 @@ void StatusPanel::on_filament_edit(wxCommandEvent &event) m_filament_setting_dlg->set_ctype(tray->ctype); m_filament_setting_dlg->ams_filament_id = tray->setting_id; - if (m_filament_setting_dlg->ams_filament_id.empty()) - { + if (m_filament_setting_dlg->ams_filament_id.empty()) { m_filament_setting_dlg->set_empty_color(color); - } - else - { + } else { m_filament_setting_dlg->set_color(color); m_filament_setting_dlg->set_colors(cols); } m_filament_setting_dlg->m_is_third = !DevFilaSystem::IsQDT_Filament(tray->tag_uid); - if (!m_filament_setting_dlg->m_is_third) - { + if (!m_filament_setting_dlg->m_is_third) { sn_number = tray->uuid; - filament = tray->sub_brands; - temp_max = tray->nozzle_temp_max; - temp_min = tray->nozzle_temp_min; + filament = tray->sub_brands; + temp_max = tray->nozzle_temp_max; + temp_min = tray->nozzle_temp_min; } } @@ -4520,46 +4348,44 @@ void StatusPanel::on_filament_edit(wxCommandEvent &event) void StatusPanel::on_ext_spool_edit(wxCommandEvent &event) { // update params - if (!m_filament_setting_dlg) m_filament_setting_dlg = new AMSMaterialsSetting((wxWindow*)this, wxID_ANY); + if (!m_filament_setting_dlg) m_filament_setting_dlg = new AMSMaterialsSetting((wxWindow *) this, wxID_ANY); - int current_position_x = m_ams_control->GetScreenPosition().x; - int current_position_y = m_ams_control->GetScreenPosition().y - FromDIP(40); - auto drect = wxDisplay(GetParent()).GetGeometry().GetHeight() - FromDIP(50); + int current_position_x = m_ams_control->GetScreenPosition().x; + int current_position_y = m_ams_control->GetScreenPosition().y - FromDIP(40); + auto drect = wxDisplay(GetParent()).GetGeometry().GetHeight() - FromDIP(50); current_position_y = current_position_y + m_filament_setting_dlg->GetSize().GetHeight() > drect ? drect - m_filament_setting_dlg->GetSize().GetHeight() : current_position_y; if (obj) { m_filament_setting_dlg->obj = obj; - int ams_id = event.GetInt(); - int slot_id = event.GetString().IsEmpty() ? 0 : std::stoi(event.GetString().ToStdString()); + int ams_id = event.GetInt(); + int slot_id = event.GetString().IsEmpty() ? 0 : std::stoi(event.GetString().ToStdString()); - m_filament_setting_dlg->ams_id = ams_id; - m_filament_setting_dlg->slot_id = slot_id; - int nozzle_index = ams_id == VIRTUAL_TRAY_MAIN_ID ? 0 : 1; + m_filament_setting_dlg->ams_id = ams_id; + m_filament_setting_dlg->slot_id = slot_id; + int nozzle_index = ams_id == VIRTUAL_TRAY_MAIN_ID ? 0 : 1; try { std::string sn_number; std::string filament; std::string temp_max; std::string temp_min; - wxString k_val; - wxString n_val; + wxString k_val; + wxString n_val; k_val = wxString::Format("%.3f", obj->vt_slot[nozzle_index].k); n_val = wxString::Format("%.3f", obj->vt_slot[nozzle_index].n); wxColor color = DevAmsTray::decode_color(obj->vt_slot[nozzle_index].color); m_filament_setting_dlg->ams_filament_id = obj->vt_slot[nozzle_index].setting_id; std::vector cols; - for (auto col : obj->vt_slot[nozzle_index].cols) { - cols.push_back(DevAmsTray::decode_color(col)); - } + for (auto col : obj->vt_slot[nozzle_index].cols) { cols.push_back(DevAmsTray::decode_color(col)); } m_filament_setting_dlg->set_ctype(obj->vt_slot[nozzle_index].ctype); if (m_filament_setting_dlg->ams_filament_id.empty()) { m_filament_setting_dlg->set_empty_color(color); - } - else { + } else { m_filament_setting_dlg->set_color(color); + m_filament_setting_dlg->set_colors(cols); } m_filament_setting_dlg->m_is_third = !DevFilaSystem::IsQDT_Filament(obj->vt_slot[nozzle_index].tag_uid); @@ -4570,10 +4396,9 @@ void StatusPanel::on_ext_spool_edit(wxCommandEvent &event) temp_min = obj->vt_slot[nozzle_index].nozzle_temp_min; } - m_filament_setting_dlg->Move(wxPoint(current_position_x,current_position_y)); + m_filament_setting_dlg->Move(wxPoint(current_position_x, current_position_y)); m_filament_setting_dlg->Popup(filament, sn_number, temp_min, temp_max, k_val, n_val); - } - catch (...) { + } catch (...) { ; } } @@ -4582,16 +4407,11 @@ void StatusPanel::on_ext_spool_edit(wxCommandEvent &event) void StatusPanel::on_ams_refresh_rfid(wxCommandEvent &event) { if (obj) { - - //std::string curr_ams_id = m_ams_control->GetCurentAms(); - if (event.GetInt() < 0 || event.GetInt() > VIRTUAL_TRAY_MAIN_ID){ - return; - } + // std::string curr_ams_id = m_ams_control->GetCurentAms(); + if (event.GetInt() < 0 || event.GetInt() > VIRTUAL_TRAY_MAIN_ID) { return; } std::string curr_ams_id = std::to_string(event.GetInt()); // do not support refresh rfid for VIRTUAL_TRAY_MAIN_ID - if (curr_ams_id.compare(std::to_string(VIRTUAL_TRAY_MAIN_ID)) == 0) { - return; - } + if (curr_ams_id.compare(std::to_string(VIRTUAL_TRAY_MAIN_ID)) == 0) { return; } std::string curr_can_id = event.GetString().ToStdString(); std::map::iterator it = obj->GetFilaSystem()->GetAmsList().find(curr_ams_id); @@ -4606,7 +4426,7 @@ void StatusPanel::on_ams_refresh_rfid(wxCommandEvent &event) } auto has_filament_at_extruder = false; - auto use_new_command = false; + auto use_new_command = false; if (obj->is_enable_np || obj->is_enable_ams_np) { use_new_command = true; @@ -4624,16 +4444,13 @@ void StatusPanel::on_ams_refresh_rfid(wxCommandEvent &event) return; } - try { if (!use_new_command) { int tray_index = atoi(curr_ams_id.c_str()) * 4 + atoi(slot_it->second->id.c_str()); obj->command_ams_refresh_rfid(std::to_string(tray_index)); } - if (use_new_command) { - obj->command_ams_refresh_rfid2(stoi(curr_ams_id), stoi(curr_can_id)); - } + if (use_new_command) { obj->command_ams_refresh_rfid2(stoi(curr_ams_id), stoi(curr_can_id)); } } catch (...) { ; @@ -4644,14 +4461,14 @@ void StatusPanel::on_ams_refresh_rfid(wxCommandEvent &event) void StatusPanel::on_ams_selected(wxCommandEvent &event) { if (obj) { - std::string curr_ams_id = m_ams_control->GetCurentAms(); + std::string curr_ams_id = m_ams_control->GetCurentAms(); std::string curr_selected_ams_id = std::to_string(event.GetInt()); if (curr_ams_id.compare(std::to_string(VIRTUAL_TRAY_MAIN_ID)) == 0) { return; } else { - std::string curr_can_id = event.GetString().ToStdString(); - std::map::iterator it = obj->GetFilaSystem()->GetAmsList().find(curr_ams_id); + std::string curr_can_id = event.GetString().ToStdString(); + std::map::iterator it = obj->GetFilaSystem()->GetAmsList().find(curr_ams_id); if (it == obj->GetFilaSystem()->GetAmsList().end()) { BOOST_LOG_TRIVIAL(trace) << "ams: find " << curr_ams_id << " failed"; return; @@ -4671,45 +4488,40 @@ void StatusPanel::on_ams_selected(wxCommandEvent &event) } } -void StatusPanel::on_ams_guide(wxCommandEvent& event) +void StatusPanel::on_ams_guide(wxCommandEvent &event) { wxString ams_wiki_url; if (m_ams_control && m_ams_control->m_is_none_ams_mode == AMSModel::GENERIC_AMS) { - ams_wiki_url = "https://wiki.qiditech.com/en/software/qidi-studio/use-ams-on-qidi-studio"; - } - else if (m_ams_control && m_ams_control->m_is_none_ams_mode == AMSModel::AMS_LITE) { - ams_wiki_url = "https://wiki.qiditech.com/en/ams-lite"; - } - else { - ams_wiki_url = "https://wiki.qiditech.com/en/software/qidi-studio/use-ams-on-qidi-studio"; + ams_wiki_url = "https://wiki.qidi3d.com/en/QIDIBOX"; + } else if (m_ams_control && m_ams_control->m_is_none_ams_mode == AMSModel::AMS_LITE) { + ams_wiki_url = "https://wiki.qidi3d.com/en/QIDIBOX"; + } else { + ams_wiki_url = "https://wiki.qidi3d.com/en/QIDIBOX"; } - //wxLaunchDefaultBrowser(ams_wiki_url); + wxLaunchDefaultBrowser(ams_wiki_url); } -void StatusPanel::on_ams_retry(wxCommandEvent& event) +void StatusPanel::on_ams_retry(wxCommandEvent &event) { BOOST_LOG_TRIVIAL(info) << "on_ams_retry"; - if (obj) { - obj->command_ams_control("resume"); - } + if (obj) { obj->command_ams_control("resume"); } } - -void StatusPanel::on_fan_changed(wxCommandEvent& event) +void StatusPanel::on_fan_changed(wxCommandEvent &event) { - auto type = event.GetInt(); + auto type = event.GetInt(); auto speed = atoi(event.GetString().c_str()); set_hold_count(this->m_switch_cham_fan_timeout); } -void StatusPanel::on_cham_temp_kill_focus(wxFocusEvent& event) +void StatusPanel::on_cham_temp_kill_focus(wxFocusEvent &event) { event.Skip(); cham_temp_input = false; } -void StatusPanel::on_cham_temp_set_focus(wxFocusEvent& event) +void StatusPanel::on_cham_temp_set_focus(wxFocusEvent &event) { event.Skip(); cham_temp_input = true; @@ -4756,8 +4568,8 @@ void StatusPanel::on_switch_speed(wxCommandEvent &event) popUp->BindUnfocusEvent(); #endif popUp->SetBackgroundColour(StateColor::darkModeColorFor(0xeeeeee)); - StepCtrl *step = new StepCtrl(popUp, wxID_ANY); - wxSizer *sizer = new wxBoxSizer(wxHORIZONTAL); + StepCtrl *step = new StepCtrl(popUp, wxID_ANY); + wxSizer *sizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(step, 1, wxEXPAND, 0); popUp->SetSizer(sizer); auto em = em_unit(this); @@ -4772,22 +4584,19 @@ void StatusPanel::on_switch_speed(wxCommandEvent &event) int selected_item = 1; if (obj) { int speed_lvl_idx = obj->GetPrintingSpeedLevel() - 1; - if (speed_lvl_idx >= 0 && speed_lvl_idx < 4) { - selected_item = speed_lvl_idx; - } + if (speed_lvl_idx >= 0 && speed_lvl_idx < 4) { selected_item = speed_lvl_idx; } } step->SelectItem(selected_item); if (!obj->is_in_printing()) { - step->Bind(wxEVT_LEFT_DOWN, [](auto& e) { - return; }); + step->Bind(wxEVT_LEFT_DOWN, [](auto &e) { return; }); } step->Bind(EVT_STEP_CHANGED, [this](auto &e) { - this->speed_lvl = e.GetInt() + 1; + this->speed_lvl = e.GetInt() + 1; if (obj) { set_hold_count(this->speed_lvl_timeout); - obj->command_set_printing_speed((DevPrintingSpeedLevel)this->speed_lvl); + obj->command_set_printing_speed((DevPrintingSpeedLevel) this->speed_lvl); } }); popUp->Bind(wxEVT_SHOW, [this, popUp](auto &e) { @@ -4795,7 +4604,7 @@ void StatusPanel::on_switch_speed(wxCommandEvent &event) popUp->Destroy(); speed_dismiss_time = boost::posix_time::microsec_clock::universal_time(); } - }); + }); wxPoint pos = m_switch_speed->ClientToScreen(wxPoint(0, -6)); popUp->Position(pos, {0, m_switch_speed->GetSize().y + 12}); @@ -4804,19 +4613,19 @@ void StatusPanel::on_switch_speed(wxCommandEvent &event) void StatusPanel::on_printing_fan_switch(wxCommandEvent &event) { - /* if (!obj) return; + /* if (!obj) return; - bool value = m_switch_printing_fan->GetValue(); + bool value = m_switch_printing_fan->GetValue(); - if (value) { - obj->command_control_fan(MachineObject::FanType::BIG_COOLING_FAN, true); - m_switch_printing_fan->SetValue(true); - set_hold_count(this->m_switch_printing_fan_timeout); - } else { - obj->command_control_fan(MachineObject::FanType::BIG_COOLING_FAN, false); - m_switch_printing_fan->SetValue(false); - set_hold_count(this->m_switch_printing_fan_timeout); - }*/ + if (value) { + obj->command_control_fan(MachineObject::FanType::BIG_COOLING_FAN, true); + m_switch_printing_fan->SetValue(true); + set_hold_count(this->m_switch_printing_fan_timeout); + } else { + obj->command_control_fan(MachineObject::FanType::BIG_COOLING_FAN, false); + m_switch_printing_fan->SetValue(false); + set_hold_count(this->m_switch_printing_fan_timeout); + }*/ } void StatusPanel::on_nozzle_fan_switch(wxCommandEvent &event) @@ -4827,28 +4636,22 @@ void StatusPanel::on_nozzle_fan_switch(wxCommandEvent &event) } if (!obj) { return; } - if (obj->GetFan()->GetAirDuctData().modes.empty()) - { - obj->GetFan()->converse_to_duct(true, obj->GetFan()->GetSupportAuxFanData(), obj->GetFan()->GetSupportChamberFan()); - } + if (obj->GetFan()->GetAirDuctData().modes.empty()) { obj->GetFan()->converse_to_duct(true, obj->GetFan()->GetSupportAuxFanData(), obj->GetFan()->GetSupportChamberFan()); } m_fan_control_popup = new FanControlPopupNew(this, obj, obj->GetFan()->GetAirDuctData()); auto pos = m_switch_fan->GetScreenPosition(); - pos.y = pos.y + m_switch_fan->GetSize().y; + pos.y = pos.y + m_switch_fan->GetSize().y; - int display_idx = wxDisplay::GetFromWindow(this); - auto display = wxDisplay(display_idx).GetClientArea(); + int display_idx = wxDisplay::GetFromWindow(this); + auto display = wxDisplay(display_idx).GetClientArea(); - - wxSize screenSize = wxSize(display.GetWidth(), display.GetHeight()); + wxSize screenSize = wxSize(display.GetWidth(), display.GetHeight()); wxSize fan_popup_size = m_fan_control_popup->GetSize(); pos.x -= FromDIP(150); pos.y -= FromDIP(20); - if (screenSize.y - fan_popup_size.y < FromDIP(300)) { - pos.y = (screenSize.y - fan_popup_size.y) / 2; - } + if (screenSize.y - fan_popup_size.y < FromDIP(300)) { pos.y = (screenSize.y - fan_popup_size.y) / 2; } m_fan_control_popup->SetPosition(pos); m_fan_control_popup->ShowModal(); @@ -4865,13 +4668,13 @@ void StatusPanel::on_lamp_switch(wxCommandEvent &event) set_hold_count(this->m_switch_lamp_timeout); obj->GetLamp()->CtrlSetChamberLight(DevLamp::LIGHT_EFFECT_ON); } else { - if (obj->GetLamp()->HasLampCloseRecheck()){ - MessageDialog msg_dlg(nullptr, _L("Turning off the lights during the task will cause the failure of AI monitoring, like spaghetti detection. Please choose carefully."), wxEmptyString, wxICON_WARNING | wxOK | wxCANCEL); + if (obj->GetLamp()->HasLampCloseRecheck()) { + MessageDialog msg_dlg(nullptr, + _L("Turning off the lights during the task will cause the failure of AI monitoring, like spaghetti detection. Please choose carefully."), + wxEmptyString, wxICON_WARNING | wxOK | wxNO); msg_dlg.SetButtonLabel(wxID_OK, _L("Keep it On")); - msg_dlg.SetButtonLabel(wxID_CANCEL, _L("Turn it Off")); - if (msg_dlg.ShowModal() != wxID_CANCEL) { - return; - } + msg_dlg.SetButtonLabel(wxID_NO, _L("Still turn it Off")); + if (msg_dlg.ShowModal() != wxID_NO) { return; } } m_switch_lamp->SetValue(false); @@ -4882,20 +4685,18 @@ void StatusPanel::on_lamp_switch(wxCommandEvent &event) void StatusPanel::on_switch_vcamera(wxMouseEvent &event) { - //if (!obj) return; - //bool value = m_recording_button->get_switch_status(); - //obj->command_ipcam_record(!value); + // if (!obj) return; + // bool value = m_recording_button->get_switch_status(); + // obj->command_ipcam_record(!value); m_media_play_ctrl->ToggleStream(); show_vcamera = m_media_play_ctrl->IsStreaming(); - if (m_camera_popup) - m_camera_popup->sync_vcamera_state(show_vcamera); + if (m_camera_popup) m_camera_popup->sync_vcamera_state(show_vcamera); } -void StatusPanel::on_camera_enter(wxMouseEvent& event) +void StatusPanel::on_camera_enter(wxMouseEvent &event) { if (obj) { - if (m_camera_popup == nullptr) - m_camera_popup = std::make_shared(this); + if (m_camera_popup == nullptr) m_camera_popup = std::make_shared(this); m_camera_popup->check_func_supported(obj); m_camera_popup->sync_vcamera_state(show_vcamera); m_camera_popup->Bind(EVT_VCAMERA_SWITCH, &StatusPanel::on_switch_vcamera, this); @@ -4905,10 +4706,10 @@ void StatusPanel::on_camera_enter(wxMouseEvent& event) sdcard_hint_dlg->update_text(_L("Can't start this without storage.")); } sdcard_hint_dlg->on_show(); - }); - wxWindow* ctrl = (wxWindow*)event.GetEventObject(); - wxPoint pos = ctrl->ClientToScreen(wxPoint(0, 0)); - wxSize sz = ctrl->GetSize(); + }); + wxWindow *ctrl = (wxWindow *) event.GetEventObject(); + wxPoint pos = ctrl->ClientToScreen(wxPoint(0, 0)); + wxSize sz = ctrl->GetSize(); pos.x += sz.x; pos.y += sz.y; m_camera_popup->SetPosition(pos); @@ -4917,28 +4718,14 @@ void StatusPanel::on_camera_enter(wxMouseEvent& event) } } -void StatusPanel::on_camera_leave(wxMouseEvent& event) +void StatusPanel::on_camera_leave(wxMouseEvent &event) { - if (obj && m_camera_popup) { - m_camera_popup->Dismiss(); - } + if (obj && m_camera_popup) { m_camera_popup->Dismiss(); } } -void StatusPanel::on_auto_leveling(wxCommandEvent &event) -{ - if (obj) obj->command_auto_leveling(); -} - -void StatusPanel::on_xyz_abs(wxCommandEvent &event) -{ - if (obj) obj->command_xyz_abs(); -} - - void StatusPanel::on_nozzle_selected(wxCommandEvent &event) { if (obj) { - /*Enable switch head while printing is paused STUDIO-9789*/ if ((obj->is_in_printing() && !obj->is_in_printing_pause()) || obj->ams_status_main == AMS_STATUS_MAIN_FILAMENT_CHANGE) { MessageDialog dlg(nullptr, _L("The printer is busy on other print job"), _L("Error"), wxICON_WARNING | wxOK); @@ -4947,16 +4734,13 @@ void StatusPanel::on_nozzle_selected(wxCommandEvent &event) } auto nozzle_id = event.GetInt(); - if (obj->GetCtrl()->command_select_extruder(nozzle_id) == 0) - { - return; - } + if (obj->GetCtrl()->command_select_extruder(nozzle_id) == 0) { return; } } m_nozzle_btn_panel->Enable(); } -void StatusPanel::on_show_print_options(wxCommandEvent& event) +void StatusPanel::on_show_print_options(wxCommandEvent &event) { if (obj) { // Always show print options dialog for all machines @@ -4965,8 +4749,7 @@ void StatusPanel::on_show_print_options(wxCommandEvent& event) print_options_dlg->update_machine_obj(obj); print_options_dlg->update_options(obj); print_options_dlg->ShowModal(); - } - else { + } else { print_options_dlg->update_machine_obj(obj); print_options_dlg->update_options(obj); print_options_dlg->ShowModal(); @@ -4974,19 +4757,18 @@ void StatusPanel::on_show_print_options(wxCommandEvent& event) } } -void StatusPanel::on_show_safety_options(wxCommandEvent& event) +void StatusPanel::on_show_safety_options(wxCommandEvent &event) { if (obj) { std::string current_printer_type = obj->printer_type; - bool supports_safety = DevPrinterConfigUtil::support_safety_options(current_printer_type); + bool supports_safety = DevPrinterConfigUtil::support_safety_options(current_printer_type); if (supports_safety) { if (safety_options_dlg == nullptr) { safety_options_dlg = new SafetyOptionsDialog(this); safety_options_dlg->update_machine_obj(obj); safety_options_dlg->update_options(obj); safety_options_dlg->ShowModal(); - } - else { + } else { safety_options_dlg->update_machine_obj(obj); safety_options_dlg->update_options(obj); safety_options_dlg->ShowModal(); @@ -5002,18 +4784,17 @@ void StatusPanel::on_show_parts_options(wxCommandEvent &event) print_parts_dlg = new PrinterPartsDialog(this); print_parts_dlg->update_machine_obj(obj); print_parts_dlg->ShowModal(); - } - else { + } else { print_parts_dlg->update_machine_obj(obj); print_parts_dlg->ShowModal(); } } } -void StatusPanel::update_printer_parts_options(MachineObject* obj_) +void StatusPanel::update_printer_parts_options(MachineObject *obj_) { - if(obj_){ - if(print_parts_dlg && print_parts_dlg->IsShown()){ + if (obj_) { + if (print_parts_dlg && print_parts_dlg->IsShown()) { print_parts_dlg->update_machine_obj(obj_); print_parts_dlg->UpdateNozzleInfo(); } @@ -5052,21 +4833,19 @@ bool StatusPanel::is_stage_list_info_changed(MachineObject *obj) void StatusPanel::set_default() { BOOST_LOG_TRIVIAL(trace) << "status_panel: set_default"; - obj = nullptr; - last_subtask = nullptr; - last_tray_exist_bits = -1; - speed_lvl = 1; - speed_lvl_timeout = 0; - m_switch_lamp_timeout = 0; - m_temp_nozzle_timeout = 0; - m_temp_nozzle_deputy_timeout = 0; - m_temp_bed_timeout = 0; - m_temp_chamber_timeout = 0; - m_switch_nozzle_fan_timeout = 0; + obj = nullptr; + last_subtask = nullptr; + last_tray_exist_bits = -1; + speed_lvl = 1; + speed_lvl_timeout = 0; + m_switch_lamp_timeout = 0; + m_temp_nozzle_timeout = 0; + m_temp_nozzle_deputy_timeout = 0; + m_temp_bed_timeout = 0; + m_temp_chamber_timeout = 0; + m_switch_nozzle_fan_timeout = 0; m_switch_printing_fan_timeout = 0; - m_switch_cham_fan_timeout = 0; - m_show_ams_group = false; - m_show_filament_group = false; + m_switch_cham_fan_timeout = 0; reset_printing_values(); m_bitmap_timelapse_img->Hide(); @@ -5078,7 +4857,6 @@ void StatusPanel::set_default() m_safety_btn->Show(); m_parts_btn->Show(); - if (m_panel_control_title) { m_panel_control_title->Layout(); m_panel_control_title->Refresh(); @@ -5089,6 +4867,9 @@ void StatusPanel::set_default() m_ams_control->Hide(); m_ams_control_box->Hide(); m_ams_control->Reset(); + m_ams_rack_switch->updateState("left"); + m_ams_rack_switch->Hide(); + m_panel_nozzle_rack->Hide(); m_scale_panel->Hide(); m_filament_load_box->Hide(); m_filament_step->Hide(); @@ -5103,11 +4884,8 @@ void StatusPanel::show_status(int status) if (last_status == status) return; last_status = status; - if (((status & (int) MonitorStatus::MONITOR_DISCONNECTED) != 0) - || ((status & (int) MonitorStatus::MONITOR_DISCONNECTED_SERVER) != 0) - || ((status & (int)MonitorStatus::MONITOR_CONNECTING) != 0) - || ((status & (int)MonitorStatus::MONITOR_NO_PRINTER) != 0) - ) { + if (((status & (int) MonitorStatus::MONITOR_DISCONNECTED) != 0) || ((status & (int) MonitorStatus::MONITOR_DISCONNECTED_SERVER) != 0) || + ((status & (int) MonitorStatus::MONITOR_CONNECTING) != 0) || ((status & (int) MonitorStatus::MONITOR_NO_PRINTER) != 0)) { show_printing_status(false, false); m_calibration_btn->Disable(); m_options_btn->Disable(); @@ -5124,10 +4902,7 @@ void StatusPanel::show_status(int status) } } -void StatusPanel::set_hold_count(int& count) -{ - count = COMMAND_TIMEOUT; -} +void StatusPanel::set_hold_count(int &count) { count = COMMAND_TIMEOUT; } void StatusPanel::rescale_camera_icons() { @@ -5136,21 +4911,19 @@ void StatusPanel::rescale_camera_icons() m_setting_button->msw_rescale(); - - m_bitmap_sdcard_state_abnormal = ScalableBitmap(this, wxGetApp().dark_mode()?"sdcard_state_abnormal_dark":"sdcard_state_abnormal", 20); - m_bitmap_sdcard_state_normal = ScalableBitmap(this, wxGetApp().dark_mode()?"sdcard_state_normal_dark":"sdcard_state_normal", 20); - m_bitmap_sdcard_state_no = ScalableBitmap(this, wxGetApp().dark_mode()?"sdcard_state_no_dark":"sdcard_state_no", 20); - m_bitmap_recording_on = ScalableBitmap(this, wxGetApp().dark_mode()?"monitor_recording_on_dark":"monitor_recording_on", 20); - m_bitmap_recording_off = ScalableBitmap(this, wxGetApp().dark_mode()?"monitor_recording_off_dark":"monitor_recording_off", 20); - m_bitmap_timelapse_on = ScalableBitmap(this, wxGetApp().dark_mode()?"monitor_timelapse_on_dark":"monitor_timelapse_on", 20); - m_bitmap_timelapse_off = ScalableBitmap(this, wxGetApp().dark_mode()?"monitor_timelapse_off_dark":"monitor_timelapse_off", 20); - m_bitmap_vcamera_on = ScalableBitmap(this, wxGetApp().dark_mode()?"monitor_vcamera_on_dark":"monitor_vcamera_on", 20); - m_bitmap_vcamera_off = ScalableBitmap(this, wxGetApp().dark_mode()?"monitor_vcamera_off_dark":"monitor_vcamera_off", 20); + m_bitmap_sdcard_state_abnormal = ScalableBitmap(this, wxGetApp().dark_mode() ? "sdcard_state_abnormal_dark" : "sdcard_state_abnormal", 20); + m_bitmap_sdcard_state_normal = ScalableBitmap(this, wxGetApp().dark_mode() ? "sdcard_state_normal_dark" : "sdcard_state_normal", 20); + m_bitmap_sdcard_state_no = ScalableBitmap(this, wxGetApp().dark_mode() ? "sdcard_state_no_dark" : "sdcard_state_no", 20); + m_bitmap_recording_on = ScalableBitmap(this, wxGetApp().dark_mode() ? "monitor_recording_on_dark" : "monitor_recording_on", 20); + m_bitmap_recording_off = ScalableBitmap(this, wxGetApp().dark_mode() ? "monitor_recording_off_dark" : "monitor_recording_off", 20); + m_bitmap_timelapse_on = ScalableBitmap(this, wxGetApp().dark_mode() ? "monitor_timelapse_on_dark" : "monitor_timelapse_on", 20); + m_bitmap_timelapse_off = ScalableBitmap(this, wxGetApp().dark_mode() ? "monitor_timelapse_off_dark" : "monitor_timelapse_off", 20); + m_bitmap_vcamera_on = ScalableBitmap(this, wxGetApp().dark_mode() ? "monitor_vcamera_on_dark" : "monitor_vcamera_on", 20); + m_bitmap_vcamera_off = ScalableBitmap(this, wxGetApp().dark_mode() ? "monitor_vcamera_off_dark" : "monitor_vcamera_off", 20); if (m_media_play_ctrl->IsStreaming()) { m_bitmap_vcamera_img->SetBitmap(m_bitmap_vcamera_on.bmp()); - } - else { + } else { m_bitmap_vcamera_img->SetBitmap(m_bitmap_vcamera_off.bmp()); } @@ -5195,22 +4968,21 @@ void StatusPanel::msw_rescale() m_project_task_panel->init_bitmaps(); m_project_task_panel->msw_rescale(); m_panel_monitoring_title->SetSize(wxSize(-1, FromDIP(PAGE_TITLE_HEIGHT))); - //m_staticText_monitoring->SetMinSize(wxSize(PAGE_TITLE_TEXT_WIDTH, PAGE_TITLE_HEIGHT)); + // m_staticText_monitoring->SetMinSize(wxSize(PAGE_TITLE_TEXT_WIDTH, PAGE_TITLE_HEIGHT)); m_bmToggleBtn_timelapse->Rescale(); m_panel_control_title->SetSize(wxSize(-1, FromDIP(PAGE_TITLE_HEIGHT))); - //m_staticText_control->SetMinSize(wxSize(-1, PAGE_TITLE_HEIGHT)); + // m_staticText_control->SetMinSize(wxSize(-1, PAGE_TITLE_HEIGHT)); m_media_play_ctrl->msw_rescale(); m_bpButton_xy->SetBitmap(m_bitmap_axis_home); m_bpButton_xy->SetMinSize(AXIS_MIN_SIZE); m_bpButton_xy->SetSize(AXIS_MIN_SIZE); m_temp_extruder_line->SetSize(wxSize(FromDIP(1), -1)); update_extruder_status(obj); - //m_bitmap_extruder_img->SetMinSize(EXTRUDER_IMAGE_SIZE); + // m_bitmap_extruder_img->SetMinSize(EXTRUDER_IMAGE_SIZE); for (Button *btn : m_buttons) { btn->Rescale(); } init_scaled_buttons(); - m_bpButton_xy->Rescale(); auto size = TEMP_CTRL_MIN_SIZE_ALIGN_ONE_ICON; if (obj && obj->GetExtderSystem()->GetTotalExtderCount() >= 2) size = TEMP_CTRL_MIN_SIZE_ALIGN_TWO_ICON; @@ -5224,13 +4996,9 @@ void StatusPanel::msw_rescale() m_tempCtrl_chamber->SetMinSize(size); m_tempCtrl_chamber->Rescale(); - for(int i = 0; i < m_extruder_book->GetPageCount(); i++) - { - ExtruderImage* ext_img = dynamic_cast (m_extruder_book->GetPage(i)); - if (ext_img) - { - ext_img->msw_rescale(); - } + for (int i = 0; i < m_extruder_book->GetPageCount(); i++) { + ExtruderImage *ext_img = dynamic_cast(m_extruder_book->GetPage(i)); + if (ext_img) { ext_img->msw_rescale(); } } m_bitmap_speed.msw_rescale(); @@ -5251,13 +5019,10 @@ void StatusPanel::msw_rescale() m_switch_fan->SetImages(m_bitmap_fan_on, m_bitmap_fan_off); m_switch_fan->Rescale(); - if (m_fan_control_popup) - { - m_fan_control_popup->msw_rescale(); - } + if (m_fan_control_popup) { m_fan_control_popup->msw_rescale(); } - //m_switch_fan->SetImages(m_bitmap_fan_on, m_bitmap_fan_off); - //m_switch_fan->Rescale(); + // m_switch_fan->SetImages(m_bitmap_fan_on, m_bitmap_fan_off); + // m_switch_fan->Rescale(); m_bpButton_z_10->Rescale(); m_bpButton_z_1->Rescale(); @@ -5266,9 +5031,9 @@ void StatusPanel::msw_rescale() m_extruder_switching_status->msw_rescale(); m_ams_control->msw_rescale(); + m_panel_nozzle_rack->Rescale(); // m_filament_step->Rescale(); - m_calibration_btn->SetMinSize(wxSize(-1, FromDIP(26))); m_calibration_btn->Rescale(); @@ -5287,7 +5052,18 @@ void StatusPanel::msw_rescale() Refresh(); } -void StatusPanel::update_filament_loading_panel(MachineObject* obj) +void StatusPanel::update_rack(MachineObject *obj) +{ + if (obj && obj->GetNozzleSystem()->GetNozzleRack()->IsSupported()) { + m_ams_rack_switch->Show(); + m_panel_nozzle_rack->UpdateRackInfo(obj->GetNozzleSystem()->GetNozzleRack()); + } else { + m_ams_rack_switch->Show(false); + m_panel_nozzle_rack->Show(false); + } +} + +void StatusPanel::update_filament_loading_panel(MachineObject *obj) { if (!obj) { show_filament_load_group(false); @@ -5295,7 +5071,7 @@ void StatusPanel::update_filament_loading_panel(MachineObject* obj) } bool ams_loading_state = false; - auto ams_status_sub = obj->ams_status_sub; + auto ams_status_sub = obj->ams_status_sub; if (obj->is_enable_np) { ams_loading_state = obj->GetExtderSystem()->IsBusyLoading(); @@ -5306,14 +5082,12 @@ void StatusPanel::update_filament_loading_panel(MachineObject* obj) if (ams_loading_state) { update_load_with_temp(); - const std::string& cur_ams_id = obj->GetExtderSystem()->GetCurrentAmsId(); - const std::string& cur_tray_id = obj->GetExtderSystem()->GetCurrentSlotId(); - if (!cur_ams_id.empty() && !cur_tray_id.empty()) { - m_filament_step->updateID(std::atoi(cur_ams_id.c_str()), std::atoi(cur_tray_id.c_str())); - } + const std::string &cur_ams_id = obj->GetExtderSystem()->GetCurrentAmsId(); + const std::string &cur_tray_id = obj->GetExtderSystem()->GetCurrentSlotId(); + if (!cur_ams_id.empty() && !cur_tray_id.empty()) { m_filament_step->updateID(std::atoi(cur_ams_id.c_str()), std::atoi(cur_tray_id.c_str())); } - auto loading_ext = obj->GetExtderSystem()->GetLoadingExtder(); - auto tar = loading_ext ? loading_ext->GetSlotTarget() : DevAmsSlotInfo(); + auto loading_ext = obj->GetExtderSystem()->GetLoadingExtder(); + auto tar = loading_ext ? loading_ext->GetSlotTarget() : DevAmsSlotInfo(); bool busy_for_vt_loading = (tar.ams_id == std::to_string(VIRTUAL_TRAY_MAIN_ID) || tar.ams_id == std::to_string(VIRTUAL_TRAY_DEPUTY_ID)) && !obj->is_target_slot_unload(); if (busy_for_vt_loading) { // wait to heat hotend @@ -5383,7 +5157,7 @@ void StatusPanel::update_filament_loading_panel(MachineObject* obj) m_filament_step->SetFilamentStep(FilamentStep::STEP_CHECK_POSITION, FilamentStepType::STEP_TYPE_UNLOAD); } } else if (ams_status_sub == 0x09) { - //just wait + // just wait } else if (ams_status_sub == 0x0B) { if (!obj->is_target_slot_unload()) { m_filament_step->SetFilamentStep(FilamentStep::STEP_CHECK_POSITION, FilamentStepType::STEP_TYPE_LOAD); @@ -5440,14 +5214,12 @@ ScoreDialog::ScoreDialog(wxWindow *parent, ScoreData *score_data) wxBoxSizer *m_main_sizer = get_main_sizer(score_data->local_to_url_image, score_data->comment_text); - m_image_url_paths = score_data->image_url_paths; - + m_image_url_paths = score_data->image_url_paths; this->SetSizer(m_main_sizer); Fit(); Layout(); wxGetApp().UpdateDlgDarkUI(this); - } ScoreDialog::~ScoreDialog() {} @@ -5459,15 +5231,11 @@ void ScoreDialog::OnBitmapClicked(wxMouseEvent &event) wxStaticBitmap *clickedBitmap = dynamic_cast(event.GetEventObject()); if (m_image.find(clickedBitmap) != m_image.end()) { if (!m_image[clickedBitmap].is_selected) { - for (auto panel : m_image[clickedBitmap].image_broad) { - panel->Show(); - } + for (auto panel : m_image[clickedBitmap].image_broad) { panel->Show(); } m_image[clickedBitmap].is_selected = true; m_selected_image_list.insert(clickedBitmap); } else { - for (auto panel : m_image[clickedBitmap].image_broad) { - panel->Hide(); - } + for (auto panel : m_image[clickedBitmap].image_broad) { panel->Hide(); } m_image[clickedBitmap].is_selected = false; m_selected_image_list.erase(clickedBitmap); m_selected_image_list.erase(clickedBitmap); @@ -5479,10 +5247,9 @@ void ScoreDialog::OnBitmapClicked(wxMouseEvent &event) m_delete_photo->Show(); Fit(); Layout(); - } - std::set > ScoreDialog::add_need_upload_imgs() +std::set> ScoreDialog::add_need_upload_imgs() { std::set> need_upload_images; for (auto bitmap : m_image) { @@ -5494,8 +5261,6 @@ void ScoreDialog::OnBitmapClicked(wxMouseEvent &event) return need_upload_images; } - - std::pair ScoreDialog::create_local_thumbnail(wxString &local_path) { std::pair bitmap_to_image_msg; @@ -5512,7 +5277,7 @@ std::pair ScoreDialog::create_local_thu m_image_sizer->Add(create_broad_sizer(imageCtrl, cur_image_msg), 0, wxALL, 5); - bitmap_to_image_msg.first = imageCtrl; + bitmap_to_image_msg.first = imageCtrl; bitmap_to_image_msg.second = cur_image_msg; BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": local picture is download"; return bitmap_to_image_msg; @@ -5528,7 +5293,6 @@ std::pair ScoreDialog::create_oss_thumb cur_image_msg.img_url_paths = oss_path; cur_image_msg.is_uploaded = true; - wxImage image(Slic3r::resources_dir() + "/images/oss_picture_loading.png", wxBITMAP_TYPE_ANY); wxStaticBitmap *imageCtrl = new wxStaticBitmap(this, wxID_ANY, wxBitmap(image.Rescale(FromDIP(80), FromDIP(60))), wxDefaultPosition, wxDefaultSize, 0); imageCtrl->Bind(wxEVT_LEFT_DOWN, &ScoreDialog::OnBitmapClicked, this); @@ -5551,7 +5315,8 @@ std::pair ScoreDialog::create_oss_thumb .on_error([this, imageCtrl, &oss_path](std::string body, std::string error, unsigned status) { BOOST_LOG_TRIVIAL(info) << "load oss picture failed, oss path: " << oss_path << " status:" << status << " error:" << error; CallAfter([this, imageCtrl]() { update_static_bitmap(imageCtrl, fail_image); }); - }).perform(); + }) + .perform(); m_image_sizer->Add(create_broad_sizer(imageCtrl, cur_image_msg), 0, wxALL, 5); @@ -5561,15 +5326,15 @@ std::pair ScoreDialog::create_oss_thumb return bitmap_to_image_msg; } -void ScoreDialog::update_static_bitmap(wxStaticBitmap* static_bitmap, wxImage image) +void ScoreDialog::update_static_bitmap(wxStaticBitmap *static_bitmap, wxImage image) { static_bitmap->SetBitmap(wxBitmap(image.Rescale(FromDIP(80), FromDIP(60)))); Layout(); Fit(); - //Refresh(); + // Refresh(); } -wxBoxSizer *ScoreDialog::create_broad_sizer(wxStaticBitmap *bitmap, ImageMsg& cur_image_msg) +wxBoxSizer *ScoreDialog::create_broad_sizer(wxStaticBitmap *bitmap, ImageMsg &cur_image_msg) { // tb: top and bottom lr: left and right auto m_image_tb_broad = new wxBoxSizer(wxVERTICAL); @@ -5607,7 +5372,8 @@ wxBoxSizer *ScoreDialog::create_broad_sizer(wxStaticBitmap *bitmap, ImageMsg& cu return m_image_tb_broad; } -void ScoreDialog::init() { +void ScoreDialog::init() +{ SetBackgroundColour(*wxWHITE); SetMinSize(wxSize(FromDIP(540), FromDIP(380))); @@ -5617,8 +5383,9 @@ void ScoreDialog::init() { SetIcon(wxIcon(encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO)); } -wxBoxSizer *ScoreDialog::get_score_sizer() { - wxBoxSizer *score_sizer = new wxBoxSizer(wxHORIZONTAL); +wxBoxSizer *ScoreDialog::get_score_sizer() +{ + wxBoxSizer *score_sizer = new wxBoxSizer(wxHORIZONTAL); wxStaticText *static_score_text = new wxStaticText(this, wxID_ANY, _L("Rate"), wxDefaultPosition, wxDefaultSize, 0); static_score_text->Wrap(-1); score_sizer->Add(static_score_text, 1, wxEXPAND | wxLEFT, FromDIP(24)); @@ -5677,8 +5444,9 @@ wxBoxSizer *ScoreDialog::get_star_sizer() return static_score_star_sizer; } -wxBoxSizer* ScoreDialog::get_comment_text_sizer() { - wxBoxSizer* m_comment_sizer = new wxBoxSizer(wxHORIZONTAL); +wxBoxSizer *ScoreDialog::get_comment_text_sizer() +{ + wxBoxSizer *m_comment_sizer = new wxBoxSizer(wxHORIZONTAL); wxStaticText *static_comment_text = new wxStaticText(this, wxID_ANY, _L("Comment"), wxDefaultPosition, wxDefaultSize, 0); static_comment_text->Wrap(-1); m_comment_sizer->Add(static_comment_text, 1, wxEXPAND | wxLEFT, FromDIP(24)); @@ -5686,16 +5454,15 @@ wxBoxSizer* ScoreDialog::get_comment_text_sizer() { return m_comment_sizer; } -void ScoreDialog::create_comment_text(const wxString& comment) { +void ScoreDialog::create_comment_text(const wxString &comment) +{ m_comment_text = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition, wxSize(FromDIP(492), FromDIP(104)), wxTE_MULTILINE); m_comment_text->SetBackgroundColour(wxColor(*wxWHITE)); - if (!comment.empty()) { - m_comment_text->SetValue(comment); - } + if (!comment.empty()) { m_comment_text->SetValue(comment); } m_comment_text->SetHint(_L("Rate this print")); m_comment_text->SetBackgroundColour(*wxWHITE); - //m_comment_text->SetForegroundColour(wxColor("#BBBBBB")); + // m_comment_text->SetForegroundColour(wxColor("#BBBBBB")); m_comment_text->SetMinSize(wxSize(FromDIP(492), FromDIP(104))); m_comment_text->Bind(wxEVT_SET_FOCUS, [this](auto &event) { @@ -5708,20 +5475,21 @@ void ScoreDialog::create_comment_text(const wxString& comment) { }); } -wxBoxSizer *ScoreDialog::get_photo_btn_sizer() { - wxBoxSizer * m_photo_sizer = new wxBoxSizer(wxHORIZONTAL); - ScalableBitmap little_photo = wxGetApp().dark_mode() ? ScalableBitmap(this, "single_little_photo_dark", 20) : ScalableBitmap(this, "single_little_photo", 20); - wxStaticBitmap *little_photo_img = new wxStaticBitmap(this, wxID_ANY, little_photo.bmp(), wxDefaultPosition, wxSize(FromDIP(20), FromDIP(20)), 0); +wxBoxSizer *ScoreDialog::get_photo_btn_sizer() +{ + wxBoxSizer *m_photo_sizer = new wxBoxSizer(wxHORIZONTAL); + ScalableBitmap little_photo = wxGetApp().dark_mode() ? ScalableBitmap(this, "single_little_photo_dark", 20) : ScalableBitmap(this, "single_little_photo", 20); + wxStaticBitmap *little_photo_img = new wxStaticBitmap(this, wxID_ANY, little_photo.bmp(), wxDefaultPosition, wxSize(FromDIP(20), FromDIP(20)), 0); m_photo_sizer->Add(little_photo_img, 0, wxEXPAND | wxLEFT, FromDIP(24)); m_add_photo = new Label(this, _L("Add Photo")); m_add_photo->SetBackgroundColour(*wxWHITE); - //m_add_photo->SetForegroundColour(wxColor("#898989")); + // m_add_photo->SetForegroundColour(wxColor("#898989")); m_add_photo->SetSize(wxSize(-1, FromDIP(20))); m_photo_sizer->Add(m_add_photo, 0, wxEXPAND | wxLEFT, FromDIP(12)); m_delete_photo = new Label(this, _L("Delete Photo")); m_delete_photo->SetBackgroundColour(*wxWHITE); - //m_delete_photo->SetForegroundColour(wxColor("#898989")); + // m_delete_photo->SetForegroundColour(wxColor("#898989")); m_delete_photo->SetSize(wxSize(-1, FromDIP(20))); m_photo_sizer->Add(m_delete_photo, 0, wxEXPAND | wxLEFT, FromDIP(12)); m_delete_photo->Hide(); @@ -5735,9 +5503,9 @@ wxBoxSizer *ScoreDialog::get_photo_btn_sizer() { wxArrayString filePaths; openFileDialog.GetPaths(filePaths); - //wxArrayString filePaths_reduction; + // wxArrayString filePaths_reduction; std::vector> local_path; - for (int i = 0; i < filePaths.GetCount(); i++) { //It's ugly, but useful + for (int i = 0; i < filePaths.GetCount(); i++) { // It's ugly, but useful bool is_repeat = false; for (auto image : m_image) { if (filePaths[i] == image.second.local_image_url) { @@ -5747,11 +5515,8 @@ wxBoxSizer *ScoreDialog::get_photo_btn_sizer() { } if (!is_repeat) { local_path.push_back(std::make_pair(filePaths[i], "")); - if (local_path.size() + m_image.size() > m_photo_nums) { - break; - } + if (local_path.size() + m_image.size() > m_photo_nums) { break; } } - } load_photo(local_path); @@ -5761,27 +5526,25 @@ wxBoxSizer *ScoreDialog::get_photo_btn_sizer() { this->Layout(); }); - m_delete_photo->Bind(wxEVT_LEFT_DOWN, [this](auto &e) { - for (auto it = m_selected_image_list.begin(); it != m_selected_image_list.end();) { - auto bitmap = *it; - m_image_sizer->Detach(m_image[bitmap].image_tb_broad); - m_image[bitmap].image_tb_broad->DeleteWindows(); + m_delete_photo->Bind(wxEVT_LEFT_DOWN, [this](auto &e) { + for (auto it = m_selected_image_list.begin(); it != m_selected_image_list.end();) { + auto bitmap = *it; + m_image_sizer->Detach(m_image[bitmap].image_tb_broad); + m_image[bitmap].image_tb_broad->DeleteWindows(); - m_image.erase(bitmap); - it = m_selected_image_list.erase(it); + m_image.erase(bitmap); + it = m_selected_image_list.erase(it); + } + m_image_url_paths.clear(); + for (const std::pair &bitmap : m_image) { + if (bitmap.second.is_uploaded) { + if (!bitmap.second.img_url_paths.empty()) { m_image_url_paths.push_back(bitmap.second.img_url_paths); } } - m_image_url_paths.clear(); - for (const std::pair &bitmap : m_image) { - if (bitmap.second.is_uploaded) { - if (!bitmap.second.img_url_paths.empty()) { - m_image_url_paths.push_back(bitmap.second.img_url_paths); - } - } - } - m_delete_photo->Hide(); - Layout(); - Fit(); - }); + } + m_delete_photo->Hide(); + Layout(); + Fit(); + }); return m_photo_sizer; } @@ -5819,7 +5582,7 @@ wxBoxSizer *ScoreDialog::get_button_sizer() std::string comment = into_u8(m_comment_text->GetValue()); unsigned int http_code; std::string http_error; - wxString error_info; + wxString error_info; if (!need_upload_images.empty()) { std::string config; @@ -5833,11 +5596,14 @@ wxBoxSizer *ScoreDialog::get_button_sizer() int need_upload_nums = need_upload_images.size(); int upload_nums = 0; int upload_failed_nums = 0; - ProgressDialog *progress_dialog = new ProgressDialog(_L("Upload Pictures"), _L("Number of images successfully uploaded") + ": " + std::to_string(upload_nums) + "/" + std::to_string(need_upload_nums), need_upload_nums, this); + ProgressDialog *progress_dialog = new ProgressDialog(_L("Upload Pictures"), + _L("Number of images successfully uploaded") + ": " + std::to_string(upload_nums) + "/" + + std::to_string(need_upload_nums), + need_upload_nums, this); for (std::set>::iterator it = need_upload_images.begin(); it != need_upload_images.end();) { std::pair need_upload = *it; - std::string need_upload_uf8 = into_u8(need_upload.second); - //Local path when incoming, cloud path when outgoing + std::string need_upload_uf8 = into_u8(need_upload.second); + // Local path when incoming, cloud path when outgoing ret = wxGetApp().getAgent()->put_rating_picture_oss(config, need_upload_uf8, m_model_id, m_profile_id, http_code, http_error); std::unordered_map::iterator iter; switch (ret) { @@ -5850,13 +5616,15 @@ wxBoxSizer *ScoreDialog::get_button_sizer() m_image_url_paths.push_back(need_upload_uf8); } it++; - progress_dialog->Update(upload_nums, _L("Number of images successfully uploaded") + ": " + std::to_string(upload_nums) + "/" + std::to_string(need_upload_nums)); + progress_dialog->Update(upload_nums, + _L("Number of images successfully uploaded") + ": " + std::to_string(upload_nums) + "/" + std::to_string(need_upload_nums)); progress_dialog->Fit(); BOOST_LOG_TRIVIAL(info) << "put_rating_picture_oss: model_id [" << m_model_id << "] profile_id [" << m_profile_id << "] http_code [" << http_code << "] http_error [" << http_error << "] config [" << config << "] image_path [" << need_upload.second << "]"; break; case -1: - error_info += need_upload.second + _L(" upload failed").ToUTF8().data() + "\n\thttp code:" + std::to_string(http_code) + "\n\thttp_error:" + http_error + "\n"; + error_info += need_upload.second + _L(" upload failed").ToUTF8().data() + "\n\thttp code:" + std::to_string(http_code) + "\n\thttp_error:" + http_error + + "\n"; m_upload_status_code = StatusCode::UPLOAD_IMG_FAILED; ++it; break; @@ -5886,9 +5654,7 @@ wxBoxSizer *ScoreDialog::get_button_sizer() if (m_upload_status_code == StatusCode::UPLOAD_IMG_FAILED) { std::string upload_failed_images = into_u8(_L("The following issues occurred during the process of uploading images. Do you want to ignore them?\n\n")); MessageDialog dlg_info(this, upload_failed_images + error_info, wxString(_L("info")), wxOK | wxNO | wxCENTER); - if (dlg_info.ShowModal() == wxID_OK) { - m_upload_status_code = StatusCode::UPLOAD_PROGRESS; - } + if (dlg_info.ShowModal() == wxID_OK) { m_upload_status_code = StatusCode::UPLOAD_PROGRESS; } } } } @@ -5961,13 +5727,11 @@ void ScoreDialog::load_photo(const std::vector> ImageMsg cur_image_msg; - if (filePath.empty()) { // local img path is empty, oss url path is exist + if (filePath.empty()) { // local img path is empty, oss url path is exist std::string oss_url_path = local_to_url_path.second; - //to do: load oss image, create wxStaticBitmap + // to do: load oss image, create wxStaticBitmap - if (!oss_url_path.empty()) { - m_image.insert(create_oss_thumbnail(oss_url_path)); - } + if (!oss_url_path.empty()) { m_image.insert(create_oss_thumbnail(oss_url_path)); } continue; } else { m_image.insert(create_local_thumbnail(filePath)); @@ -5978,7 +5742,6 @@ void ScoreDialog::load_photo(const std::vector> dlg_info_up_to_8->ShowModal(); break; } - } } @@ -6018,9 +5781,7 @@ wxBoxSizer *ScoreDialog::get_main_sizer(const std::vectorAdd(m_photo_sizer, 0, wxEXPAND | wxTOP, FromDIP(8)); m_image_sizer = new wxGridSizer(5, FromDIP(5), FromDIP(5)); - if (!images.empty()) { - load_photo(images); - } + if (!images.empty()) { load_photo(images); } m_main_sizer->Add(m_image_sizer, 0, wxEXPAND | wxLEFT, FromDIP(24)); m_main_sizer->Add(0, 0, 1, wxEXPAND, 0); @@ -6030,16 +5791,17 @@ wxBoxSizer *ScoreDialog::get_main_sizer(const std::vectorGetValue(); - score_data.image_url_paths = m_image_url_paths; + score_data.rating_id = m_rating_id; + score_data.design_id = m_design_id; + score_data.model_id = m_model_id; + score_data.profile_id = m_profile_id; + score_data.star_count = m_star_count; + score_data.success_printed = m_success_printed; + score_data.comment_text = m_comment_text->GetValue(); + score_data.image_url_paths = m_image_url_paths; for (auto img : m_image) { score_data.local_to_url_image.push_back(std::make_pair(img.second.local_image_url, img.second.img_url_paths)); } return score_data; @@ -6047,10 +5809,7 @@ ScoreData ScoreDialog::get_score_data() { void ScoreDialog::set_comment(std::string comment) { - if (m_comment_text) { - - m_comment_text->SetValue(wxString::FromUTF8(comment)); - } + if (m_comment_text) { m_comment_text->SetValue(wxString::FromUTF8(comment)); } } void ScoreDialog::set_cloud_bitmap(std::vector cloud_bitmaps) @@ -6072,8 +5831,7 @@ RectTextPanel::RectTextPanel(wxWindow *parent) : wxPanel(parent) void RectTextPanel::setText(const wxString text) { - if (this->text != text) - { + if (this->text != text) { this->text = text; Rescale(); } @@ -6090,9 +5848,9 @@ void RectTextPanel::Rescale() Refresh(); } -void RectTextPanel::OnPaint(wxPaintEvent &event) { - - const auto& size = GetSize(); +void RectTextPanel::OnPaint(wxPaintEvent &event) +{ + const auto &size = GetSize(); wxPaintDC dc(this); @@ -6103,5 +5861,4 @@ void RectTextPanel::OnPaint(wxPaintEvent &event) { dc.DrawText(text, wxPoint(FromDIP(2), FromDIP(2))); } -} // namespace GUI -} // namespace Slic3r +}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/StatusPanel.hpp b/src/slic3r/GUI/StatusPanel.hpp index 456d12a..dcd7954 100644 --- a/src/slic3r/GUI/StatusPanel.hpp +++ b/src/slic3r/GUI/StatusPanel.hpp @@ -42,13 +42,14 @@ class StepIndicator; #define COMMAND_TIMEOUT 5 namespace Slic3r { - + class DevExtderSystem; namespace GUI { // Previous definitions class MessageDialog; +class wgtDeviceNozzleRack; enum CameraRecordingStatus { RECORDING_NONE, @@ -369,6 +370,7 @@ public: void set_brightness_value(int value) { m_brightness_value = value; } void set_plate_index(int plate_idx = -1); void market_scoring_show(bool show); + bool is_market_scoring_show(); public: ScalableButton* get_abort_button() {return m_button_abort;}; @@ -513,12 +515,10 @@ protected: float m_fixed_aspect_ratio{1.8}; AxisCtrlButton *m_bpButton_xy; - //wxStaticText * m_staticText_xy; Button * m_bpButton_z_10; Button * m_bpButton_z_1; Button * m_bpButton_z_down_1; Button * m_bpButton_z_down_10; - //Button * m_button_unload; wxStaticText * m_staticText_z_tip; Label * m_extruder_label; Button * m_bpButton_e_10; @@ -527,16 +527,18 @@ protected: wxPanel * m_temp_temp_line; wxPanel * m_temp_extruder_line; - wxBoxSizer* m_ams_list; - wxStaticText * m_ams_debug; - bool m_show_ams_group{false}; bool m_show_filament_group{ false }; + /* switch*/ + SwitchBoard* m_ams_rack_switch; + AMSControl* m_ams_control; StaticBox* m_ams_control_box; wxStaticBitmap *m_ams_extruder_img; wxStaticBitmap* m_bitmap_extruder_img; + wgtDeviceNozzleRack* m_panel_nozzle_rack{ nullptr }; + wxPanel * m_panel_separator_right; wxPanel * m_panel_separotor_bottom; wxGridBagSizer *m_tasklist_info_sizer{nullptr}; @@ -610,14 +612,19 @@ public: void reset_temp_misc_control(); int before_error_code = 0; int skip_print_error = 0; - wxBoxSizer *create_ams_group(wxWindow *parent); - wxBoxSizer *create_settings_group(wxWindow *parent); + StaticBox* create_ams_group(wxWindow *parent); + wxBoxSizer* create_settings_group(wxWindow *parent); wxBoxSizer* create_filament_group(wxWindow* parent); void expand_filament_loading(wxMouseEvent &e); void show_ams_group(bool show = true); void show_filament_load_group(bool show = true); MediaPlayCtrl* get_media_play_ctrl() {return m_media_play_ctrl;}; + + void jump_to_Rack(); + +private: + void on_ams_rack_switch(wxCommandEvent& event); }; @@ -748,9 +755,6 @@ protected: void on_switch_vcamera(wxMouseEvent &event); void on_camera_enter(wxMouseEvent &event); void on_camera_leave(wxMouseEvent& event); - void on_auto_leveling(wxCommandEvent &event); - void on_xyz_abs(wxCommandEvent &event); - void on_show_parts_options(wxCommandEvent& event); /* print options */ @@ -764,6 +768,7 @@ protected: /* update apis */ void update(MachineObject* obj); + void show_printing_status(bool ctrl_area = true, bool temp_area = true); void update_left_time(int mc_left_time); void update_basic_print_data(bool def = false); @@ -779,9 +784,11 @@ protected: void update_extruder_status(MachineObject* obj); void update_ams_control_state(std::string ams_id, std::string slot_id); + void update_rack(MachineObject* obj); void update_cali(MachineObject* obj); void update_calib_bitmap(); + void update_market_scoring(bool show); void reset_printing_values(); void on_webrequest_state(wxWebRequestEvent &evt); bool is_task_changed(MachineObject* obj); diff --git a/src/slic3r/GUI/SyncAmsInfoDialog.cpp b/src/slic3r/GUI/SyncAmsInfoDialog.cpp index 50a89cb..890600e 100644 --- a/src/slic3r/GUI/SyncAmsInfoDialog.cpp +++ b/src/slic3r/GUI/SyncAmsInfoDialog.cpp @@ -1240,15 +1240,9 @@ bool SyncAmsInfoDialog::do_ams_mapping(MachineObject *obj_) } // single nozzle else { - if (obj_->is_support_amx_ext_mix_mapping()) { - map_opt = {false, true, false, true}; // four values: use_left_ams, use_right_ams, use_left_ext, use_right_ext - filament_result = DevMappingUtil::ams_filament_mapping(obj_, m_filaments, m_ams_mapping_result, map_opt, std::vector(), - wxGetApp().app_config->get_bool("ams_sync_match_full_use_color_dist") ? false : true); - // auto_supply_with_ext(obj_->vt_slot); - } else { - map_opt = {false, true, false, false}; - filament_result = DevMappingUtil::ams_filament_mapping(obj_, m_filaments, m_ams_mapping_result, map_opt); - } + map_opt = { false, true, false, true }; // four values: use_left_ams, use_right_ams, use_left_ext, use_right_ext + filament_result = DevMappingUtil::ams_filament_mapping(obj_, m_filaments, m_ams_mapping_result, map_opt, std::vector(), + wxGetApp().app_config->get_bool("ams_sync_match_full_use_color_dist") ? false : true); } if (filament_result == 0) { @@ -1964,7 +1958,7 @@ bool SyncAmsInfoDialog::is_same_nozzle_type(std::string &filament_type, NozzleTy while (iter != m_materialList.end()) { Material * item = iter->second; auto m = item->item; - auto filament_nozzle_hrc = preset_bundle->get_required_hrc_by_filament_type(m->m_material_name.ToStdString()); + auto filament_nozzle_hrc = preset_bundle->get_required_hrc_by_filament_id(m->m_filament_id); if (abs(filament_nozzle_hrc) > abs(printer_nozzle_hrc)) { filament_type = m->m_material_name.ToStdString(); @@ -2335,20 +2329,6 @@ void SyncAmsInfoDialog::update_show_status() do_ams_mapping(obj_); } - - // reading done - if (wxGetApp().app_config) { - if (obj_->upgrade_force_upgrade) { - show_status(PrintDialogStatus::PrintStatusNeedForceUpgrading); - return; - } - - if (obj_->upgrade_consistency_request) { - show_status(PrintStatusNeedConsistencyUpgrading); - return; - } - } - if (is_blocking_printing(obj_)) { show_status(PrintDialogStatus::PrintStatusUnsupportedPrinter); return; @@ -2679,10 +2659,10 @@ void SyncAmsInfoDialog::reset_and_sync_ams_list() MaterialSyncItem *item = nullptr; if (use_double_extruder) { if (m_filaments_map[extruder] == 1) { - item = new MaterialSyncItem(m_filament_panel, colour_rgb, _L(display_materials[extruder])); // m_filament_left_panel//special + item = new MaterialSyncItem(m_filament_panel, colour_rgb, _L(display_materials[extruder]), m_filaments_id[extruder]); // m_filament_left_panel//special m_sizer_ams_mapping->Add(item, 0, wxALL, FromDIP(5)); // m_sizer_ams_mapping_left } else if (m_filaments_map[extruder] == 2) { - item = new MaterialSyncItem(m_filament_panel, colour_rgb, _L(display_materials[extruder])); // m_filament_right_panel + item = new MaterialSyncItem(m_filament_panel, colour_rgb, _L(display_materials[extruder]), m_filaments_id[extruder]); // m_filament_right_panel m_sizer_ams_mapping->Add(item, 0, wxALL, FromDIP(5)); // m_sizer_ams_mapping_right } else { @@ -2690,7 +2670,7 @@ void SyncAmsInfoDialog::reset_and_sync_ams_list() continue; } } else { - item = new MaterialSyncItem(m_filament_panel, colour_rgb, _L(display_materials[extruder])); + item = new MaterialSyncItem(m_filament_panel, colour_rgb, _L(display_materials[extruder]), m_filaments_id[extruder]); m_sizer_ams_mapping->Add(item, 0, wxALL, FromDIP(5)); } auto item_index_str = std::to_string(item_index); @@ -2894,17 +2874,17 @@ void SyncAmsInfoDialog::generate_override_fix_ams_list() MaterialSyncItem *item = nullptr; if (use_double_extruder) { if (m_filaments_map[extruder] == 1) { - item = new MaterialSyncItem(m_fix_filament_panel, colour_rgb, _L(display_materials[extruder])); // m_filament_left_panel//special + item = new MaterialSyncItem(m_fix_filament_panel, colour_rgb, _L(display_materials[extruder]), m_filaments_id[extruder]); // m_filament_left_panel//special m_fix_sizer_ams_mapping->Add(item, 0, wxALL, FromDIP(5)); // m_sizer_ams_mapping_left } else if (m_filaments_map[extruder] == 2) { - item = new MaterialSyncItem(m_fix_filament_panel, colour_rgb, _L(display_materials[extruder])); // m_filament_right_panel + item = new MaterialSyncItem(m_fix_filament_panel, colour_rgb, _L(display_materials[extruder]), m_filaments_id[extruder]); // m_filament_right_panel m_fix_sizer_ams_mapping->Add(item, 0, wxALL, FromDIP(5)); // m_sizer_ams_mapping_right } else { BOOST_LOG_TRIVIAL(error) << "check error:MaterialItem *item = nullptr"; continue; } } else { - item = new MaterialSyncItem(m_fix_filament_panel, colour_rgb, _L(display_materials[extruder])); + item = new MaterialSyncItem(m_fix_filament_panel, colour_rgb, _L(display_materials[extruder]), m_filaments_id[extruder]); m_fix_sizer_ams_mapping->Add(item, 0, wxALL, FromDIP(5)); } item->set_material_index_str(std::to_string(item_index)); diff --git a/src/slic3r/GUI/SyncBoxInfoDialog.cpp b/src/slic3r/GUI/SyncBoxInfoDialog.cpp index 7737363..e3860cb 100644 --- a/src/slic3r/GUI/SyncBoxInfoDialog.cpp +++ b/src/slic3r/GUI/SyncBoxInfoDialog.cpp @@ -1230,15 +1230,9 @@ bool SyncBoxInfoDialog::do_ams_mapping(MachineObject* obj_) } // single nozzle else { - if (obj_->is_support_amx_ext_mix_mapping()) { - map_opt = {false, true, false, true}; // four values: use_left_ams, use_right_ams, use_left_ext, use_right_ext - filament_result = DevMappingUtil::ams_filament_mapping(obj_, m_filaments, m_ams_mapping_result, map_opt, std::vector(), - wxGetApp().app_config->get_bool("ams_sync_match_full_use_color_dist") ? false : true); - // auto_supply_with_ext(obj_->vt_slot); - } else { - map_opt = {false, true, false, false}; - filament_result = DevMappingUtil::ams_filament_mapping(obj_, m_filaments, m_ams_mapping_result, map_opt); - } + map_opt = { false, true, false, true }; // four values: use_left_ams, use_right_ams, use_left_ext, use_right_ext + filament_result = DevMappingUtil::ams_filament_mapping(obj_, m_filaments, m_ams_mapping_result, map_opt, std::vector(), + wxGetApp().app_config->get_bool("ams_sync_match_full_use_color_dist") ? false : true); } if (filament_result == 0) { @@ -1920,7 +1914,7 @@ bool SyncBoxInfoDialog::is_same_nozzle_type(std::string &filament_type, NozzleTy while (iter != m_materialList.end()) { Material * item = iter->second; auto m = item->item; - auto filament_nozzle_hrc = preset_bundle->get_required_hrc_by_filament_type(m->m_material_name.ToStdString()); + auto filament_nozzle_hrc = preset_bundle->get_required_hrc_by_filament_id(m->m_filament_id); if (abs(filament_nozzle_hrc) > abs(printer_nozzle_hrc)) { filament_type = m->m_material_name.ToStdString(); @@ -2291,20 +2285,6 @@ void SyncBoxInfoDialog::update_show_status() do_ams_mapping(obj_); } - - // reading done - if (wxGetApp().app_config) { - if (obj_->upgrade_force_upgrade) { - show_status(PrintDialogStatus::PrintStatusNeedForceUpgrading); - return; - } - - if (obj_->upgrade_consistency_request) { - show_status(PrintStatusNeedConsistencyUpgrading); - return; - } - } - if (is_blocking_printing(obj_)) { show_status(PrintDialogStatus::PrintStatusUnsupportedPrinter); return; @@ -2632,10 +2612,10 @@ void SyncBoxInfoDialog::reset_and_sync_ams_list() MaterialSyncItem *item = nullptr; if (use_double_extruder) { if (m_filaments_map[extruder] == 1) { - item = new MaterialSyncItem(m_filament_panel, colour_rgb, _L(display_materials[extruder])); // m_filament_left_panel//special + item = new MaterialSyncItem(m_filament_panel, colour_rgb, _L(display_materials[extruder]), m_filaments_id[extruder]); // m_filament_left_panel//special m_sizer_ams_mapping->Add(item, 0, wxALL, FromDIP(5)); // m_sizer_ams_mapping_left } else if (m_filaments_map[extruder] == 2) { - item = new MaterialSyncItem(m_filament_panel, colour_rgb, _L(display_materials[extruder])); // m_filament_right_panel + item = new MaterialSyncItem(m_filament_panel, colour_rgb, _L(display_materials[extruder]), m_filaments_id[extruder]); // m_filament_right_panel m_sizer_ams_mapping->Add(item, 0, wxALL, FromDIP(5)); // m_sizer_ams_mapping_right } else { @@ -2643,7 +2623,7 @@ void SyncBoxInfoDialog::reset_and_sync_ams_list() continue; } } else { - item = new MaterialSyncItem(m_filament_panel, colour_rgb, _L(display_materials[extruder])); + item = new MaterialSyncItem(m_filament_panel, colour_rgb, _L(display_materials[extruder]), m_filaments_id[extruder]); m_sizer_ams_mapping->Add(item, 0, wxALL, FromDIP(5)); } auto item_index_str = std::to_string(item_index); @@ -2843,17 +2823,17 @@ void SyncBoxInfoDialog::generate_override_fix_ams_list() MaterialSyncItem *item = nullptr; if (use_double_extruder) { if (m_filaments_map[extruder] == 1) { - item = new MaterialSyncItem(m_fix_filament_panel, colour_rgb, _L(display_materials[extruder])); // m_filament_left_panel//special + item = new MaterialSyncItem(m_fix_filament_panel, colour_rgb, _L(display_materials[extruder]), m_filaments_id[extruder]); // m_filament_left_panel//special m_fix_sizer_ams_mapping->Add(item, 0, wxALL, FromDIP(5)); // m_sizer_ams_mapping_left } else if (m_filaments_map[extruder] == 2) { - item = new MaterialSyncItem(m_fix_filament_panel, colour_rgb, _L(display_materials[extruder])); // m_filament_right_panel - m_fix_sizer_ams_mapping->Add(item, 0, wxALL, FromDIP(5)); // m_sizer_ams_mapping_right + item = new MaterialSyncItem(m_fix_filament_panel, colour_rgb, _L(display_materials[extruder]), m_filaments_id[extruder]); // m_filament_right_panel + m_fix_sizer_ams_mapping->Add(item, 0, wxALL, FromDIP(5)); // m_sizer_ams_mapping_right } else { BOOST_LOG_TRIVIAL(error) << "check error:MaterialItem *item = nullptr"; continue; } } else { - item = new MaterialSyncItem(m_fix_filament_panel, colour_rgb, _L(display_materials[extruder])); + item = new MaterialSyncItem(m_fix_filament_panel, colour_rgb, _L(display_materials[extruder]), m_filaments_id[extruder]); m_fix_sizer_ams_mapping->Add(item, 0, wxALL, FromDIP(5)); } item->set_material_index_str(std::to_string(item_index)); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 274963e..e92cfa8 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -43,6 +44,7 @@ #include "MarkdownTip.hpp" #include "Search.hpp" #include "BedShapeDialog.hpp" +#include "Widgets/MultiNozzleSync.hpp" #include "DeviceCore/DevManager.h" @@ -501,35 +503,91 @@ void Tab::create_preset_tab() m_main_sizer->Add(m_tabctrl, 0, wxEXPAND | wxALL, 0 ); if (dynamic_cast(this) || dynamic_cast(this)) { - m_extruder_switch = new SwitchButton(panel); - m_extruder_switch->SetMaxSize({em_unit(this) * 24, -1}); - m_extruder_switch->SetLabels(_L("Left"), _L("Right")); - m_extruder_switch->Bind(wxEVT_TOGGLEBUTTON, [this] (auto & evt) { + m_extruder_switch = new MultiSwitchButton(panel); + m_extruder_switch->SetMaxSize({em_unit(this) * 40, -1}); + m_extruder_switch->Bind(wxCUSTOMEVT_MULTISWITCH_SELECTION, [this](auto &evt) { evt.Skip(); - switch_excluder(evt.GetInt()); + int selection = evt.GetInt(); + + int extruder_id; + NozzleVolumeType nozzle_type; + parse_extruder_selection(selection, extruder_id, nozzle_type); + int extruder_count = m_preset_bundle->get_printer_extruder_count(); + m_actual_nozzle_volumes.resize(extruder_count, NozzleVolumeType::nvtStandard); + if (extruder_id >= 0 && extruder_id < m_preset_bundle->get_printer_extruder_count()) + m_actual_nozzle_volumes[extruder_id] = nozzle_type; + + switch_excluder(extruder_id); }); - m_extruder_sync = new ScalableButton(panel, wxID_ANY, "extruder_sync"); + m_extruder_sync_box = new wxPanel(panel, wxID_ANY); + m_extruder_sync_box->SetBackgroundColour(panel->GetBackgroundColour()); + m_extruder_sync_box->SetToolTip(_L("Synchronization of different extruder drives or nozzle volume types is not supported.")); + m_extruder_sync = new ScalableButton(m_extruder_sync_box, wxID_ANY, "extruder_sync"); m_extruder_sync->SetToolTip(_L("Synchronize the modification of parameters to the corresponding parameters of another extruder.")); m_extruder_sync->Bind(wxEVT_BUTTON, [this](auto &evt) { evt.Skip(); sync_excluder(); }); + + auto sync_box_sizer = new wxBoxSizer(wxHORIZONTAL); + sync_box_sizer->Add(m_extruder_sync, 1, wxEXPAND); + m_extruder_sync_box->SetSizer(sync_box_sizer); + m_variant_sizer = new wxBoxSizer(wxHORIZONTAL); auto right_sizer = new wxBoxSizer(wxHORIZONTAL); + m_variant_sizer->AddStretchSpacer(1); m_variant_sizer->Add(m_extruder_switch, 0, wxALIGN_CENTER, 0); m_variant_sizer->Add(right_sizer, 1, wxALIGN_CENTER); right_sizer->AddStretchSpacer(1); - right_sizer->Add(m_extruder_sync, 0, wxALIGN_CENTER | wxRIGHT, m_em_unit); + right_sizer->Add(m_extruder_sync_box, 0, wxALIGN_CENTER | wxRIGHT, m_em_unit); + m_main_sizer->Add(m_variant_sizer, 0, wxEXPAND | wxTOP, m_em_unit); } else if (dynamic_cast(this)) { - m_variant_combo = new ComboBox(panel, wxID_ANY, "", wxDefaultPosition, {20 * m_em_unit, -1}, 0, {}, wxCB_READONLY); - m_variant_combo->Bind(wxEVT_COMBOBOX, [this](auto &evt) { + m_variant_combo = new MultiSwitchButton(panel); + m_variant_combo->Bind(wxCUSTOMEVT_MULTISWITCH_SELECTION, [this](auto &evt) { evt.Skip(); switch_excluder(evt.GetInt()); }); - m_variant_sizer = new wxBoxSizer(wxHORIZONTAL); - m_variant_sizer->Add(m_variant_combo, 0, wxLEFT, m_em_unit); + + wxBoxSizer *wiki_sizer = new wxBoxSizer(wxHORIZONTAL); + auto wiki_icon = new ScalableBitmap(panel, "wiki", 16); + auto wiki_icon_hover = new ScalableBitmap(panel, "wiki_hover", 16); + m_wiki_bmp = new wxStaticBitmap(panel, wxID_ANY, wiki_icon->bmp()); + wiki_sizer->Add(m_wiki_bmp, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT | wxBOTTOM, 2); + m_wiki_label = new wxStaticText(panel, wxID_ANY, _L("Wiki")); + m_wiki_label->SetForegroundColour(wxColour("#6B6B6B")); + m_wiki_label->SetFont(Label::Body_13); + m_wiki_label->SetToolTip(_L("Click to learn more")); + m_wiki_label->Hide(); + m_wiki_bmp->Hide(); + wiki_sizer->Add(m_wiki_label, 0, wxALIGN_CENTER_VERTICAL); + auto set_hover = [this, wiki_icon, wiki_icon_hover](bool hover) { + wxColour color = hover ? wxColour("#4479fb") : wxColour("#6B6B6B"); + m_wiki_bmp->SetBitmap(hover ? wiki_icon_hover->bmp() : wiki_icon->bmp()); + m_wiki_label->SetForegroundColour(color); + m_wiki_label->SetFont(hover ? Label::Body_13.Underlined() : Label::Body_13); + m_wiki_label->SetCursor(hover ? wxCURSOR_HAND : wxCURSOR_ARROW); + }; + auto open_wiki = [](wxMouseEvent&) { + wxLaunchDefaultBrowser("https://wiki.qidi3d.com/"); + }; + m_wiki_label->Bind(wxEVT_LEFT_DOWN, open_wiki); + m_wiki_bmp->Bind(wxEVT_ENTER_WINDOW, [set_hover](wxMouseEvent &) { set_hover(true); }); + m_wiki_label->Bind(wxEVT_ENTER_WINDOW, [set_hover](wxMouseEvent &) { set_hover(true); }); + m_wiki_bmp->Bind(wxEVT_LEAVE_WINDOW, [set_hover](wxMouseEvent &) { set_hover(false); }); + m_wiki_label->Bind(wxEVT_LEAVE_WINDOW, [set_hover](wxMouseEvent &) { set_hover(false); }); + + wxBoxSizer *combo_sizer = new wxBoxSizer(wxHORIZONTAL); + combo_sizer->Add(m_variant_combo, 1, wxEXPAND); + wxBoxSizer *top_sizer = new wxBoxSizer(wxHORIZONTAL); + top_sizer->Add(combo_sizer, 1, wxEXPAND | wxLEFT, m_em_unit); + top_sizer->AddStretchSpacer(1); + top_sizer->Add(wiki_sizer, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, m_em_unit); + m_nozzle_status_sizer = new wxBoxSizer(wxHORIZONTAL); + m_variant_sizer = new wxBoxSizer(wxVERTICAL); + m_variant_sizer->Add(top_sizer, 0, wxLEFT, m_em_unit); + m_variant_sizer->Add(m_nozzle_status_sizer, 0, wxEXPAND | wxLEFT | wxTOP, m_em_unit * 0.8); m_main_sizer->Add(m_variant_sizer, 0, wxEXPAND | wxTOP, m_em_unit); } @@ -577,6 +635,77 @@ void Tab::create_preset_tab() m_completed = true; } +void Tab::parse_extruder_selection(int selection, int &extruder_id, NozzleVolumeType &nozzle_type) +{ + auto nozzle_volumes = m_preset_bundle->project_config.option("nozzle_volume_type"); + int extruder_nums = m_preset_bundle->get_printer_extruder_count(); + + int current_index = 0; + + for (int i = 0; i < extruder_nums; ++i) { + NozzleVolumeType volume_type = NozzleVolumeType(nozzle_volumes->values[i]); + + if (volume_type == NozzleVolumeType::nvtHybrid) { + if (selection == current_index) { + extruder_id = i; + nozzle_type = NozzleVolumeType::nvtStandard; + return; + } else if (selection == current_index + 1) { + extruder_id = i; + nozzle_type = NozzleVolumeType::nvtHighFlow; + return; + } + current_index += 2; + } else { + if (selection == current_index) { + extruder_id = i; + nozzle_type = volume_type; + return; + } + current_index += 1; + } + } + + extruder_id = 0; + nozzle_type = NozzleVolumeType::nvtStandard; +} + +int Tab::calculate_selection_index_for_extruder(int extruder_id, NozzleVolumeType nozzle_type) +{ + auto nozzle_volumes = m_preset_bundle->project_config.option("nozzle_volume_type"); + int extruder_nums = m_preset_bundle->get_printer_extruder_count(); + + int index = 0; + + for (int i = 0; i < extruder_nums; ++i) { + if (i == extruder_id) { + NozzleVolumeType volume_type = NozzleVolumeType(nozzle_volumes->values[i]); + if (volume_type == NozzleVolumeType::nvtHybrid) { + return nozzle_type == NozzleVolumeType::nvtHighFlow ? index + 1 : index; + } else { + return index; + } + } + + NozzleVolumeType volume_type = NozzleVolumeType(nozzle_volumes->values[i]); + index += (volume_type == NozzleVolumeType::nvtHybrid) ? 2 : 1; + } + + return 0; +} + +int Tab::get_current_active_extruder() +{ + if (m_extruder_switch && m_extruder_switch->IsThisEnabled()) { + int selection = m_extruder_switch->GetSelection(); + int extruder_id; + NozzleVolumeType nozzle_type; + parse_extruder_selection(selection, extruder_id, nozzle_type); + return extruder_id; + } + return 0; +} + void Tab::add_scaled_button(wxWindow* parent, ScalableButton** btn, const std::string& icon_name, @@ -888,6 +1017,7 @@ void Tab::update_changed_ui() } update_custom_dirty(dirty_options, nonsys_options); + update_all_extruder_options_status(); filter_diff_option(dirty_options); filter_diff_option(nonsys_options); @@ -907,6 +1037,7 @@ void Tab::update_changed_ui() } decorate(); + update_extruder_switch_colors(); wxTheApp->CallAfter([this]() { if (parent()) //To avoid a crash, parent should be exist for a moment of a tree updating @@ -922,6 +1053,170 @@ void add_correct_opts_to_options_list(const std::string &opt_key, std::mapget_printer_extruder_count(); + auto extruders = m_preset_bundle->printers.get_edited_preset().config.option("extruder_type"); + auto nozzle_volumes = m_preset_bundle->project_config.option("nozzle_volume_type"); + + std::set all_config_indices; + for (int extruder_id = 0; extruder_id < extruder_count; ++extruder_id) { + for (auto nozzle_type : {NozzleVolumeType::nvtStandard, NozzleVolumeType::nvtHighFlow}) { + auto variant_keys = extruder_variant_keys[m_type >= Preset::TYPE_COUNT ? Preset::TYPE_PRINT : m_type]; + int config_index = m_config->get_index_for_extruder( + extruder_id + 1, + variant_keys.first, + ExtruderType(extruders->values[extruder_id]), + nozzle_type, + variant_keys.second + ); + if (config_index >= 0) { + all_config_indices.insert(config_index); + } + } + } + + auto dirty_options = m_presets->current_dirty_options(true); + auto nonsys_options = m_presets->current_different_from_parent_options(true); + auto filter_extruder_options = [](const std::vector& options) { + std::vector filtered_options; + for (const auto& opt : options) { + if (opt.find('#') != std::string::npos) { + filtered_options.push_back(opt); + } + } + return filtered_options; + }; + + auto filtered_dirty_options = filter_extruder_options(dirty_options); + auto filtered_nonsys_options = filter_extruder_options(nonsys_options); + + for (int config_index : all_config_indices) { + int status_value = m_opt_status_value; + for (const auto &opt_key : filtered_dirty_options) { + m_all_extruder_options_status[opt_key] = status_value & ~osInitValue; + } + for (const auto &opt_key : filtered_nonsys_options) { + auto iter = m_all_extruder_options_status.find(opt_key); + if (iter != m_all_extruder_options_status.end()) { + iter->second &= ~osSystemValue; + } else { + m_all_extruder_options_status[opt_key] = status_value & ~osSystemValue; + } + } + } +} + +void Tab::update_extruder_switch_colors() +{ + if (!m_extruder_switch && !m_variant_combo) { + return; + } + + auto options = generate_extruder_options(); + auto extruders = m_preset_bundle->printers.get_edited_preset().config.option("extruder_type"); + + for (size_t switch_index = 0; switch_index < options.size(); ++switch_index) { + int selection = m_extruder_switch ? m_extruder_switch->GetSelection() : (m_variant_combo ? m_variant_combo->GetSelection() : 0); + if (switch_index == selection) continue; + + bool sys_extruder = true; + bool modified_extruder = false; + std::vector pages_to_check; + + if (m_active_page) { + if (m_active_page->title() == "Speed" || m_active_page->title() == "Motion ability" || m_active_page->title() == "Filament" || + m_active_page->title() == "Setting Overrides" || m_active_page->title() == "Multi Filament") { + for (auto page_ptr : m_pages) { + if (page_ptr.get() == m_active_page) { + pages_to_check.push_back(page_ptr); + break; + } + } + } + } + if (pages_to_check.empty()) { + continue; + } + check_extruder_options_status(switch_index, sys_extruder, modified_extruder, pages_to_check); + + StateColor default_color(std::make_pair(0x6B6B6B, (int) StateColor::NotChecked), std::make_pair(0xFFFFFE, (int) StateColor::Normal)); + StateColor color = (modified_extruder || m_type >= Preset::TYPE_COUNT) ? StateColor(m_modified_label_clr) : default_color; + + if (m_extruder_switch) + m_extruder_switch->SetButtonTextColor(switch_index, color); + if (m_variant_combo) { + StateColor default_color_grayed(std::make_pair(0x999999, (int) StateColor::NotChecked), std::make_pair(0x99DFB2, (int) StateColor::Normal)); + Button *btn = m_variant_combo->GetButton(switch_index); + if (btn) { + m_variant_combo->SetButtonTextColor(switch_index, btn->IsGrayed() ? default_color_grayed : color); + } + } + } +} + +void Tab::check_extruder_options_status(int index, bool &sys_extruder, bool &modified_extruder, const std::vector& pages_to_check) +{ + int config_index = index; + if (m_type == Preset::TYPE_PRINT || m_type == Preset::TYPE_PRINTER) { + int extruder_id; + NozzleVolumeType nozzle_type; + parse_extruder_selection(index, extruder_id, nozzle_type); + + auto extruders = m_preset_bundle->printers.get_edited_preset().config.option("extruder_type"); + auto variant_keys = extruder_variant_keys[m_type >= Preset::TYPE_COUNT ? Preset::TYPE_PRINT : m_type]; + config_index = m_config->get_index_for_extruder( + extruder_id + 1, + variant_keys.first, + ExtruderType(extruders->values[extruder_id]), + nozzle_type, + variant_keys.second + ); + } + + for (auto page : pages_to_check) { + /*if (page->title() != "Speed" && page->title() != "Motion ability" && page->title() != "Filament" && page->title() != "Setting Overrides" && page->title() != "Multi Filament") { + continue; + }*/ + for (auto group : page->m_optgroups) { + for (const auto &kvp : group->opt_map()) { + std::string base_opt_key = kvp.second.first; + // For filament tab, common options will not change color when edited + if (m_type == Preset::TYPE_FILAMENT && kvp.second.second == -1) { + continue; + } + std::string target_opt_key = base_opt_key + "#" + std::to_string(config_index); + + auto status_iter = m_all_extruder_options_status.find(target_opt_key); + if (status_iter != m_all_extruder_options_status.end()) { + bool found_modified_for_this_config = false; + const bool deep_compare = (m_type == Preset::TYPE_PRINTER || m_type == Preset::TYPE_PRINT || m_type == Preset::TYPE_FILAMENT || m_type == Preset::TYPE_SLA_MATERIAL || + m_type == Preset::TYPE_MODEL); + auto original_dirty_options = m_presets->current_dirty_options(deep_compare); + for (const std::string &orig_opt : original_dirty_options) { + if (orig_opt == target_opt_key) { + found_modified_for_this_config = true; + break; + } + } + + if (found_modified_for_this_config) { + sys_extruder = (status_iter->second & osSystemValue) != 0; + modified_extruder |= (status_iter->second & osInitValue) == 0; + + if (!sys_extruder && modified_extruder) { return; } + } + } + } + } + } +} + void Tab::init_options_list() { if (!m_options_list.empty()) @@ -1031,6 +1326,13 @@ void Tab::update_changed_tree_ui() get_sys_and_mod_flags("compatible_printers", sys_page, modified_page); } } + if (page->title() == "Speed" || page->title() == "Motion ability" || page->title() == "Filament" || page->title() == "Setting Overrides" || page->title() == "Multi Filament") { + auto options = generate_extruder_options(); + for (size_t switch_index = 0; switch_index < options.size(); ++switch_index) { + std::vector pages_to_check = { page }; + check_extruder_options_status(switch_index, sys_page, modified_page, pages_to_check); + } + } for (auto group : page->m_optgroups) { if (!sys_page && modified_page) @@ -1479,6 +1781,24 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) auto timelapse_type = m_config->option>("timelapse_type"); bool timelapse_enabled = timelapse_type->value == TimelapseType::tlSmooth; if (!boost::any_cast(value)) { + + {// Add prime tower disabling confirm dialog for multi nozzle + const auto &extruder_max_nozzle_count_temp = + GUI::wxGetApp().preset_bundle->printers.get_edited_preset().config.option("extruder_max_nozzle_count")->values; + int max_total_nozzle = std::accumulate(extruder_max_nozzle_count_temp.begin(), extruder_max_nozzle_count_temp.end(), 0); + if (max_total_nozzle>=2 ) { + MessageDialog dlg( + wxGetApp().plater(), + _L("Prime tower is required for nozzle changing. There may be flaws on the model without prime tower. Are you sure you want to disable prime tower?"), + _L("Warning"), wxICON_WARNING | wxYES | wxNO); + if (dlg.ShowModal() == wxID_NO) { + DynamicPrintConfig new_conf = *m_config; + new_conf.set_key_value("enable_prime_tower", new ConfigOptionBool(true)); + m_config_manipulation.apply(m_config, &new_conf); + } + } + } + bool set_enable_prime_tower = false; if (timelapse_enabled) { MessageDialog @@ -1619,19 +1939,18 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) } if ((is_soluble_filament(filament_id) || support_TPU) && !(m_config->opt_float("support_top_z_distance") == 0 && m_config->opt_float("support_interface_spacing") == 0 && - m_config->opt_float("support_object_xy_distance") == 0 && m_config->opt_bool("top_z_overrides_xy_distance") && + m_config->opt_float("support_object_xy_distance") == 0 /*&& m_config->opt_bool("top_z_overrides_xy_distance")*/ && m_config->opt_enum("support_interface_pattern") == SupportMaterialInterfacePattern::smipRectilinearInterlaced && filament_id == interface_filament_id)) { wxString msg_text; if (support_TPU) msg_text = _L("When using PLA to support TPU, We recommend the following settings:\n" - "0 top z distance, 0 interface spacing, 0 support/object xy distance, interlaced rectilinear pattern, enable Z \n" - "overrides XY, disable independent support layer height and use PLA for both support interface and support base"); + "0 top z distance, 0 interface spacing, 0 support/object xy distance, interlaced rectilinear pattern, disable \n" + "independent support layer height and use PLA for both support interface and support base"); else msg_text = _L("When using soluble material for the support, We recommend the following settings:\n" - "0 top z distance, 0 interface spacing, 0 support/object xy distance, interlaced rectilinear pattern, enable Z \n" - "overrides XY, disable independent support layer height and use soluble materials for both support interface \n" - "and support base"); + "0 top z distance, 0 interface spacing, 0 support/object xy distance, interlaced rectilinear pattern, disable \n" + "independent support layer height and use soluble materials for both support interface and support base"); msg_text += "\n\n" + _L("Change these settings automatically? \n" "Yes - Change these settings automatically\n" "No - Do not change these settings for me"); @@ -1643,7 +1962,7 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) new_conf.set_key_value("support_object_xy_distance", new ConfigOptionFloat(0)); new_conf.set_key_value("support_interface_pattern", new ConfigOptionEnum(SupportMaterialInterfacePattern::smipRectilinearInterlaced)); - new_conf.set_key_value("top_z_overrides_xy_distance", new ConfigOptionBool(true)); + //new_conf.set_key_value("top_z_overrides_xy_distance", new ConfigOptionBool(true)); new_conf.set_key_value("independent_support_layer_height", new ConfigOptionBool(false)); new_conf.set_key_value("support_interface_filament", new ConfigOptionInt(filament_id + 1)); m_config_manipulation.apply(m_config, &new_conf); @@ -1668,42 +1987,40 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) } if ((is_support_filament(interface_filament_id, false) && - !(m_config->opt_float("support_top_z_distance") == 0 && m_config->opt_float("support_interface_spacing") == 0 && m_config->opt_bool("top_z_overrides_xy_distance") && + !(m_config->opt_float("support_top_z_distance") == 0 && m_config->opt_float("support_interface_spacing") == 0 /*&& m_config->opt_bool("top_z_overrides_xy_distance")*/ && m_config->opt_enum("support_interface_pattern") == SupportMaterialInterfacePattern::smipRectilinearInterlaced && (support_TPU ? m_config->opt_float("support_object_xy_distance") == 0 : -1))) || (is_soluble_filament(interface_filament_id) && !is_soluble_filament(filament_id))) { wxString msg_text; if (support_TPU) { msg_text = _L("When using PLA to support TPU, We recommend the following settings:\n" - "0 top z distance, 0 interface spacing, 0 support/object xy distance, interlaced rectilinear pattern, enable Z \n" - "overrides XY, disable independent support layer height and use PLA for both support interface and support base"); + "0 top z distance, 0 interface spacing, 0 support/object xy distance, interlaced rectilinear pattern, disable \n" + "independent support layer height and use PLA for both support interface and support base"); } else if (!is_soluble_filament(interface_filament_id)) { msg_text = _L("When using support material for the support interface, We recommend the following settings:\n" - "0 top z distance, 0 interface spacing, interlaced rectilinear pattern, enable Z overrides XY and disable \n" - "independent support layer height"); + "0 top z distance, 0 interface spacing, interlaced rectilinear pattern and disable independent support layer height"); } else { msg_text = _L("When using soluble material for the support interface, We recommend the following settings:\n" - "0 top z distance, 0 interface spacing, 0 support/object xy distance, interlaced rectilinear pattern, enable Z \n" - "overrides XY, disable independent support layer height and use soluble materials for both support interface \n" - "and support base"); + "0 top z distance, 0 interface spacing, 0 support/object xy distance, interlaced rectilinear pattern, disable \n" + "independent support layer height and use soluble materials for both support interface and support base"); } msg_text += "\n\n" + _L("Change these settings automatically? \n" - "Yes - Change these settings automatically\n" - "No - Do not change these settings for me"); + "Yes - Change these settings automatically\n" + "No - Do not change these settings for me"); MessageDialog dialog(wxGetApp().plater(), msg_text, "Suggestion", wxICON_WARNING | wxYES | wxNO); DynamicPrintConfig new_conf = *m_config; if (dialog.ShowModal() == wxID_YES) { new_conf.set_key_value("support_top_z_distance", new ConfigOptionFloat(0)); new_conf.set_key_value("support_interface_spacing", new ConfigOptionFloat(0)); - new_conf.set_key_value("support_interface_pattern", + new_conf.set_key_value("support_interface_pattern", new ConfigOptionEnum(SupportMaterialInterfacePattern::smipRectilinearInterlaced)); - new_conf.set_key_value("top_z_overrides_xy_distance", new ConfigOptionBool(true)); + //new_conf.set_key_value("top_z_overrides_xy_distance", new ConfigOptionBool(true)); new_conf.set_key_value("independent_support_layer_height", new ConfigOptionBool(false)); - m_config_manipulation.apply(m_config, &new_conf); if (support_TPU || (is_soluble_filament(interface_filament_id) && !is_soluble_filament(filament_id))) { new_conf.set_key_value("support_object_xy_distance", new ConfigOptionFloat(0)); new_conf.set_key_value("support_filament", new ConfigOptionInt(interface_filament_id + 1)); } + m_config_manipulation.apply(m_config, &new_conf); } wxGetApp().plater()->update(); } @@ -1715,8 +2032,8 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) auto layer_height_floor = *std::min_element(min_layer_height_from_nozzle.begin(), min_layer_height_from_nozzle.end()); auto layer_height_ceil = *std::max_element(max_layer_height_from_nozzle.begin(), max_layer_height_from_nozzle.end()); float layer_height = m_config->opt_float("layer_height"); - bool exceed_minimum_flag = layer_height < layer_height_floor; - bool exceed_maximum_flag = layer_height > layer_height_ceil; + bool exceed_minimum_flag = (layer_height_floor - layer_height) > EPSILON; + bool exceed_maximum_flag = (layer_height - layer_height_ceil) > EPSILON; if (exceed_maximum_flag || exceed_minimum_flag) { if(layer_height < EPSILON){ @@ -2296,6 +2613,8 @@ void TabPrint::build() optgroup = page->new_optgroup(L("Walls"), L"param_wall"); //y32 optgroup->append_single_option_line("wall_loops","print-settings/wall-generator"); + optgroup->append_single_option_line("embedding_wall_into_infill"); + optgroup->append_single_option_line("detect_thin_wall","print-settings/wall-generator"); optgroup = page->new_optgroup(L("Top/bottom shells"), L"param_shell"); @@ -2310,10 +2629,12 @@ void TabPrint::build() optgroup->append_single_option_line("bottom_shell_layers"); optgroup->append_single_option_line("bottom_shell_thickness"); optgroup->append_single_option_line("bottom_color_penetration_layers"); + optgroup->append_single_option_line("infill_instead_top_bottom_surfaces"); optgroup->append_single_option_line("internal_solid_infill_pattern"); optgroup = page->new_optgroup(L("Sparse infill"), L"param_infill"); optgroup->append_single_option_line("sparse_infill_density"); + optgroup->append_single_option_line("fill_multiline"); //y32 optgroup->append_single_option_line("sparse_infill_pattern", "print-settings/fill-patterns"); optgroup->append_single_option_line("locked_skin_infill_pattern", "fill-patterns#infill types and their properties of sparse", -1, true); @@ -2532,6 +2853,9 @@ void TabPrint::build() optgroup->append_single_option_line("interlocking_beam_layer_count"); optgroup->append_single_option_line("interlocking_depth"); optgroup->append_single_option_line("interlocking_boundary_avoidance"); + optgroup->append_single_option_line("sparse_infill_filament"); + optgroup->append_single_option_line("solid_infill_filament"); + optgroup->append_single_option_line("wall_filament"); optgroup = page->new_optgroup(L("G-code output"), L"param_gcode"); optgroup->append_single_option_line("reduce_infill_retraction"); @@ -3163,7 +3487,7 @@ void TabPrintPlate::on_value_change(const std::string& opt_key, const boost::any for (int i = 0; i < wxGetApp().filaments_cnt(); i++) { initial_sequence.push_back(i + 1); } - std::vector initial_layer_sequence{ std::make_pair(std::make_pair(2, INT_MAX), initial_sequence) }; + std::vector initial_layer_sequence{ std::make_pair(std::make_pair(2, INT_MAX-1), initial_sequence) }; plate->set_other_layers_print_sequence(initial_layer_sequence); } wxCommandEvent evt(EVT_OPEN_PLATESETTINGSDIALOG); @@ -3365,6 +3689,7 @@ void TabFilament::add_filament_overrides_page() "filament_z_hop_types", "filament_retraction_speed", "filament_deretraction_speed", + "filament_retract_length_nc", "filament_retract_restart_extra", "filament_retraction_minimum_travel", "filament_retract_when_changing_layer", @@ -3399,6 +3724,7 @@ void TabFilament::update_filament_overrides_page() "filament_z_hop_types", "filament_retraction_speed", "filament_deretraction_speed", + "filament_retract_length_nc", "filament_retract_restart_extra", "filament_retraction_minimum_travel", "filament_retract_when_changing_layer", @@ -3463,9 +3789,8 @@ void TabFilament::build() // QDS optgroup->append_single_option_line("filament_is_support"); optgroup->append_single_option_line("impact_strength_z"); - optgroup->append_single_option_line("filament_change_length"); - optgroup->append_single_option_line("filament_prime_volume"); + //optgroup->append_single_option_line("filament_prime_volume"); //optgroup->append_single_option_line("filament_colour"); optgroup->append_single_option_line("required_nozzle_HRC"); optgroup->append_single_option_line("default_filament_colour"); @@ -3478,12 +3803,32 @@ void TabFilament::build() optgroup->append_single_option_line("filament_shrink"); optgroup->append_single_option_line("filament_velocity_adaptation_factor"); optgroup->append_single_option_line("filament_cost"); - - //QDS optgroup->append_single_option_line("temperature_vitrification"); - optgroup->append_single_option_line("filament_ramming_travel_time", "",0); - optgroup->append_single_option_line("filament_pre_cooling_temperature", "", 0); - Line line = { L("Recommended nozzle temperature"), L("Recommended nozzle temperature range of this filament. 0 means no set") }; + + optgroup->append_single_option_line("filament_cooling_before_tower"); + //QDS + Line line = {L("Filament prime volume"), L("The volume of material to prime extruder on tower.")}; + line.append_option(optgroup->get_option("filament_prime_volume")); + line.append_option(optgroup->get_option("filament_prime_volume_nc")); + optgroup->append_line(line); + + + line = {L("Filament ramming length"), L("Filament length used in ramming.")}; + line.append_option(optgroup->get_option("filament_change_length")); + line.append_option(optgroup->get_option("filament_change_length_nc")); + optgroup->append_line(line); + + line = {L("Travel time after ramming"), L("A reverse travel movement after ramming.")}; + line.append_option(optgroup->get_option("filament_ramming_travel_time",0)); + line.append_option(optgroup->get_option("filament_ramming_travel_time_nc",0)); + optgroup->append_line(line); + + line = { L("Precooling target temperature"), L("Precooling target temperature during ramming.") }; + line.append_option(optgroup->get_option("filament_pre_cooling_temperature",0)); + line.append_option(optgroup->get_option("filament_pre_cooling_temperature_nc",0)); + optgroup->append_line(line); + + line = { L("Recommended nozzle temperature"), L("Recommended nozzle temperature range of this filament. 0 means no set") }; line.append_option(optgroup->get_option("nozzle_temperature_range_low")); line.append_option(optgroup->get_option("nozzle_temperature_range_high")); optgroup->append_line(line); @@ -3579,7 +3924,10 @@ void TabFilament::build() optgroup = page->new_optgroup(L("Volumetric speed limitation"), L"param_volumetric_speed"); optgroup->append_single_option_line("filament_adaptive_volumetric_speed", "", 0); optgroup->append_single_option_line("filament_max_volumetric_speed", "", 0); - optgroup->append_single_option_line("filament_ramming_volumetric_speed", "",0); + line = {L("Ramming volumetric speed"), L("The maximum volumetric speed for ramming, where -1 means using the maximum volumetric speed.")}; + line.append_option(optgroup->get_option("filament_ramming_volumetric_speed", 0)); + line.append_option(optgroup->get_option("filament_ramming_volumetric_speed_nc", 0)); + optgroup->append_line(line); // QDS optgroup = page->new_optgroup(L("Filament scarf seam settings"), L"param_volumetric_speed"); @@ -3843,6 +4191,9 @@ void TabFilament::toggle_options() std::string volumetric_speed_cos = m_config->opt_string("volumetric_speed_coefficients", (unsigned int)(m_variant_combo->GetSelection())); bool enable_fit = volumetric_speed_cos != "0 0 0 0 0 0"; toggle_option("filament_adaptive_volumetric_speed", enable_fit, 256 + (unsigned int) (m_variant_combo->GetSelection())); + auto prime_volume = this->m_preset_bundle->project_config.option>("prime_volume_mode")->value; + toggle_option("filament_prime_volume", prime_volume == PrimeVolumeMode::pvmDefault, 256 + (unsigned int) (m_variant_combo->GetSelection())); + toggle_option("filament_prime_volume_nc", prime_volume == PrimeVolumeMode::pvmDefault, 256 + (unsigned int) (m_variant_combo->GetSelection())); } if (m_active_page->title() == "Multi Filament") { @@ -4056,6 +4407,7 @@ void TabPrinter::build_fff() optgroup = page->new_optgroup(L("Advanced"), L"param_advanced"); optgroup->append_single_option_line("printer_structure"); optgroup->append_single_option_line("gcode_flavor"); + optgroup->append_single_option_line("apply_top_surface_compensation"); //w12 @@ -4109,6 +4461,7 @@ void TabPrinter::build_fff() optgroup->append_single_option_line("machine_load_filament_time"); optgroup->append_single_option_line("machine_unload_filament_time"); optgroup->append_single_option_line("machine_switch_extruder_time"); + optgroup->append_single_option_line("machine_hotend_change_time"); optgroup = page->new_optgroup(L("Extruder Clearance")); optgroup->append_single_option_line("extruder_clearance_max_radius"); @@ -4125,6 +4478,8 @@ void TabPrinter::build_fff() optgroup->append_single_option_line("support_box_temp_control"); optgroup->append_single_option_line("support_air_filtration"); + optgroup->append_single_option_line("cooling_filter_enabled"); + optgroup->append_single_option_line("auto_disable_filter_on_overheat"); const int gcode_field_height = 15; // 150 const int notes_field_height = 25; // 250 @@ -4654,6 +5009,7 @@ void TabPrinter::on_preset_loaded() if (!base_printer) base_printer = ¤t_printer; std::string base_name = base_printer->name; + std::string base_model = base_printer->config.option("printer_model")->value; // update the extruders count field auto *nozzle_diameter = dynamic_cast(m_config->option("nozzle_diameter")); size_t extruders_count = nozzle_diameter->values.size(); @@ -4670,12 +5026,44 @@ void TabPrinter::on_preset_loaded() if (!prev_nozzle_volume_type.empty()) { ConfigOptionEnumsGeneric* nozzle_volume_type_option = m_preset_bundle->project_config.option("nozzle_volume_type"); if (nozzle_volume_type_option->deserialize(prev_nozzle_volume_type)) { + for (size_t idx = 0; idx < nozzle_volume_type_option->size(); ++idx) { + NozzleVolumeType volume_type=NozzleVolumeType(nozzle_volume_type_option->values[idx]); + m_preset_bundle->extruder_nozzle_stat.on_volume_type_switch(idx, volume_type); + if (wxGetApp().plater()) { + wxGetApp().plater()->update_filament_volume_map(idx, volume_type); + } + updateNozzleCountDisplay(m_preset_bundle, idx, volume_type); + }; use_default_nozzle_volume_type = false; } } if (use_default_nozzle_volume_type) { - m_preset_bundle->project_config.option("nozzle_volume_type")->values = current_printer.config.option("default_nozzle_volume_type")->values; + auto default_nozzle_volume_type = current_printer.config.option("default_nozzle_volume_type")->values; + for (size_t eid = 0; eid < default_nozzle_volume_type.size(); ++eid) + set_extruder_volume_type(eid, (NozzleVolumeType)(default_nozzle_volume_type[eid])); } + + // only reset nozzle count when printer model is changed + if (base_model != m_base_preset_model) { + auto extruder_max_nozzle_count = current_printer.config.option("extruder_max_nozzle_count"); + auto nozzle_volume_type = m_preset_bundle->project_config.option("nozzle_volume_type"); + bool has_multiple_nozzle = std::any_of(extruder_max_nozzle_count->values.begin(), extruder_max_nozzle_count->values.end(), [](int i) { return i > 1; }); + if (extruder_max_nozzle_count && nozzle_volume_type) { + wxGetApp().plater()->sidebar().enable_nozzle_count_edit(has_multiple_nozzle); + m_preset_bundle->extruder_nozzle_stat.on_printer_model_change(m_preset_bundle); + for (size_t idx = 0; idx < extruders_count; ++idx) { + updateNozzleCountDisplay(m_preset_bundle, idx, NozzleVolumeType(nozzle_volume_type->values[idx])); + } + } + m_preset_bundle->extruder_nozzle_stat.set_nozzle_data_flag(ExtruderNozzleStat::ndfNone); + + // only trigger prime volume type for printers with multi nozzle + auto prime_volume_type = m_preset_bundle->project_config.option>("prime_volume_mode"); + if(!has_multiple_nozzle) + prime_volume_type->value = PrimeVolumeMode::pvmDefault; + wxGetApp().plater()->sidebar().enable_purge_mode_btn(has_multiple_nozzle); + } + m_base_preset_model = base_model; } } @@ -4753,7 +5141,7 @@ void TabPrinter::toggle_options() auto get_index_for_extruder = [this, &extruders, &nozzle_volumes](int extruder_id, int stride = 1) { return m_config->get_index_for_extruder(extruder_id + 1, "printer_extruder_id", - ExtruderType(extruders->values[extruder_id]), NozzleVolumeType(nozzle_volumes->values[extruder_id]), "printer_extruder_variant", stride); + ExtruderType(extruders->values[extruder_id]), get_actual_nozzle_volume_type(extruder_id), "printer_extruder_variant", stride); }; auto config_mode = wxGetApp().get_mode(); @@ -4785,8 +5173,9 @@ void TabPrinter::toggle_options() toggle_option("use_firmware_retraction", !is_QDT_printer); //w15 y71 - bool support_air_filtration = m_preset_bundle->printers.get_edited_preset().config.opt_bool("support_air_filtration"); - toggle_option("support_air_filtration", support_air_filtration); + toggle_line("support_air_filtration", !m_config->opt_bool("support_cooling_filter")); + toggle_line("cooling_filter_enabled", m_config->opt_bool("support_cooling_filter")); + toggle_line("auto_disable_filter_on_overheat", m_config->opt_bool("cooling_filter_enabled")); auto flavor = m_config->option>("gcode_flavor")->value; bool is_marlin_flavor = flavor == gcfMarlinLegacy || flavor == gcfMarlinFirmware; @@ -5802,7 +6191,8 @@ bool Tab::tree_sel_change_delayed(wxCommandEvent& event) if (m_variant_sizer) { wxWindow *variant_ctrl = m_extruder_switch ? (wxWindow *) m_extruder_switch : m_variant_combo; m_main_sizer->Show(m_variant_sizer, variant_ctrl->IsThisEnabled() && !m_active_page->m_opt_id_map.empty() && !m_active_page->title().StartsWith("Extruder ")); - if (m_extruder_sync) m_extruder_sync->Show(variant_ctrl->IsShown() && m_extruder_sync->IsThisEnabled()); + if (m_extruder_sync) m_extruder_sync->Show(variant_ctrl->IsShown()); + show_wiki(); GetParent()->Layout(); } @@ -5818,7 +6208,8 @@ bool Tab::tree_sel_change_delayed(wxCommandEvent& event) if (m_variant_sizer) { wxWindow *variant_ctrl = m_extruder_switch ? (wxWindow *) m_extruder_switch : m_variant_combo; m_main_sizer->Show(m_variant_sizer, variant_ctrl->IsThisEnabled() && !m_active_page->m_opt_id_map.empty() && !m_active_page->title().StartsWith("Extruder")); - if (m_extruder_sync) m_extruder_sync->Show(variant_ctrl->IsShown() && m_extruder_sync->IsThisEnabled()); + if (m_extruder_sync) m_extruder_sync->Show(variant_ctrl->IsShown()); + show_wiki(); GetParent()->Layout(); } @@ -6315,13 +6706,26 @@ void TabPrinter::set_extruder_volume_type(int extruder_id, NozzleVolumeType type auto nozzle_volumes = m_preset_bundle->project_config.option("nozzle_volume_type"); assert(nozzle_volumes->values.size() > (size_t)extruder_id); nozzle_volumes->values[extruder_id] = type; + + m_preset_bundle->extruder_nozzle_stat.on_volume_type_switch(extruder_id, type); + if (wxGetApp().plater()) { + wxGetApp().plater()->update_filament_volume_map(extruder_id, int(type)); + } + updateNozzleCountDisplay(m_preset_bundle, extruder_id, type); + on_value_change((boost::format("nozzle_volume_type#%1%") % extruder_id).str(), int(type)); update_dirty(); //save to app config if (!m_base_preset_name.empty()) { - ConfigOptionEnumsGeneric* nozzle_volume_type_option = m_preset_bundle->project_config.option("nozzle_volume_type"); - std::string nozzle_volume_type_str = nozzle_volume_type_option->serialize(); + // do not save hybrid flow status to config + ConfigOptionEnumsGeneric nozzle_volume_type_option = *m_preset_bundle->project_config.option("nozzle_volume_type"); + for(size_t i = 0; i < nozzle_volume_type_option.values.size(); i++){ + if(nozzle_volume_type_option.values[i] == (int)(nvtHybrid)){ + nozzle_volume_type_option.values[i] = (int)(nvtStandard); + } + } + std::string nozzle_volume_type_str = nozzle_volume_type_option.serialize(); wxGetApp().app_config->save_nozzle_volume_types_to_config(m_base_preset_name, nozzle_volume_type_str); } @@ -6462,6 +6866,14 @@ void Tab::set_just_edit(bool just_edit) } } +MachineObject* get_current_machine_object() +{ + DeviceManager *dev_manager = wxGetApp().getDeviceManager(); + if (!dev_manager) return nullptr; + + return dev_manager->get_selected_machine(); +} + /// /// Call from: /// 1: on_value_change "nozzle_volume_type" @@ -6477,16 +6889,23 @@ void Tab::update_extruder_variants(int extruder_id, bool reload) int extruder_nums = m_preset_bundle->get_printer_extruder_count(); nozzle_volumes->values.resize(extruder_nums); if (extruder_nums == 2) { - auto nozzle_volumes_def = m_preset_bundle->project_config.def()->get("nozzle_volume_type"); - wxString left, right; - for (size_t i = 0; i < nozzle_volumes_def->enum_labels.size(); ++i) { - if (nozzle_volumes->values[0] == i) left = _L(nozzle_volumes_def->enum_labels[i]); - if (nozzle_volumes->values[1] == i) right = _L(nozzle_volumes_def->enum_labels[i]); + auto options = generate_extruder_options(); + m_extruder_switch->SetOptions(options); + + int selection_index; + if (extruder_id >= 0) { + NozzleVolumeType current_nozzle_type = get_actual_nozzle_volume_type(extruder_id); + selection_index = calculate_selection_index_for_extruder(extruder_id, current_nozzle_type); + } else { + selection_index = m_extruder_switch->GetSelection(); + if (selection_index < 0) { + selection_index = 0; + } } - m_extruder_switch->SetLabels(wxString::Format(_L("Left: %s"), left), wxString::Format(_L("Right: %s"), right)); - m_extruder_switch->SetValue(extruder_id == 1); + + m_extruder_switch->SetSelection(selection_index); m_extruder_switch->Enable(true); - m_extruder_sync->Enable(left == right); + m_extruder_sync->Enable(true); } else { m_extruder_switch->Enable(false); m_extruder_sync->Enable(false); @@ -6494,44 +6913,186 @@ void Tab::update_extruder_variants(int extruder_id, bool reload) } else if (m_variant_combo) { if (extruder_id >= 0) // variant_combo did not depend on extruder return; - auto variants = m_config->option("filament_extruder_variant"); int n = m_variant_combo->GetSelection(); - m_variant_combo->Clear(); - for (auto &v : variants->values) { - int n = v.find("Drive "); - if (n != std::string::npos) - m_variant_combo->Append(_L(v.substr(0, n + 5)) + " " + _L(v.substr(n + 6))); - else - m_variant_combo->Append(_L(v)); + auto options = generate_extruder_options(); + m_variant_combo->SetOptions(options); + for (int i = 0; i < m_variant_combo->GetCount(); ++i) { + auto flow_type = get_actual_nozzle_flow_type(i); + auto ext_type = get_actual_extruder_type(i); + bool connected = false; + auto r_nozzles = collect_nozzles(MAIN_EXTRUDER_ID, ext_type, flow_type, connected); + std::vector l_nozzles; + if (m_preset_bundle->get_printer_extruder_count() > 1) + l_nozzles = collect_nozzles(DEPUTY_EXTRUDER_ID, ext_type, flow_type, connected); + if (connected && r_nozzles.empty() && l_nozzles.empty()) { + Button *btn = m_variant_combo->GetButton(i); + if (btn) { + btn->SetGrayed(true); + } + } } - m_variant_combo->SetSelection(n < 0 || (unsigned int)n >= m_variant_combo->GetCount() ? 0 : n); + m_variant_combo->SetSelection(n < 0 || (unsigned int) n >= m_variant_combo->GetCount() ? 0 : n); m_variant_combo->Enable(m_variant_combo->GetCount() > 1); } switch_excluder(extruder_id, reload); if (m_variant_sizer) { wxWindow *variant_ctrl = m_extruder_switch ? (wxWindow *) m_extruder_switch : m_variant_combo; m_main_sizer->Show(m_variant_sizer, variant_ctrl->IsThisEnabled() && m_active_page && !m_active_page->m_opt_id_map.empty() && !m_active_page->title().StartsWith("Extruder ")); - if (m_extruder_sync) m_extruder_sync->Show(variant_ctrl->IsShown() && m_extruder_sync->IsThisEnabled()); + if (m_extruder_sync) m_extruder_sync->Show(variant_ctrl->IsShown()); + show_wiki(); GetParent()->Layout(); } } +void Tab::show_wiki() +{ + if (m_wiki_bmp && m_wiki_label) { + auto variants = m_config->option("filament_extruder_variant")->values; + bool has_direct = std::any_of(variants.begin(), variants.end(), [](const std::string &v) { return v.find("Direct Drive") != std::string::npos; }); + m_wiki_bmp->Show(has_direct); + m_wiki_label->Show(has_direct); + } +} + +std::vector Tab::generate_extruder_options() +{ + std::vector options; + if (m_type == Preset::TYPE_FILAMENT) { + auto variants = m_config->option("filament_extruder_variant"); + for (auto &v : variants->values) { + std::string drive, nozzle; + size_t pos = v.rfind(' '); + if (pos != std::string::npos) { + drive = v.substr(0, pos); + nozzle = v.substr(pos + 1); + if (nozzle == "Flow") { + size_t pos2 = drive.rfind(' '); + if (pos2 != std::string ::npos) { + nozzle = drive.substr(pos2 + 1) + " " + nozzle; + drive = drive.substr(0, pos2); + } + } + } + options.push_back(wxString::Format(_L("%s: %s"), _L(drive), _L(nozzle))); + } + return options; + } + + auto nozzle_volumes = m_preset_bundle->project_config.option("nozzle_volume_type"); + auto nozzle_volumes_def = m_preset_bundle->project_config.def()->get("nozzle_volume_type"); + int extruder_nums = m_preset_bundle->get_printer_extruder_count(); + + if (!nozzle_volumes || extruder_nums <= 0) { + return options; + } + + for (int i = 0; i < extruder_nums; ++i) { + wxString extruder_name = (i == 0) ? _L("Left") : _L("Right"); + NozzleVolumeType volume_type = NozzleVolumeType(nozzle_volumes->values[i]); + + if (volume_type == NozzleVolumeType::nvtHybrid) { + options.push_back(wxString::Format(_L("%s: %s"), extruder_name, _L("Standard"))); + options.push_back(wxString::Format(_L("%s: %s"), extruder_name, _L("High Flow"))); + } else { + wxString volume_name = get_nozzle_volume_type_name(volume_type); + options.push_back(wxString::Format(_L("%s: %s"), extruder_name, volume_name)); + } + } + return options; +} + +NozzleVolumeType Tab::get_actual_nozzle_volume_type(int extruder_id) +{ + int extruder_count = m_preset_bundle->get_printer_extruder_count(); + auto nozzle_volumes = m_preset_bundle->project_config.option("nozzle_volume_type"); + if (extruder_count == 1) { + if (extruder_id < 0) + return NozzleVolumeType::nvtStandard; + + return NozzleVolumeType(nozzle_volumes->values[extruder_id]); + } + + if (extruder_id < 0 || extruder_id >= extruder_count) + return NozzleVolumeType::nvtStandard; + + if (m_actual_nozzle_volumes.size() != static_cast(extruder_count)) + m_actual_nozzle_volumes.resize(extruder_count, NozzleVolumeType::nvtStandard); + + return m_actual_nozzle_volumes[extruder_id]; +} + +bool Tab::get_extruder_sync_enable_state(int extruder_id) +{ + Preset& printer_preset = m_preset_bundle->printers.get_edited_preset(); + auto nozzle_volumes = m_preset_bundle->project_config.option("nozzle_volume_type"); + auto extruders = printer_preset.config.option("extruder_type"); + if (nozzle_volumes->values.size() < 2 || extruders->values.size() < 2) { + return false; + } + + NozzleVolumeType left_nozzle = NozzleVolumeType(nozzle_volumes->values[0]); + NozzleVolumeType right_nozzle = NozzleVolumeType(nozzle_volumes->values[1]); + ExtruderType left_extruder = ExtruderType(extruders->values[0]); + ExtruderType right_extruder = ExtruderType(extruders->values[1]); + + if (left_extruder != right_extruder) { + return false; + } + + if (left_nozzle == right_nozzle) { + return true; + } + + if (left_nozzle != NozzleVolumeType::nvtHybrid && right_nozzle != NozzleVolumeType::nvtHybrid) { + return false; + } + + if (left_nozzle == NozzleVolumeType::nvtHybrid && right_nozzle == NozzleVolumeType::nvtHybrid) { + return true; + } + + // Hybrid rules + auto current_nozzle = get_actual_nozzle_volume_type(extruder_id); + if (left_nozzle != NozzleVolumeType::nvtHybrid) { + if (extruder_id == 0) { + return true; + } + if (extruder_id == 1 && current_nozzle == left_nozzle) { + return true; + } + return false; + } + if (right_nozzle != NozzleVolumeType::nvtHybrid) { + if (extruder_id == 1) { + return true; + } + if (extruder_id == 0 && current_nozzle == right_nozzle) { + return true; + } + return false; + } + return false; +} + void Tab::switch_excluder(int extruder_id, bool reload) { Preset & printer_preset = m_preset_bundle->printers.get_edited_preset(); auto nozzle_volumes = m_preset_bundle->project_config.option("nozzle_volume_type"); auto extruders = printer_preset.config.option("extruder_type"); - if (m_extruder_switch && m_type != Preset::TYPE_PRINTER) { - int current_extruder = m_extruder_switch->IsThisEnabled() && m_extruder_switch->GetValue() ? 1 : 0; - m_extruder_sync->Enable(m_extruder_switch->IsThisEnabled() && extruders->values[0] == extruders->values[1] && - nozzle_volumes->values[0] == nozzle_volumes->values[1]); - m_extruder_sync->Show(m_extruder_sync->IsThisEnabled()); - if (extruder_id == -1) - extruder_id = current_extruder; - else if (extruder_id != current_extruder) - return; + if (m_extruder_switch) { + int current_extruder = get_current_active_extruder(); + bool sync_enable = get_extruder_sync_enable_state(current_extruder); + m_extruder_sync->Enable(m_extruder_switch->IsThisEnabled() && sync_enable); + m_extruder_sync->Show(); + if (m_type != Preset::TYPE_PRINTER) { + if (extruder_id == -1) + extruder_id = current_extruder; + else if (extruder_id != current_extruder) + return; + } } else if (m_variant_combo) { int current_variant = m_variant_combo->GetSelection(); + update_nozzle_status_display(); if (extruder_id == -1) extruder_id = current_variant; else if (extruder_id != current_variant) @@ -6541,7 +7102,7 @@ void Tab::switch_excluder(int extruder_id, bool reload) auto get_index_for_extruder = [this, &extruders, &nozzle_volumes, variant_keys = extruder_variant_keys[m_type >= Preset::TYPE_COUNT ? Preset::TYPE_PRINT : m_type]](int extruder_id, int stride = 1) { return m_config->get_index_for_extruder(extruder_id + 1, variant_keys.first, - ExtruderType(extruders->values[extruder_id]), NozzleVolumeType(nozzle_volumes->values[extruder_id]), variant_keys.second, stride); + ExtruderType(extruders->values[extruder_id]), get_actual_nozzle_volume_type(extruder_id), variant_keys.second, stride); }; auto index = m_variant_combo ? extruder_id : get_index_for_extruder(extruder_id == -1 ? 0 : extruder_id); if (index < 0) @@ -6596,13 +7157,14 @@ void Tab::sync_excluder() auto nozzle_volumes = m_preset_bundle->project_config.option("nozzle_volume_type"); auto extruders = printer_preset.config.option("extruder_type"); auto get_index_for_extruder = - [this, &extruders, &nozzle_volumes, variant_keys = extruder_variant_keys[m_type >= Preset::TYPE_COUNT ? Preset::TYPE_PRINT : m_type]](int extruder_id) { + [this, &extruders, &nozzle_volumes, variant_keys = extruder_variant_keys[m_type >= Preset::TYPE_COUNT ? Preset::TYPE_PRINT : m_type]](int extruder_id, NozzleVolumeType nozzle_type) { return m_config->get_index_for_extruder(extruder_id + 1, variant_keys.first, - ExtruderType(extruders->values[extruder_id]), NozzleVolumeType(nozzle_volumes->values[extruder_id]), variant_keys.second); + ExtruderType(extruders->values[extruder_id]), nozzle_type, variant_keys.second); }; - int active_index = m_extruder_switch->GetValue() ? 1 : 0; - int from_index = get_index_for_extruder(active_index); - int dest_index = get_index_for_extruder(1 - active_index); + int active_index = get_current_active_extruder(); + auto active_nozzle = get_actual_nozzle_volume_type(active_index); + int from_index = get_index_for_extruder(active_index, active_nozzle); + int dest_index = get_index_for_extruder(1 - active_index, active_nozzle); auto from_str = std::to_string(from_index); auto dest_str = std::to_string(dest_index); auto dirty_options = m_presets->current_dirty_options(true); @@ -6644,7 +7206,7 @@ void Tab::sync_excluder() wxString title = active_index == 0 ? _L("Modify paramters of right nozzle") : _L("Modify paramters of left nozzle"); wxString header = active_index == 0 ? _L("Do you want to modify the following parameters of the right nozzle to that of the left nozzle?") : _L("Do you want to modify the following parameters of the left nozzle to that of the right nozzle?"); - UnsavedChangesDialog dlg(title, header, &config_origin, from_index, dest_index, active_index == 0); + UnsavedChangesDialog dlg(title, header, &config_origin, from_index, dest_index, active_index == 0, active_nozzle); dlg.ShowModal(); if (dlg.transfer_changes()) { m_config->apply(config_to_apply); @@ -6662,6 +7224,152 @@ void Tab::sync_excluder() } } +NozzleFlowType Tab::get_actual_nozzle_flow_type(int selection) +{ + if (!m_variant_combo || selection < 0) { + return NozzleFlowType::S_FLOW; + } + if (m_type == Preset::TYPE_FILAMENT) { + auto variants = m_config->option("filament_extruder_variant"); + if (selection < static_cast(variants->values.size())) { + const std::string& variant = variants->values[selection]; + if (variant.find("High Flow") != std::string::npos) { + return NozzleFlowType::H_FLOW; + } else if (variant.find("Standard") != std::string::npos) { + return NozzleFlowType::S_FLOW; + } + return NozzleFlowType::S_FLOW; + } + } + return NozzleFlowType::S_FLOW; +} + +ExtruderType Tab::get_actual_extruder_type(int selection) +{ + return ExtruderType::etDirectDrive; +} + +void Tab::update_nozzle_status_display() +{ + if (!m_nozzle_status_sizer) return; + Freeze(); + m_nozzle_status_sizer->Clear(true); + + const Preset ¤t_printer = m_preset_bundle->printers.get_selected_preset(); + auto extruder_max_nozzle_count = current_printer.config.option("extruder_max_nozzle_count"); + bool has_multiple_nozzle = std::any_of(extruder_max_nozzle_count->values.begin(), extruder_max_nozzle_count->values.end(), [](int i) { return i > 1; }); + if (!has_multiple_nozzle) { + Thaw(); + return; + } + + int selection = m_variant_combo ? m_variant_combo->GetSelection() : -1; + NozzleFlowType flow_type = get_actual_nozzle_flow_type(selection); + ExtruderType extruder_type = get_actual_extruder_type(selection); + + bool connected = false; + auto r_nozzles = collect_nozzles(MAIN_EXTRUDER_ID, extruder_type, flow_type, connected); + std::vector l_nozzles; + if (m_preset_bundle->get_printer_extruder_count() > 1) + l_nozzles = collect_nozzles(DEPUTY_EXTRUDER_ID, extruder_type, flow_type, connected); + if (!connected) { + Thaw(); + return; + } + if (r_nozzles.empty() && l_nozzles.empty()) { + auto bmp = ScalableBitmap(this, "warning", 16); + auto warning_icon = new wxStaticBitmap(this, wxID_ANY, bmp.bmp(), wxDefaultPosition, wxDefaultSize, 0); + m_nozzle_status_sizer->Add(warning_icon, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5); + wxStaticText *reminder_text = new wxStaticText(this, wxID_ANY, _L("No available nozzles for current preset")); + reminder_text->SetFont(Label::Body_13); + reminder_text->SetForegroundColour(m_modified_label_clr); + m_nozzle_status_sizer->Add(reminder_text, 1, wxALIGN_CENTER_VERTICAL); + + Thaw(); + return; + } + + wxStaticText *reminder_text = new wxStaticText(this, wxID_ANY, _L("Available nozzles for current preset: ")); + reminder_text->SetFont(Label::Body_13); + reminder_text->SetForegroundColour(wxColour("#4479fb")); + m_nozzle_status_sizer->Add(reminder_text, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, 10); + + auto create_nozzle_button = [this](const wxString &name) { + Button *btn = new Button(); + btn->Create(this, name, "", wxBORDER_NONE); + btn->SetMinSize(wxSize(24, 24)); + btn->SetFont(wxGetApp().bold_font()); + StateColor bg_color(wxColour("#E6F7ED")); + btn->SetBackgroundColor(bg_color); + StateColor fg_color(wxColour("#4479fb")); + btn->SetTextColor(fg_color); + btn->SetCornerRadius(6); + btn->Enable(false); + + m_nozzle_status_sizer->Add(btn, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5); + }; + + if (!l_nozzles.empty()) { + create_nozzle_button("L"); + } + if (!r_nozzles.empty() && !l_nozzles.empty()) { + wxPanel *line = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(4, 16)); + line->SetBackgroundStyle(wxBG_STYLE_PAINT); + line->Bind(wxEVT_PAINT, [line](wxPaintEvent &) { + wxPaintDC dc(line); + wxColour color = wxGetApp().dark_mode() ? wxColour("#6B6B6B") : wxColour("#C8C8C8"); + dc.SetPen(wxPen(color, 1)); + int x = line->GetSize().GetWidth() / 2; + dc.DrawLine(x, 0, x, line->GetSize().GetHeight()); + }); + m_nozzle_status_sizer->Add(line, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5); + } + if (!r_nozzles.empty()) { + for (const auto nozzle : r_nozzles) { + wxString name = nozzle.IsOnRack() ? "R" + std::to_string(nozzle.GetNozzleId() + 1) : "R"; + create_nozzle_button(name); + } + } + Thaw(); +} + +std::vector Tab::collect_nozzles(int extruder_id, ExtruderType ext_type, NozzleFlowType flow_type, bool& connected) +{ + MachineObject *obj = get_current_machine_object(); + if (!obj) { + connected = false; + return {}; + } + + std::string printer_type = obj->get_show_printer_type(); + Preset& printer_preset = m_preset_bundle->printers.get_edited_preset(); + auto preset_printer_type = printer_preset.get_current_printer_type(m_preset_bundle); + if (printer_type != preset_printer_type) { + connected = false; + return {}; + } + + DevNozzleSystem *nozzle_sys = obj->GetNozzleSystem(); + DevExtderSystem *extder_sys = obj->GetExtderSystem(); + if (!nozzle_sys || !extder_sys) { + connected = false; + return {}; + } + + connected = true; + auto extder_opt = extder_sys->GetExtderById(extruder_id); + if (!extder_opt.has_value()) { + BOOST_LOG_TRIVIAL(info) << "No extruder found for extruder id " << extruder_id; + return {}; + } + + auto extder_type = ExtruderType::etDirectDrive; + if (extder_type != ext_type) { + return {}; + } + return nozzle_sys->CollectNozzles(extruder_id, flow_type); +} + void Tab::compatible_widget_reload(PresetDependencies &deps) { Field* field = this->get_field(deps.key_condition); diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index d641eaf..7e3b877 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -40,7 +40,10 @@ #include "ParamsPanel.hpp" #include "Widgets/RoundedRectangle.hpp" #include "Widgets/TextInput.hpp" +#include "Widgets/SwitchButton.hpp" #include "UnsavedChangesDialog.hpp" +#include "DeviceCore/DevDefs.h" +#include "DeviceCore/DevNozzleSystem.h" class TabCtrl; class ComboBox; @@ -54,6 +57,7 @@ namespace GUI { class TabPresetComboBox; class OG_CustomCtrl; +//extern std::set filament_options_with_variant; // Single Tab page containing a{ vsizer } of{ optgroups } // package Slic3r::GUI::Tab::Page; @@ -246,6 +250,7 @@ protected: std::vector m_dependent_tabs; enum OptStatus { osSystemValue = 1, osInitValue = 2 }; std::map m_options_list; + std::map m_all_extruder_options_status; int m_opt_status_value = 0; bool m_is_modified_values{ false }; @@ -303,9 +308,14 @@ public: SwitchButton *m_mode_view = nullptr; wxSizer * m_variant_sizer = nullptr; - SwitchButton * m_extruder_switch = nullptr; + MultiSwitchButton * m_extruder_switch = nullptr; + MultiSwitchButton * m_variant_combo = nullptr; + wxSizer * m_nozzle_status_sizer = nullptr; + wxStaticBitmap * m_wiki_bmp = nullptr; + wxStaticText * m_wiki_label = nullptr; ScalableButton *m_extruder_sync = nullptr; - ComboBox * m_variant_combo = nullptr; + wxPanel * m_extruder_sync_box = nullptr; + std::vector m_actual_nozzle_volumes; public: // QDS @@ -357,8 +367,11 @@ public: void decorate(); void update_changed_ui(); void get_sys_and_mod_flags(const std::string& opt_key, bool& sys_page, bool& modified_page); - void update_changed_tree_ui(); + void update_changed_tree_ui(); void update_undo_buttons(); + void update_extruder_switch_colors(); + void update_all_extruder_options_status(); + void check_extruder_options_status(int index, bool &sys_extruder, bool &modified_extruder, const std::vector& pages_to_check); void on_roll_back_value(const bool to_sys = false); @@ -422,6 +435,18 @@ public: void update_extruder_variants(int extruder_id = -1, bool reload = true); void switch_excluder(int extruder_id = -1, bool reload = true); void sync_excluder(); + void update_nozzle_status_display(); + void parse_extruder_selection(int selection, int &extruder_id, NozzleVolumeType &nozzle_type); + int calculate_selection_index_for_extruder(int extruder_id, NozzleVolumeType nozzle_type); + bool get_extruder_sync_enable_state(int extruder_id); + int get_current_active_extruder(); + void show_wiki(); + + std::vector generate_extruder_options(); + std::vector collect_nozzles(int extruder_id, ExtruderType ext_type, NozzleFlowType flow_type, bool& connected); + NozzleVolumeType get_actual_nozzle_volume_type(int extruder_id); + NozzleFlowType get_actual_nozzle_flow_type(int selection); + ExtruderType get_actual_extruder_type(int selection); protected: void create_line_with_widget(ConfigOptionsGroup* optgroup, const std::string& opt_key, const std::string& path, widget_t widget); @@ -602,8 +627,9 @@ public: size_t m_initial_extruders_count; size_t m_sys_extruders_count; size_t m_cache_extruder_count = 0; - std::vector m_extruder_variant_list; + std::vector m_extruder_variant_list; std::string m_base_preset_name; + std::string m_base_preset_model; PrinterTechnology m_printer_technology = ptFFF; diff --git a/src/slic3r/GUI/ThermalPreconditioningDialog.cpp b/src/slic3r/GUI/ThermalPreconditioningDialog.cpp index e73e357..cc03e69 100644 --- a/src/slic3r/GUI/ThermalPreconditioningDialog.cpp +++ b/src/slic3r/GUI/ThermalPreconditioningDialog.cpp @@ -12,8 +12,8 @@ BEGIN_EVENT_TABLE(ThermalPreconditioningDialog, wxDialog) EVT_BUTTON(wxID_OK, ThermalPreconditioningDialog::on_ok_clicked) END_EVENT_TABLE() -ThermalPreconditioningDialog::ThermalPreconditioningDialog(wxWindow *parent, std::string dev_id, const wxString &remaining_time) - : wxDialog(parent, wxID_ANY, _L("Thermal Preconditioning for first layer optimization"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) +ThermalPreconditioningDialog::ThermalPreconditioningDialog(wxWindow *parent, std::string dev_id, bool is_show_remain_time) + : wxDialog(parent, wxID_ANY, _L("Thermal Preconditioning for first layer optimization"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE) , m_dev_id(dev_id) { wxBitmap bitmap = create_scaled_bitmap("thermal_preconditioning_title", this, 16); @@ -27,11 +27,16 @@ ThermalPreconditioningDialog::ThermalPreconditioningDialog(wxWindow *parent, std this->Bind(wxEVT_TIMER, &ThermalPreconditioningDialog::on_timer, this); m_refresh_timer->Start(1000); - // Set remaining time - m_remaining_time_label->SetLabelText(_L("Remaining time: Calculating...")); + if (is_show_remain_time) + { + m_remaining_time_label->SetLabelText(_L("Remaining time: Calculating...")); + } + else + { + m_remaining_time_label->Hide(); + } - Layout(); - // Set dialog size and position + Layout(); SetSize(wxSize(FromDIP(400), FromDIP(200))); wxGetApp().UpdateDlgDarkUI(this); CentreOnScreen(); @@ -115,6 +120,7 @@ void ThermalPreconditioningDialog::on_timer(wxTimerEvent &event) { if (IsShown() && m_obj && m_obj->stage_curr == 58) { update_thermal_remaining_time(); } else { + EndModal(wxID_OK); m_refresh_timer->Stop(); } } diff --git a/src/slic3r/GUI/ThermalPreconditioningDialog.hpp b/src/slic3r/GUI/ThermalPreconditioningDialog.hpp index d13a1d1..b2d46bf 100644 --- a/src/slic3r/GUI/ThermalPreconditioningDialog.hpp +++ b/src/slic3r/GUI/ThermalPreconditioningDialog.hpp @@ -16,7 +16,7 @@ namespace GUI { class ThermalPreconditioningDialog : public wxDialog { public: - ThermalPreconditioningDialog(wxWindow *parent, std::string dev_id, const wxString &remaining_time); + ThermalPreconditioningDialog(wxWindow *parent, std::string dev_id, bool is_show_remain_time); ~ThermalPreconditioningDialog(); void update_thermal_remaining_time(); diff --git a/src/slic3r/GUI/UIHelpers/MeshBooleanUI.cpp b/src/slic3r/GUI/UIHelpers/MeshBooleanUI.cpp index 1bb3f60..03809bd 100644 --- a/src/slic3r/GUI/UIHelpers/MeshBooleanUI.cpp +++ b/src/slic3r/GUI/UIHelpers/MeshBooleanUI.cpp @@ -122,12 +122,18 @@ void MeshBooleanUI::render_content(GLCanvas3D& parent, ImGuiWrapper* imgui, bool draw_only_entity_checkbox(); - // Action buttons (OK / Cancel / Progress + extras) - draw_action_buttons(); + // Warnings and errors + draw_warnings(); - // Progress bar for async operations + // Separator line + draw_separator(); + + // Progress bar (if async operation is running) draw_progress_bar(); + // Action buttons (Execute / Cancel / Reset) + draw_action_buttons(); + // Request frame updates while async operations are running if (is_async_busy && is_async_busy() && m_parent) { m_parent->set_as_dirty(); @@ -351,34 +357,10 @@ void MeshBooleanUI::draw_action_buttons() if (m_operation_mode == MeshBooleanOperation::Difference) enable_button = m_volume_manager->validate_for_difference(); else enable_button = (m_operation_mode == MeshBooleanOperation::Union) ? m_volume_manager->validate_for_union() : m_volume_manager->validate_for_intersection(); - { - auto current_warnings = m_warning_manager->get_warnings_for_current_mode(m_operation_mode); - if (!current_warnings.empty()) { - m_warning_manager->render_warnings_list(current_warnings, m_computed_control_width, m_warning_icon_id, m_error_icon_id, m_imgui, m_computed_icon_size_display); - } else { - auto hints = m_warning_manager->get_inline_hints_for_state(m_operation_mode, *m_volume_manager); - if (!hints.empty()) { - m_warning_manager->render_warnings_list(hints, m_computed_control_width, m_warning_icon_id, m_error_icon_id, m_imgui, m_computed_icon_size_display); - } - } - } - - // Separator line - ImDrawList* draw_list = ImGui::GetWindowDrawList(); - float separator_start_x = (ImGui::GetWindowWidth() - m_computed_list_width) * 0.5f; - ImVec2 separator_pos = ImGui::GetCursorScreenPos(); - separator_pos.x = ImGui::GetWindowPos().x + separator_start_x; - ImU32 separator_color = m_is_dark_mode ? MeshBooleanConfig::COLOR_SEPARATOR_DARK : MeshBooleanConfig::COLOR_SEPARATOR; - draw_list->AddLine(ImVec2(separator_pos.x, separator_pos.y), - ImVec2(separator_pos.x + m_computed_list_width, separator_pos.y), - separator_color, 1.0f); - - ImGui::Spacing();ImGui::Spacing(); - // Calculate adaptive button widths based on text float button_spacing = 15.0f; - float button_padding = 2.0f * ImGui::GetStyle().FramePadding.x; // Use ImGui's actual frame padding - float min_button_width = 50.0f; // Minimum button width for aesthetics + float button_padding = 2.0f * ImGui::GetStyle().FramePadding.x; + float min_button_width = 50.0f; // Calculate Execute Boolean button width std::string execute_text = _L("Execute").ToStdString(); @@ -391,10 +373,13 @@ void MeshBooleanUI::draw_action_buttons() float reset_cancel_text_width = ImGui::CalcTextSize(reset_cancel_text.c_str()).x; float reset_cancel_button_width = std::max(reset_cancel_text_width + button_padding, min_button_width); - // Right-aligned Execute/Cancel buttons within LIST_WIDTH area + // Calculate total width for buttons float buttons_total_width = execute_button_width + reset_cancel_button_width + button_spacing; - float ctrl_start_x = (ImGui::GetWindowWidth() - m_computed_list_width) * 0.5f; - float buttons_start_x = ctrl_start_x + m_computed_list_width - buttons_total_width; + + // Position buttons (right-aligned) + float line_width = m_computed_list_width - 4.0f; + float ctrl_start_x = (ImGui::GetWindowWidth() - line_width) * 0.5f; + float buttons_start_x = ctrl_start_x + line_width - buttons_total_width; ImGui::SetCursorPosX(buttons_start_x); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 6.0f); @@ -440,7 +425,7 @@ void MeshBooleanUI::draw_action_buttons() // Cancel/Reset button - context sensitive bool cancel_enabled = true; // Always enabled - // Determine button text and color based on async state (reuse already calculated text) + // Determine button text based on async state (reuse already calculated text) auto button_text = async_is_busy ? _L("Cancel") : _L("Reset"); bool is_cancel = async_is_busy; @@ -453,12 +438,8 @@ void MeshBooleanUI::draw_action_buttons() ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(230.0f / 255.0f, 230.0f / 255.0f, 230.0f / 255.0f, 1.0f)); ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(163.0f / 255.0f, 163.0f / 255.0f, 163.0f / 255.0f, 1.0f)); } - } else if (is_cancel) { - // Red color for cancel button - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.8f, 0.2f, 0.2f, 1.0f)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.9f, 0.3f, 0.3f, 1.0f)); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.7f, 0.1f, 0.1f, 1.0f)); } + // Note: Cancel and Reset buttons now use default button colors (no red highlighting) if (m_imgui->button((button_text + "##btn").c_str(), reset_cancel_button_width, 0.0f)) { if (is_cancel) { @@ -473,8 +454,6 @@ void MeshBooleanUI::draw_action_buttons() if (!cancel_enabled) { ImGui::PopItemFlag(); ImGui::PopStyleColor(2); - } else if (is_cancel) { - ImGui::PopStyleColor(3); } ImGui::PopStyleVar(); @@ -645,7 +624,10 @@ bool MeshBooleanUI::load_icons() {"bool_swap_active_dark.svg", &m_swap_dark_icon_id, true}, {"bool_swap_hover.svg", &m_swap_hover_icon_id, true}, {"bool_swap_inactive.svg", &m_swap_inactive_icon_id, true}, - {"bool_swap_clicked.svg", &m_swap_clicked_icon_id, true} + {"bool_swap_clicked.svg", &m_swap_clicked_icon_id, true}, + + // Check list icon (select all) + {"bool_check_list.svg", &m_check_list_icon_id, true} }; auto load_icons_helper = [this, &resources_path](const std::vector& icons) -> bool { @@ -800,6 +782,51 @@ void MeshBooleanUI::draw_object_list(const std::string& table_name, ImVec2 size, ImVec2 title_max = ImVec2(pos.x + size.x, pos.y + m_computed_list_title_height); draw_list->AddRectFilled(title_min, title_max, title_bg_color, MeshBooleanConfig::ROUNDING_LIST, ImDrawFlags_RoundCornersTop); + // Select all button on the left side of title bar + bool all_items_selected = !items.empty() && selected_indices.size() == items.size(); + if (m_check_list_icon_id && !items.empty()) { + float button_size = m_computed_icon_size_display; + float button_margin_y = (m_computed_list_title_height - button_size) * 0.5f; + float button_margin_x = 6.0f; + ImVec2 button_pos = ImVec2(title_min.x + button_margin_x, title_min.y + button_margin_y); + ImVec2 button_max = ImVec2(button_pos.x + button_size, button_pos.y + button_size); + + // Create invisible button for click detection with unique ID + ImGui::PushID(table_name.c_str()); + ImGui::SetCursorPos(ImVec2(button_pos.x - ImGui::GetWindowPos().x, button_pos.y - ImGui::GetWindowPos().y)); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1, 1, 1, 0.1f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1, 1, 1, 0.2f)); + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0)); + bool select_all_clicked = ImGui::Button("##select_all", ImVec2(button_size, button_size)); + ImGui::PopStyleColor(4); + + // Draw the check list icon + ImU32 icon_color = ImGui::IsItemHovered() ? IM_COL32(0, 0, 0, 255) : IM_COL32(0, 0, 0, 180); + draw_list->AddImage(m_check_list_icon_id, button_pos, button_max, ImVec2(0,0), ImVec2(1,1), icon_color); + + ImGui::PopID(); + + // Handle click: toggle select all/none + if (select_all_clicked) { + if (all_items_selected) { + // Deselect all + for (size_t i = 0; i < items.size(); ++i) { + if (selected_indices.find(i) != selected_indices.end()) { + on_item_click(i, true); // was_selected = true + } + } + } else { + // Select all + for (size_t i = 0; i < items.size(); ++i) { + if (selected_indices.find(i) == selected_indices.end()) { + on_item_click(i, false); // was_selected = false + } + } + } + } + } + // Make title text center with item count std::string title; if (table_name.find("SUB_A") != std::string::npos) @@ -1182,41 +1209,100 @@ std::set MeshBooleanUI::groups_to_selected_indices( return out; } +void MeshBooleanUI::draw_warnings() +{ + if (!m_warning_manager || !m_volume_manager || !m_imgui) return; + + auto current_warnings = m_warning_manager->get_warnings_for_current_mode(m_operation_mode); + if (!current_warnings.empty()) { + m_warning_manager->render_warnings_list(current_warnings, m_computed_control_width, + m_warning_icon_id, m_error_icon_id, m_imgui, m_computed_icon_size_display); + } else { + auto hints = m_warning_manager->get_inline_hints_for_state(m_operation_mode, *m_volume_manager); + if (!hints.empty()) { + m_warning_manager->render_warnings_list(hints, m_computed_control_width, + m_warning_icon_id, m_error_icon_id, m_imgui, m_computed_icon_size_display); + } + } +} + +void MeshBooleanUI::draw_separator() +{ + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + float separator_start_x = (ImGui::GetWindowWidth() - m_computed_list_width) * 0.5f; + ImVec2 separator_pos = ImGui::GetCursorScreenPos(); + separator_pos.x = ImGui::GetWindowPos().x + separator_start_x; + ImU32 separator_color = m_is_dark_mode ? MeshBooleanConfig::COLOR_SEPARATOR_DARK : MeshBooleanConfig::COLOR_SEPARATOR; + draw_list->AddLine(ImVec2(separator_pos.x, separator_pos.y), + ImVec2(separator_pos.x + m_computed_list_width, separator_pos.y), + separator_color, 1.0f); + + ImGui::Spacing(); + ImGui::Spacing(); +} + void MeshBooleanUI::draw_progress_bar() { - // Debug: Check async status + // Check if we need to draw progress bar bool async_enabled = is_async_enabled ? is_async_enabled() : false; - bool async_busy = is_async_busy ? is_async_busy() : false; + bool async_is_busy = is_async_busy ? is_async_busy() : false; // Only show progress bar if async is enabled and busy - if (!async_enabled || !async_busy) { + if (!async_enabled || !async_is_busy) { return; } float progress = get_async_progress ? get_async_progress() : 0.0f; - // Clamp progress to [0.0, 1.0] range - progress = std::max(0.0f, std::min(1.0f, progress)); + // Calculate layout + float line_width = m_computed_list_width - 4.0f; + float ctrl_start_x = (ImGui::GetWindowWidth() - line_width) * 0.5f; - ImGui::Spacing(); + // === First line: Progress bar with percentage === + ImGui::SetCursorPosX(ctrl_start_x); + ImGui::BeginGroup(); - // Center the progress bar - float progress_width = m_computed_control_width; - set_centered_cursor_x(progress_width); + // Calculate percentage text width + std::string progress_text = GUI::format(_L("%1%"), int(progress + 0.5f)) + "%%"; + float progress_text_width = ImGui::CalcTextSize(progress_text.c_str()).x; + float text_padding = 8.0f; - // Progress bar styling (matching original QIDIStudio style) - ImGui::PushStyleColor(ImGuiCol_PlotHistogram, ImVec4(0.27f, 0.47f, 0.98f, 1.00f)); // Green progress //y73 - - // Progress bar with percentage text - std::string progress_text = GUI::format(_L("%1%"), int(progress * 100.0f + 0.5f)) + "%%"; - ImVec2 progress_size(progress_width, 24.0f); + // Progress bar width + float progress_bar_width = line_width - progress_text_width - text_padding; + // Draw progress bar + ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 6.0f); + ImGui::PushStyleColor(ImGuiCol_PlotHistogram, ImVec4(0.00f, 0.68f, 0.26f, 1.00f)); + ImVec2 progress_size(progress_bar_width, 0.0f); + ImGui::QDTProgressBar2(progress / 100.0f, progress_size); ImGui::PopStyleColor(1); + ImGui::PopStyleVar(); - // Progress text on the side - ImGui::SameLine(); + // Draw percentage text on the same line + ImGui::SameLine(0, text_padding); ImGui::AlignTextToFramePadding(); ImGui::TextColored(ImVec4(0.42f, 0.42f, 0.42f, 1.00f), progress_text.c_str()); + + ImGui::EndGroup(); + + // === Second line: Status text === + ImGui::SetCursorPosX(ctrl_start_x); + + // Determine status text and color based on progress + std::string status_text; + ImVec4 text_color = ImVec4(0.42f, 0.42f, 0.42f, 1.0f); + + if (progress < 10.0f) { + status_text = _u8L("Preparing data..."); + } else if (progress < 90.0f) { + status_text = _u8L("Computing..."); + } else { + status_text = _u8L("Applying result.."); + } + + ImGui::TextColored(text_color, status_text.c_str()); + + ImGui::Spacing(); } // ========================== ICON HELPER FUNCTIONS ========================== diff --git a/src/slic3r/GUI/UIHelpers/MeshBooleanUI.hpp b/src/slic3r/GUI/UIHelpers/MeshBooleanUI.hpp index f66fed7..2b5b53f 100644 --- a/src/slic3r/GUI/UIHelpers/MeshBooleanUI.hpp +++ b/src/slic3r/GUI/UIHelpers/MeshBooleanUI.hpp @@ -165,6 +165,8 @@ private: ImTextureID m_swap_inactive_icon_id{0}; ImTextureID m_swap_clicked_icon_id{0}; + ImTextureID m_check_list_icon_id{0}; + bool m_icons_loaded{false}; // ========================== UI RENDERING METHODS ========================== @@ -175,7 +177,9 @@ private: void draw_control_buttons(); void draw_action_buttons(); void draw_only_entity_checkbox(); - void draw_progress_bar(); // Async progress display + void draw_warnings(); // Warnings and error messages + void draw_separator(); // Separator line between content and controls + void draw_progress_bar(); // Async progress display (progress bar + status text) // Tab and button helpers bool draw_tab_button(const char* icon_name, const char* text, bool selected, diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index f0bbb8a..4fe880f 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -806,15 +806,7 @@ UnsavedChangesDialog::UnsavedChangesDialog(const wxString &caption, const wxStri wxGetApp().UpdateDlgDarkUI(this); } -struct SyncExtruderParams -{ - DynamicConfig *config; - int from; - int to; - bool left_to_right; -}; - -UnsavedChangesDialog::UnsavedChangesDialog(const wxString &caption, const wxString &header, DynamicConfig *config, int from, int to, bool left_to_right) +UnsavedChangesDialog::UnsavedChangesDialog(const wxString &caption, const wxString &header, DynamicConfig *config, int from, int to, bool left_to_right, NozzleVolumeType nozzle) : DPIDialog(static_cast(wxGetApp().mainframe), wxID_ANY, caption, @@ -823,7 +815,7 @@ UnsavedChangesDialog::UnsavedChangesDialog(const wxString &caption, const wxStri wxCAPTION | wxCLOSE_BOX) , m_buttons(ActionButtons::SAVE | ActionButtons::DONT_SAVE) { - SyncExtruderParams params { config, from, to, left_to_right }; + SyncExtruderParams params { config, from, to, left_to_right, nozzle }; build(Preset::TYPE_PRINT, reinterpret_cast(¶ms), "SyncExtruderParams", header); this->CenterOnScreen(); wxGetApp().UpdateDlgDarkUI(this); @@ -933,12 +925,10 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection *dependent_ wxBoxSizer *top_title_oldv = new wxBoxSizer(wxVERTICAL); wxBoxSizer *top_title_oldv_h = new wxBoxSizer(wxHORIZONTAL); - wxString modified = _L("(Modified)"); - static_oldv_title = new wxStaticText(m_panel_oldv, wxID_ANY, params ? _L("Left nozzle") + (params->left_to_right ? "" : modified) : _L("Preset(Old)"), wxDefaultPosition, - wxDefaultSize, 0); + static_oldv_title = new wxStaticText(m_panel_oldv, wxID_ANY, params ? _L("Left nozzle") + ": " + get_nozzle_volume_type_name(params->nozzle) : _L("Preset(Old)"), wxDefaultPosition, wxDefaultSize, 0); static_oldv_title->SetFont(::Label::Body_13); static_oldv_title->Wrap(-1); - static_oldv_title->SetForegroundColour(*wxWHITE); + static_oldv_title->SetForegroundColour(params && params->left_to_right ? wxGetApp().get_label_clr_modified() : *wxWHITE); top_title_oldv_h->Add(static_oldv_title, 0, wxALIGN_CENTER | wxBOTTOM | wxTOP, 5); top_title_oldv->Add(top_title_oldv_h, 1, wxALIGN_CENTER, 0); m_panel_oldv->SetSizer(top_title_oldv); @@ -954,11 +944,11 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection *dependent_ wxBoxSizer *top_title_newv = new wxBoxSizer(wxVERTICAL); wxBoxSizer *top_title_newv_h = new wxBoxSizer(wxHORIZONTAL); - static_newv_title = new wxStaticText(m_panel_newv, wxID_ANY, params ? _L("Right nozzle") + (!params->left_to_right ? "" : modified) : _L("Modified Value(New)"), + static_newv_title = new wxStaticText(m_panel_newv, wxID_ANY, params ? _L("Right nozzle") + ": " + get_nozzle_volume_type_name(params->nozzle) : _L("Modified Value(New)"), wxDefaultPosition, wxDefaultSize, 0); static_newv_title->SetFont(::Label::Body_13); static_newv_title->Wrap(-1); - static_newv_title->SetForegroundColour(*wxWHITE); + static_newv_title->SetForegroundColour(params && !params->left_to_right ? wxGetApp().get_label_clr_modified() : *wxWHITE); top_title_newv_h->Add(static_newv_title, 0, wxALIGN_CENTER | wxBOTTOM | wxTOP, 5); @@ -1095,7 +1085,7 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection *dependent_ update_tree(type, params->config, params->to, params->from); m_action_line->SetLabel(header); m_action_line->Wrap(UNSAVE_CHANGE_DIALOG_SCROLL_WINDOW_SIZE.x); - update_list(); + update_list(params); } else { update(type, dependent_presets, new_selected_preset, header); } @@ -1506,7 +1496,7 @@ void UnsavedChangesDialog::update(Preset::Type type, PresetCollection* dependent update_list(); } -void UnsavedChangesDialog::update_list() +void UnsavedChangesDialog::update_list(SyncExtruderParams *params) { if (!m_scrolledWindow) { Layout(); @@ -1652,7 +1642,7 @@ void UnsavedChangesDialog::update_list() auto text_oldv = new wxStaticText(panel_oldv, wxID_ANY, data.old_value, wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END); text_oldv->SetFont(::Label::Body_13); text_oldv->Wrap(-1); - text_oldv->SetForegroundColour(GREY700); + text_oldv->SetForegroundColour(params && params->left_to_right ? wxGetApp().get_label_clr_modified() : GREY700); sizer_old_v->Add(text_oldv, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT, 5); panel_oldv->SetSizer(sizer_old_v); @@ -1666,7 +1656,7 @@ void UnsavedChangesDialog::update_list() auto text_newv = new wxStaticText(panel_newv, wxID_ANY, data.new_value, wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END); text_newv->SetFont(::Label::Body_13); text_newv->Wrap(-1); - text_newv->SetForegroundColour(GREY700); + text_newv->SetForegroundColour(params && !params->left_to_right ? wxGetApp().get_label_clr_modified() : GREY700); sizer_new_v->Add(text_newv, 0, wxALIGN_CENTER|wxLEFT|wxRIGHT, 5); diff --git a/src/slic3r/GUI/UnsavedChangesDialog.hpp b/src/slic3r/GUI/UnsavedChangesDialog.hpp index ae07799..9772e87 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.hpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.hpp @@ -329,18 +329,27 @@ public: REMEMBER_CHOISE = 0x10000 }; + struct SyncExtruderParams + { + DynamicConfig *config; + int from; + int to; + bool left_to_right; + NozzleVolumeType nozzle; + }; + // show unsaved changes when preset is switching UnsavedChangesDialog(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset, bool no_transfer = false); // show unsaved changes for all another cases UnsavedChangesDialog(const wxString& caption, const wxString& header, const std::string& app_config_key, int act_buttons); - UnsavedChangesDialog(const wxString &caption, const wxString &header, DynamicConfig *config, int from, int to, bool left_to_right); + UnsavedChangesDialog(const wxString &caption, const wxString &header, DynamicConfig *config, int from, int to, bool left_to_right, NozzleVolumeType nozzle); ~UnsavedChangesDialog(){}; int ShowModal(); void build(Preset::Type type, PresetCollection *dependent_presets, const std::string &new_selected_preset, const wxString &header = ""); void update(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset, const wxString& header); - void update_list(); + void update_list(SyncExtruderParams *params = nullptr); std::string subreplace(std::string resource_str, std::string sub_str, std::string new_str); void update_tree(Preset::Type type, PresetCollection *presets); void update_tree(Preset::Type type, DynamicConfig *config, int from, int to); diff --git a/src/slic3r/GUI/UpgradePanel.cpp b/src/slic3r/GUI/UpgradePanel.cpp index 2d86762..21a94d8 100644 --- a/src/slic3r/GUI/UpgradePanel.cpp +++ b/src/slic3r/GUI/UpgradePanel.cpp @@ -9,8 +9,13 @@ #include "libslic3r/Thread.hpp" #include "DeviceCore/DevFilaSystem.h" +#include "DeviceCore/DevNozzleSystem.h" +#include "DeviceCore/DevUpgrade.h" + #include "DeviceCore/DevManager.h" +#include "DeviceTab/wgtDeviceNozzleRackUpdate.h" + namespace Slic3r { namespace GUI { @@ -141,8 +146,8 @@ MachineInfoPanel::MachineInfoPanel(wxWindow* parent, wxWindowID id, const wxPoin m_ams_sizer->Add(m_ams_img, 0, wxALIGN_TOP | wxALL, FromDIP(5)); wxBoxSizer *m_ams_content_sizer = new wxBoxSizer(wxVERTICAL); - m_ams_content_sizer->Add(0, 40, 0, wxEXPAND, FromDIP(5)); - + m_ams_content_sizer->Add(0, 10, 0, wxEXPAND, FromDIP(5)); + m_ams_content_sizer->Add(0, 0, 1, wxEXPAND, 0); m_ahb_panel = new AmsPanel(this, wxID_ANY); m_ams_content_sizer->Add(m_ahb_panel, 0, wxEXPAND, 0); @@ -164,6 +169,7 @@ MachineInfoPanel::MachineInfoPanel(wxWindow* parent, wxWindowID id, const wxPoin //} m_ams_content_sizer->Add(m_ams_info_sizer, 0, wxEXPAND, 0); + m_ams_content_sizer->Add(0, 0, 1, wxEXPAND, 0); m_ams_sizer->Add(m_ams_content_sizer, 1, wxEXPAND, 0); m_main_left_sizer->Add(m_ams_sizer, 0, wxEXPAND, 0); @@ -217,6 +223,9 @@ MachineInfoPanel::MachineInfoPanel(wxWindow* parent, wxWindowID id, const wxPoin createAirPumpWidgets(m_main_left_sizer); createExtinguishWidgets(m_main_left_sizer); + // nozzle rack widgets + createNozzleRackWidgets(m_main_left_sizer); + m_main_sizer->Add(m_main_left_sizer, 1, wxEXPAND, 0); wxBoxSizer *m_main_right_sizer = new wxBoxSizer(wxVERTICAL); @@ -297,7 +306,47 @@ MachineInfoPanel::MachineInfoPanel(wxWindow* parent, wxWindowID id, const wxPoin m_button_upgrade_firmware->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(MachineInfoPanel::on_upgrade_firmware), NULL, this); wxGetApp().UpdateDarkUIWin(this); } +void MachineInfoPanel::createNozzleRackWidgets(wxBoxSizer *main_left_sizer) +{ + // horizontal line above + m_nozzle_rack_line_above = new wxStaticLine(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL); + m_nozzle_rack_line_above->SetBackgroundColour(wxColour(206, 206, 206)); + main_left_sizer->Add(m_nozzle_rack_line_above, 0, wxEXPAND | wxLEFT, FromDIP(40)); + m_nozzle_rack_sizer = new wxBoxSizer(wxHORIZONTAL); + + // left placeholder icon (keep consistent spacing with others) + m_nozzle_rack_img = new wxStaticBitmap(this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize(FromDIP(200), FromDIP(200))); + m_nozzle_rack_img->SetBitmap(m_img_nozzle_rack.bmp()); + m_nozzle_rack_sizer->Add(m_nozzle_rack_img, 0, wxALIGN_CENTER_VERTICAL | wxALL, FromDIP(5)); + + // right content: label + update button + auto *content_sizer = new wxBoxSizer(wxHORIZONTAL); + m_nozzle_rack_text = new wxStaticText(this, wxID_ANY, _L("Hotends on Rack"), wxDefaultPosition, wxDefaultSize, 0); + m_nozzle_rack_text->Wrap(-1); + m_nozzle_rack_text->SetFont(Label::Head_14); + content_sizer->Add(m_nozzle_rack_text, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT | wxLEFT, FromDIP(50)); + + m_nozzle_rack_update_btn = new Button(this, _L("Info")); + StateColor btn_bg(std::pair(wxColour(255, 255, 255), StateColor::Disabled), std::pair(wxColour(200, 200, 200), StateColor::Pressed), + std::pair(wxColour(240, 240, 240), StateColor::Hovered), std::pair(wxColour(255, 255, 255), StateColor::Enabled), + std::pair(wxColour(255, 255, 255), StateColor::Normal)); + StateColor btn_bd(std::pair(wxColour(200, 200, 200), StateColor::Disabled), std::pair(wxColour(150, 150, 150), StateColor::Enabled)); + StateColor btn_text(std::pair(wxColour(150, 150, 150), StateColor::Disabled), std::pair(wxColour(0, 0, 0), StateColor::Enabled)); + m_nozzle_rack_update_btn->SetBackgroundColor(btn_bg); + m_nozzle_rack_update_btn->SetBorderColor(btn_bd); + m_nozzle_rack_update_btn->SetTextColor(btn_text); + m_nozzle_rack_update_btn->SetFont(Label::Body_10.Bold()); + m_nozzle_rack_update_btn->SetMinSize(wxSize(FromDIP(-1), FromDIP(24))); + m_nozzle_rack_update_btn->SetCornerRadius(FromDIP(12)); + m_nozzle_rack_update_btn->Bind(wxEVT_BUTTON, &MachineInfoPanel::on_nozzle_rack_update, this); + content_sizer->Add(m_nozzle_rack_update_btn, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(350)); + + + m_nozzle_rack_sizer->Add(content_sizer, 1, wxEXPAND, 0); + + main_left_sizer->Add(m_nozzle_rack_sizer, 0, wxEXPAND, 0); +} wxPanel *MachineInfoPanel::create_caption_panel(wxWindow *parent) { @@ -330,20 +379,24 @@ void MachineInfoPanel::createAirPumpWidgets(wxBoxSizer* main_left_sizer) { m_air_pump_line_above = new wxStaticLine(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL); m_air_pump_line_above->SetBackgroundColour(wxColour(206, 206, 206)); - main_left_sizer->Add(m_air_pump_line_above, 0, wxEXPAND | wxLEFT, FromDIP(40)); m_air_pump_img = new wxStaticBitmap(this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize(FromDIP(200), FromDIP(200))); m_air_pump_img->SetBitmap(m_img_air_pump.bmp()); - wxBoxSizer* content_sizer = new wxBoxSizer(wxVERTICAL); - content_sizer->Add(0, 40, 0, wxEXPAND, FromDIP(5)); - m_air_pump_version = new uiDeviceUpdateVersion(this, wxID_ANY); - content_sizer->Add(m_air_pump_version, 0, wxEXPAND, 0); + auto panel_pump = new wxPanel(this); + wxBoxSizer* content_sizer_v = new wxBoxSizer(wxVERTICAL); + wxBoxSizer* content_sizer_h = new wxBoxSizer(wxHORIZONTAL); + + m_air_pump_version = new uiDeviceUpdateVersion(panel_pump, wxID_ANY); + content_sizer_h->Add(m_air_pump_version, 0, wxALIGN_CENTER, 0); + content_sizer_v->Add(content_sizer_h, 1, wxLEFT, 0); + panel_pump->SetSizer(content_sizer_v); m_air_pump_sizer = new wxBoxSizer(wxHORIZONTAL); m_air_pump_sizer->Add(m_air_pump_img, 0, wxALIGN_TOP | wxALL, FromDIP(5)); - m_air_pump_sizer->Add(content_sizer, 1, wxEXPAND, 0); + m_air_pump_sizer->Add(panel_pump, 1, wxEXPAND, 0); + main_left_sizer->Add(m_air_pump_line_above, 0, wxEXPAND | wxLEFT, FromDIP(40)); main_left_sizer->Add(m_air_pump_sizer, 0, wxEXPAND, 0); } @@ -356,14 +409,20 @@ void MachineInfoPanel::createCuttingWidgets(wxBoxSizer* main_left_sizer) m_cutting_img = new wxStaticBitmap(this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize(FromDIP(200), FromDIP(200))); m_cutting_img->SetBitmap(m_img_cutting.bmp()); - wxBoxSizer* content_sizer = new wxBoxSizer(wxVERTICAL); - content_sizer->Add(0, 40, 0, wxEXPAND, FromDIP(5)); - m_cutting_version = new uiDeviceUpdateVersion(this, wxID_ANY); - content_sizer->Add(m_cutting_version, 0, wxEXPAND, 0); + auto panel_cut = new wxPanel(this); + wxBoxSizer *content_sizer_h = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer *content_sizer_v = new wxBoxSizer(wxVERTICAL); + + m_cutting_version = new uiDeviceUpdateVersion(panel_cut, wxID_ANY); + + content_sizer_h->Add(m_cutting_version, 0, wxALIGN_CENTER, 0); + content_sizer_v->Add(content_sizer_h, 1, wxLEFT, 0); + + panel_cut->SetSizer(content_sizer_v); m_cutting_sizer = new wxBoxSizer(wxHORIZONTAL); m_cutting_sizer->Add(m_cutting_img, 0, wxALIGN_TOP | wxALL, FromDIP(5)); - m_cutting_sizer->Add(content_sizer, 1, wxEXPAND, 0); + m_cutting_sizer->Add(panel_cut, 1, wxEXPAND, 0); main_left_sizer->Add(m_cutting_sizer, 0, wxEXPAND, 0); }; @@ -375,18 +434,23 @@ void MachineInfoPanel::createLaserWidgets(wxBoxSizer* main_left_sizer) main_left_sizer->Add(m_laser_line_above, 0, wxEXPAND | wxLEFT, FromDIP(40)); m_lazer_img = new wxStaticBitmap(this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize(FromDIP(200), FromDIP(200))); - - m_lazer_img->SetBitmap(m_img_laser.bmp()); - wxBoxSizer* content_sizer = new wxBoxSizer(wxVERTICAL); - content_sizer->Add(0, 40, 0, wxEXPAND, FromDIP(5)); - m_laser_version = new uiDeviceUpdateVersion(this, wxID_ANY); - content_sizer->Add(m_laser_version, 0, wxEXPAND, 0); + + auto panel_laser = new wxPanel(this); + wxBoxSizer *content_sizer_h = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer *content_sizer_v = new wxBoxSizer(wxVERTICAL); + + m_laser_version = new uiDeviceUpdateVersion(panel_laser, wxID_ANY); + + content_sizer_h->Add(m_laser_version, 0, wxALIGN_CENTER, 0); + content_sizer_v->Add(content_sizer_h, 1, wxLEFT, 0); + + panel_laser->SetSizer(content_sizer_v); m_laser_sizer = new wxBoxSizer(wxHORIZONTAL); m_laser_sizer->Add(m_lazer_img, 0, wxALIGN_TOP | wxALL, FromDIP(5)); - m_laser_sizer->Add(content_sizer, 1, wxEXPAND, 0); + m_laser_sizer->Add(panel_laser, 1, wxEXPAND, 0); main_left_sizer->Add(m_laser_sizer, 0, wxEXPAND, 0); } @@ -400,14 +464,20 @@ void MachineInfoPanel::createExtinguishWidgets(wxBoxSizer* main_left_sizer) m_extinguish_img = new wxStaticBitmap(this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize(FromDIP(200), FromDIP(200))); m_extinguish_img->SetBitmap(m_img_extinguish.bmp()); - wxBoxSizer* content_sizer = new wxBoxSizer(wxVERTICAL); - content_sizer->Add(0, 40, 0, wxEXPAND, FromDIP(5)); - m_extinguish_version = new uiDeviceUpdateVersion(this, wxID_ANY); - content_sizer->Add(m_extinguish_version, 0, wxEXPAND, 0); + auto panel_extinguish = new wxPanel(this); + wxBoxSizer *content_sizer_h = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer *content_sizer_v = new wxBoxSizer(wxVERTICAL); + + m_extinguish_version = new uiDeviceUpdateVersion(panel_extinguish, wxID_ANY); + + content_sizer_h->Add(m_extinguish_version, 0, wxALIGN_CENTER, 0); + content_sizer_v->Add(content_sizer_h, 1, wxLEFT, 0); + + panel_extinguish->SetSizer(content_sizer_v); m_extinguish_sizer = new wxBoxSizer(wxHORIZONTAL); m_extinguish_sizer->Add(m_extinguish_img, 0, wxALIGN_TOP | wxALL, FromDIP(5)); - m_extinguish_sizer->Add(content_sizer, 1, wxEXPAND, 0); + m_extinguish_sizer->Add(panel_extinguish, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL, 0); main_left_sizer->Add(m_extinguish_sizer, 0, wxEXPAND, 0); } @@ -438,9 +508,14 @@ void MachineInfoPanel::init_bitmaps() m_img_air_pump = ScalableBitmap(this, "air_pump", 160); m_img_extra_ams = ScalableBitmap(this, "extra_icon_png", 160); - m_img_laser = ScalableBitmap(this, "laser", 160); - m_img_cutting = ScalableBitmap(this, "cut", 160); - m_img_extinguish = ScalableBitmap(this, "extinguish", 160); + m_img_laser = ScalableBitmap(this, "laser", 160); + m_img_cutting = ScalableBitmap(this, "cut", 160); + m_img_extinguish = ScalableBitmap(this, "extinguish", 160); // TODO + m_img_nozzle_rack = ScalableBitmap(this, "nozzle_rack", 160); + + upgrade_green_icon = ScalableBitmap(this, "monitor_upgrade_online", 5); + upgrade_gray_icon = ScalableBitmap(this, "monitor_upgrade_offline", 5); + upgrade_yellow_icon = ScalableBitmap(this, "monitor_upgrade_busy", 5); upgrade_green_icon = ScalableBitmap(this, "monitor_upgrade_online", 5); upgrade_gray_icon = ScalableBitmap(this, "monitor_upgrade_offline", 5); @@ -510,6 +585,11 @@ void MachineInfoPanel::update(MachineObject* obj) m_obj = obj; if (obj) { + auto upgrade_ptr = obj->GetUpgrade().lock(); + if (!upgrade_ptr) { + return; + } + this->Freeze(); //update online status img m_panel_caption->Freeze(); @@ -517,10 +597,10 @@ void MachineInfoPanel::update(MachineObject* obj) m_upgrade_status_img->SetBitmap(upgrade_gray_icon.bmp()); wxString caption_text = wxString::Format("%s(%s)", from_u8(obj->get_dev_name()), _L("Offline")); m_caption_text->SetLabelText(caption_text); - show_status((int)DevFirmwareUpgradingState::UpgradingUnavaliable); + show_status((int)DevFirmwareUpgradeState::UpgradingUnavaliable); } else { - show_status((int)obj->upgrade_display_state, obj->upgrade_status); - if (obj->upgrade_display_state == DevFirmwareUpgradingState::UpgradingUnavaliable) { + show_status((int)upgrade_ptr->GetUpgradeState(), upgrade_ptr->GetUpgradeStatusStr()); + if (upgrade_ptr->GetUpgradeState() == DevFirmwareUpgradeState::UpgradingUnavaliable) { if (obj->can_abort()) { wxString caption_text = wxString::Format("%s(%s)", from_u8(obj->get_dev_name()), _L("Printing")); m_caption_text->SetLabelText(caption_text); @@ -549,16 +629,18 @@ void MachineInfoPanel::update(MachineObject* obj) update_cut(obj); update_laszer(obj); update_extinguish(obj); + update_nozzle_rack(obj); //update progress - int upgrade_percent = obj->get_upgrade_percent(); - if (obj->upgrade_display_state == DevFirmwareUpgradingState::UpgradingInProgress) { - m_upgrade_progress->SetValue(upgrade_percent); - m_staticText_upgrading_percent->SetLabelText(wxString::Format("%d%%", upgrade_percent)); - } else if (obj->upgrade_display_state == DevFirmwareUpgradingState::UpgradingFinished) { - wxString result_text = obj->get_upgrade_result_str(obj->upgrade_err_code); - m_upgrade_progress->SetValue(upgrade_percent); - m_staticText_upgrading_percent->SetLabelText(wxString::Format("%d%%", upgrade_percent)); + if (upgrade_ptr) { + int upgrade_percent = upgrade_ptr->GetUpgradeProgressInt(); + if (upgrade_ptr->GetUpgradeState() == DevFirmwareUpgradeState::UpgradingInProgress) { + m_upgrade_progress->SetValue(upgrade_percent); + m_staticText_upgrading_percent->SetLabelText(wxString::Format("%d%%", upgrade_percent)); + } else if (upgrade_ptr->GetUpgradeState() == DevFirmwareUpgradeState::UpgradingFinished) { + m_upgrade_progress->SetValue(upgrade_percent); + m_staticText_upgrading_percent->SetLabelText(wxString::Format("%d%%", upgrade_percent)); + } } wxString model_id_text = obj->get_printer_type_display_str(); @@ -573,8 +655,12 @@ void MachineInfoPanel::update(MachineObject* obj) void MachineInfoPanel::update_version_text(MachineObject* obj) { + auto upgrade_ptr = obj->GetUpgrade().lock(); + if (!upgrade_ptr) { + return; + } - if (obj->upgrade_display_state == DevFirmwareUpgradingState::UpgradingInProgress) { + if (upgrade_ptr->IsUpgrading()) { m_staticText_ver_val->SetLabelText("-"); //m_staticText_ams_ver_val->SetLabelText("-"); m_ota_new_version_img->Hide(); @@ -583,15 +669,15 @@ void MachineInfoPanel::update_version_text(MachineObject* obj) auto it = obj->module_vers.find("ota"); // old protocol - if (obj->new_ver_list.empty() && !obj->m_new_ver_list_exist) { - if (obj->upgrade_new_version - && !obj->ota_new_version_number.empty()) { + if (upgrade_ptr->GetNewVersionList().empty()) { + if (upgrade_ptr->HasNewVersion() + && !upgrade_ptr->GetOtaNewVersion().empty()) { if (it != obj->module_vers.end()) { wxString ver_text= it->second.sw_ver; if ((it->second.firmware_flag & 0x3) == FIRMWARE_STASUS::BETA) { ver_text+= wxString::Format("(%s)", _L("Beta version")); } - ver_text += wxString::Format("->%s", obj->ota_new_version_number); + ver_text += wxString::Format("->%s", upgrade_ptr->GetOtaNewVersion()); if (((it->second.firmware_flag >> 2) & 0x3) == FIRMWARE_STASUS::BETA) { ver_text += wxString::Format("(%s)", _L("Beta version")); } @@ -620,8 +706,9 @@ void MachineInfoPanel::update_version_text(MachineObject* obj) m_ota_new_version_img->Hide(); } } else { - auto ota_it = obj->new_ver_list.find("ota"); - if (ota_it == obj->new_ver_list.end()) { + const auto &new_version_list = upgrade_ptr->GetNewVersionList(); + auto ota_it = new_version_list.find("ota"); + if (ota_it == new_version_list.end()) { if (it != obj->module_vers.end()) { wxString ver_text = wxString::Format("%s(%s)", it->second.sw_ver, _L("Latest version")); if ((it->second.firmware_flag & 0x3) == FIRMWARE_STASUS::BETA) { @@ -668,6 +755,13 @@ void MachineInfoPanel::update_version_text(MachineObject* obj) void MachineInfoPanel::update_ams_ext(MachineObject *obj) { + auto upgrade_ptr = obj->GetUpgrade().lock(); + if (!upgrade_ptr) { + return; + } + + const auto &new_version_list = upgrade_ptr->GetNewVersionList(); + bool has_hub_model = false; bool is_o_series = obj->is_series_o(); @@ -719,8 +813,8 @@ void MachineInfoPanel::update_ams_ext(MachineObject *obj) hub_ver = wxString::Format("%s(%s)", hub_ver, _L("Latest version")); }*/ - if (obj->new_ver_list.empty() && !obj->m_new_ver_list_exist) { - if (obj->upgrade_new_version && obj->ahb_new_version_number.compare(obj->module_vers.find("ahb")->second.sw_ver) != 0) { + if (new_version_list.empty()) { + if (upgrade_ptr->HasNewVersion() && obj->ahb_new_version_number.compare(obj->module_vers.find("ahb")->second.sw_ver) != 0) { m_ahb_panel->m_ams_new_version_img->Show(); if (obj->ahb_new_version_number.empty()) { @@ -734,9 +828,8 @@ void MachineInfoPanel::update_ams_ext(MachineObject *obj) hub_ver = ver_text; } } else { - auto ver_item = obj->new_ver_list.find("ahb"); - - if (ver_item == obj->new_ver_list.end()) { + auto ver_item = new_version_list.find("ahb"); + if (ver_item == new_version_list.end()) { m_ahb_panel->m_ams_new_version_img->Hide(); wxString ver_text = wxString::Format("%s(%s)", obj->module_vers.find("ahb")->second.sw_ver, _L("Latest version")); hub_ver = ver_text; @@ -758,7 +851,7 @@ void MachineInfoPanel::update_ams_ext(MachineObject *obj) } //ams - if (obj->ams_exist_bits != 0) + if (obj->ams_exist_bits != 0) { std::string extra_ams_str = (boost::format("ams_f1/%1%") % 0).str(); auto extra_ams_it = obj->module_vers.find(extra_ams_str); @@ -769,8 +862,8 @@ void MachineInfoPanel::update_ams_ext(MachineObject *obj) wxString ver_text = extra_ams_it->second.sw_ver; bool has_new_version = false; - auto new_extra_ams_ver = obj->new_ver_list.find(extra_ams_str); - if (new_extra_ams_ver != obj->new_ver_list.end()) + auto new_extra_ams_ver = new_version_list.find(extra_ams_str); + if (new_extra_ams_ver != new_version_list.end()) has_new_version = true; extra_ams_it->second.sw_new_ver; @@ -812,8 +905,8 @@ void MachineInfoPanel::update_ams_ext(MachineObject *obj) show_ams(true); std::map ver_list = obj->get_ams_version(); - if (obj->GetFilaSystem()->GetAmsList().size() != m_amspanel_list.size()) { - int add_count = obj->GetFilaSystem()->GetAmsList().size() - m_amspanel_list.size(); + if (ver_list.size() != m_amspanel_list.size()) { + int add_count = ver_list.size() - m_amspanel_list.size(); if (add_count > 0) { for (int i = 0; i < add_count; i++) { auto amspanel = new AmsPanel(this, wxID_ANY); @@ -837,7 +930,9 @@ void MachineInfoPanel::update_ams_ext(MachineObject *obj) auto ams_index = 0; const auto& ams_list = obj->GetFilaSystem()->GetAmsList(); - for (std::map::const_iterator iter = ams_list.cbegin(); iter != ams_list.cend(); iter++) { + const int ams_size = ver_list.size(); + // for (int i = 0; i <= ver_list.size(); i++) + for (auto iter_ams = ver_list.cbegin(); iter_ams != ver_list.cend(); iter_ams++) { wxString ams_name; wxString ams_sn; wxString ams_ver; @@ -845,26 +940,26 @@ void MachineInfoPanel::update_ams_ext(MachineObject *obj) AmsPanel* amspanel = m_amspanel_list[ams_index]; amspanel->Show(); - auto it = ver_list.find(atoi(iter->first.c_str())); + /* auto it = ver_list.find(atoi(iter->first.c_str())); - if (it == ver_list.end()) { - continue; - } + if (it == ver_list.end()) { + continue; + }*/ - auto ams_id = std::stoi(iter->second->GetAmsId()); + auto ams_id = iter_ams->first; ams_id -= ams_id >= 128 ? 128 : 0; - if (!it->second.product_name.empty()) + if (!iter_ams->second.product_name.empty()) { - ams_name = it->second.product_name; + ams_name = iter_ams->second.product_name; } else { - size_t pos = it->second.name.find('/'); + size_t pos = iter_ams->second.name.find('/'); wxString ams_device_name = "Box-%s"; if (pos != std::string::npos) { - wxString result = it->second.name.substr(0, pos); + wxString result = iter_ams->second.name.substr(0, pos); result.MakeUpper(); if (auto str_it = ACCESSORY_DISPLAY_STR.find(result); str_it != ACCESSORY_DISPLAY_STR.end()) result = str_it->second; @@ -875,27 +970,25 @@ void MachineInfoPanel::update_ams_ext(MachineObject *obj) ams_name = ams_text; } - if (it == ver_list.end()) { + if (iter_ams == ver_list.end()) { // hide this ams ams_sn = "-"; ams_ver = "-"; } else { // update ams img - if (m_obj->upgrade_display_state == DevFirmwareUpgradingState::UpgradingInProgress) { + if (upgrade_ptr->GetUpgradeState() == DevFirmwareUpgradeState::UpgradingInProgress) { ams_ver = "-"; amspanel->m_ams_new_version_img->Hide(); } else { - if (obj->new_ver_list.empty() && !obj->m_new_ver_list_exist) { - if (obj->upgrade_new_version && - !obj->ams_new_version_number.empty() && - obj->ams_new_version_number.compare(it->second.sw_ver) != 0) { + if (new_version_list.empty()) { + if (upgrade_ptr->HasNewVersion() && !obj->ams_new_version_number.empty() && obj->ams_new_version_number.compare(iter_ams->second.sw_ver) != 0) { amspanel->m_ams_new_version_img->Show(); if (obj->ams_new_version_number.empty()) { - ams_ver = wxString::Format("%s", it->second.sw_ver); - if ((it->second.firmware_flag & 0x3) == FIRMWARE_STASUS::BETA) { + ams_ver = wxString::Format("%s", iter_ams->second.sw_ver); + if ((iter_ams->second.firmware_flag & 0x3) == FIRMWARE_STASUS::BETA) { amspanel->m_staticText_beta_version->Show(); } else { @@ -905,8 +998,8 @@ void MachineInfoPanel::update_ams_ext(MachineObject *obj) } else { //ams_ver = wxString::Format("%s->%s", it->second.sw_ver, obj->ams_new_version_number); - ams_ver = it->second.sw_ver; - if ((it->second.firmware_flag & 0x3) == FIRMWARE_STASUS::BETA) { + ams_ver = iter_ams->second.sw_ver; + if ((iter_ams->second.firmware_flag & 0x3) == FIRMWARE_STASUS::BETA) { ams_ver += wxString::Format("(%s)", _L("Beta version")); } ams_ver += wxString::Format("->%s", obj->ams_new_version_number); @@ -915,8 +1008,8 @@ void MachineInfoPanel::update_ams_ext(MachineObject *obj) } else { amspanel->m_ams_new_version_img->Hide(); - wxString ver_text = wxString::Format("%s", it->second.sw_ver, _L("Latest version")); - if ((it->second.firmware_flag & 0x3) == FIRMWARE_STASUS::BETA) + wxString ver_text = wxString::Format("%s", iter_ams->second.sw_ver, _L("Latest version")); + if ((iter_ams->second.firmware_flag & 0x3) == FIRMWARE_STASUS::BETA) { amspanel->m_staticText_beta_version->Show(); } @@ -926,19 +1019,18 @@ void MachineInfoPanel::update_ams_ext(MachineObject *obj) } ams_ver = ver_text; } - } - else if (!it->second.sw_new_ver.empty() && (it->second.sw_new_ver != it->second.sw_ver)) { + } else if (!iter_ams->second.sw_new_ver.empty() && (iter_ams->second.sw_new_ver != iter_ams->second.sw_ver)) { amspanel->m_ams_new_version_img->Show(); - ams_ver = wxString::Format("%s->%s", it->second.sw_ver, it->second.sw_new_ver); + ams_ver = wxString::Format("%s->%s", iter_ams->second.sw_ver, iter_ams->second.sw_new_ver); } else { std::string ams_idx = (boost::format("ams/%1%") % ams_id).str(); - auto ver_item = obj->new_ver_list.find(ams_idx); + auto ver_item = new_version_list.find(ams_idx); - if (ver_item == obj->new_ver_list.end()) { + if (ver_item == new_version_list.end()) { amspanel->m_ams_new_version_img->Hide(); - wxString ver_text = wxString::Format("%s(%s)", it->second.sw_ver, _L("Latest version")); - if ((it->second.firmware_flag & 0x3) == FIRMWARE_STASUS::BETA) { + wxString ver_text = wxString::Format("%s(%s)", iter_ams->second.sw_ver, _L("Latest version")); + if ((iter_ams->second.firmware_flag & 0x3) == FIRMWARE_STASUS::BETA) { amspanel->m_staticText_beta_version->Show(); } else { @@ -951,11 +1043,11 @@ void MachineInfoPanel::update_ams_ext(MachineObject *obj) amspanel->m_ams_new_version_img->Show(); //wxString ver_text = wxString::Format("%s->%s", ver_item->second.sw_ver, ver_item->second.sw_new_ver); wxString ver_text = ver_item->second.sw_ver; - if ((it->second.firmware_flag & 0x3) == FIRMWARE_STASUS::BETA) { + if ((iter_ams->second.firmware_flag & 0x3) == FIRMWARE_STASUS::BETA) { ver_text += wxString::Format("(%s)", _L("Beta version")); } ver_text += wxString::Format("->%s", ver_item->second.sw_new_ver); - if (((it->second.firmware_flag >> 2) & 0x3) == FIRMWARE_STASUS::BETA) { + if (((iter_ams->second.firmware_flag >> 2) & 0x3) == FIRMWARE_STASUS::BETA) { amspanel->m_staticText_beta_version->Show(); } else { @@ -966,7 +1058,7 @@ void MachineInfoPanel::update_ams_ext(MachineObject *obj) else { amspanel->m_ams_new_version_img->Hide(); wxString ver_text = wxString::Format("%s(%s)", ver_item->second.sw_ver, _L("Latest version")); - if ((it->second.firmware_flag & 0x3) == FIRMWARE_STASUS::BETA) { + if ((iter_ams->second.firmware_flag & 0x3) == FIRMWARE_STASUS::BETA) { amspanel->m_staticText_beta_version->Show(); } else { @@ -979,11 +1071,11 @@ void MachineInfoPanel::update_ams_ext(MachineObject *obj) } // update ams sn - if (it->second.sn.empty()) { + if (iter_ams->second.sn.empty()) { ams_sn = "-"; } else { - wxString sn_text = it->second.sn; + wxString sn_text = iter_ams->second.sn; ams_sn = sn_text.MakeUpper(); } } @@ -993,7 +1085,7 @@ void MachineInfoPanel::update_ams_ext(MachineObject *obj) amspanel->m_staticText_ams_ver_val->SetLabelText(ams_ver); ams_index++; - } + } } } else { if (!has_hub_model) { show_ams(false); } @@ -1012,8 +1104,8 @@ void MachineInfoPanel::update_ams_ext(MachineObject *obj) // has new version bool has_new_version = false; - auto new_ext_ver = obj->new_ver_list.find("ext"); - if (new_ext_ver != obj->new_ver_list.end()) + auto new_ext_ver = new_version_list.find("ext"); + if (new_ext_ver != new_version_list.end()) has_new_version = true; if (has_new_version) { @@ -1134,6 +1226,20 @@ void MachineInfoPanel::update_extinguish(MachineObject* obj) } } +void MachineInfoPanel::update_nozzle_rack(MachineObject* obj) +{ + if (obj && obj->GetNozzleSystem()) { + auto rack = obj->GetNozzleSystem()->GetNozzleRack(); + if (rack && rack->IsSupported()) { + show_nozzle_rack(true); + } + else { + show_nozzle_rack(false); + } + } +} + + void MachineInfoPanel::show_status(int status, std::string upgrade_status_str) { if (last_status == status && last_status_str == upgrade_status_str) return; @@ -1144,7 +1250,7 @@ void MachineInfoPanel::show_status(int status, std::string upgrade_status_str) Freeze(); - if (status == (int)DevFirmwareUpgradingState::UpgradingUnavaliable) { + if (status == (int)DevFirmwareUpgradeState::UpgradingUnavaliable) { m_button_upgrade_firmware->Show(); m_button_upgrade_firmware->Disable(); for (size_t i = 0; i < m_upgrading_sizer->GetItemCount(); i++) { @@ -1153,23 +1259,30 @@ void MachineInfoPanel::show_status(int status, std::string upgrade_status_str) m_upgrade_retry_img->Hide(); m_staticText_upgrading_info->Hide(); m_staticText_upgrading_percent->Hide(); - } else if (status == (int) DevFirmwareUpgradingState::UpgradingAvaliable) { + } else if (status == (int) DevFirmwareUpgradeState::UpgradingAvaliable) { m_button_upgrade_firmware->Show(); m_button_upgrade_firmware->Enable(); for (size_t i = 0; i < m_upgrading_sizer->GetItemCount(); i++) { m_upgrading_sizer->Show(false); } m_upgrade_retry_img->Hide(); m_staticText_upgrading_info->Hide(); m_staticText_upgrading_percent->Hide(); - } else if (status == (int) DevFirmwareUpgradingState::UpgradingInProgress) { + } else if (status == (int) DevFirmwareUpgradeState::UpgradingInProgress) { m_button_upgrade_firmware->Disable(); for (size_t i = 0; i < m_upgrading_sizer->GetItemCount(); i++) { m_upgrading_sizer->Show(true); } m_upgrade_retry_img->Hide(); m_staticText_upgrading_info->Show(); - m_staticText_upgrading_info->SetLabel(_L("Updating")); + if (upgrade_status_str == "HOTEND_INFO_REFRESHING") { + m_staticText_upgrading_info->SetLabel(_L("Refreshing hotend information...")); + } else if (upgrade_status_str == "VERSIONG_CHECKING") { + m_staticText_upgrading_info->SetLabel(_L("Checking version...")); + } else { + m_staticText_upgrading_info->SetLabel(_L("Updating")); + } + m_staticText_upgrading_info->SetForegroundColour(TEXT_NORMAL_CLR); m_staticText_upgrading_percent->SetForegroundColour(TEXT_NORMAL_CLR); m_staticText_upgrading_percent->Show(); - } else if (status == (int) DevFirmwareUpgradingState::UpgradingFinished) { + } else if (status == (int) DevFirmwareUpgradeState::UpgradingFinished) { if (upgrade_status_str == "UPGRADE_FAIL") { m_staticText_upgrading_info->SetLabel(_L("Updating failed")); m_staticText_upgrading_info->SetForegroundColour(TEXT_FAILED_CLR); @@ -1269,6 +1382,16 @@ void MachineInfoPanel::show_extinguish(bool show) } +void MachineInfoPanel::show_nozzle_rack(bool show) +{ + if (m_nozzle_rack_img->IsShown() != show) { + m_nozzle_rack_line_above->Show(show); + m_nozzle_rack_update_btn->Show(show); + m_nozzle_rack_img->Show(show); + m_nozzle_rack_text->Show(show); + } +} + void MachineInfoPanel::on_sys_color_changed() { if (m_obj) { @@ -1278,22 +1401,25 @@ void MachineInfoPanel::on_sys_color_changed() void MachineInfoPanel::confirm_upgrade(MachineObject* obj) { - if (obj) { - obj->command_upgrade_confirm(); - obj->upgrade_display_state = DevFirmwareUpgradingState::UpgradingInProgress; - obj->upgrade_display_hold_count = HOLD_COUNT_MAX; + auto upgrade_ptr = m_obj ? m_obj->GetUpgrade().lock() : nullptr; + if (upgrade_ptr) { + upgrade_ptr->CtrlUpgradeConfirm(); + // enter in progress status first - this->show_status((int)DevFirmwareUpgradingState::UpgradingInProgress); + this->show_status((int) DevFirmwareUpgradeState::UpgradingInProgress); } } void MachineInfoPanel::upgrade_firmware_internal() { - if (!m_obj) + auto upgrade_ptr = m_obj ? m_obj->GetUpgrade().lock() : nullptr; + if (!upgrade_ptr) { return; + } + if (panel_type == ptOtaPanel) { - m_obj->command_upgrade_firmware(m_ota_info); + upgrade_ptr->CtrlUpgradeFirmware(m_ota_info); } else if (panel_type == ptAmsPanel) { - m_obj->command_upgrade_firmware(m_ams_info); + upgrade_ptr->CtrlUpgradeFirmware(m_ams_info); } else if (panel_type == ptPushPanel) { confirm_upgrade(); } @@ -1316,8 +1442,9 @@ void MachineInfoPanel::on_consisitency_upgrade_firmware(wxCommandEvent &event) if (confirm_dlg == nullptr) { confirm_dlg = new SecondaryCheckDialog(this->GetParent(), wxID_ANY, _L("Update firmware")); confirm_dlg->Bind(EVT_SECONDARY_CHECK_CONFIRM, [this](wxCommandEvent& e) { - if (m_obj) { - m_obj->command_consistency_upgrade_confirm(); + auto upgrade_ptr = m_obj ? m_obj->GetUpgrade().lock() : nullptr; + if (upgrade_ptr) { + upgrade_ptr->CtrlUpgradeConsistencyConfirm(); } }); } @@ -1330,14 +1457,16 @@ void MachineInfoPanel::on_show_release_note(wxMouseEvent &event) DeviceManager *dev = wxGetApp().getDeviceManager(); if (!dev) return; + auto upgrade_ptr = m_obj->GetUpgrade().lock(); + if (!upgrade_ptr) return; wxString next_version_release_note; wxString now_version_release_note; std::string version_number = ""; for (auto iter : m_obj->firmware_list) { - if (iter.version == m_obj->ota_new_version_number) { - version_number = m_obj->ota_new_version_number; + if (iter.version == upgrade_ptr->GetOtaNewVersion()) { + version_number = upgrade_ptr->GetOtaNewVersion(); next_version_release_note = wxString::FromUTF8(iter.description); } if (iter.version == m_obj->get_ota_version()) { @@ -1347,7 +1476,7 @@ void MachineInfoPanel::on_show_release_note(wxMouseEvent &event) } ReleaseNoteDialog dlg; - if (!m_obj->ota_new_version_number.empty()) { + if (!upgrade_ptr->GetOtaNewVersion().empty()) { dlg.update_release_note(next_version_release_note, version_number); dlg.ShowModal(); return; @@ -1421,6 +1550,8 @@ void UpgradePanel::update(MachineObject *obj) refresh_version_and_firmware(obj); } + auto upgrade_ptr = obj ? obj->GetUpgrade().lock() : nullptr; + Freeze(); if (m_obj && m_need_update) { if (m_obj->is_firmware_info_valid()) { @@ -1433,20 +1564,21 @@ void UpgradePanel::update(MachineObject *obj) //force upgrade //unlock hint - if (m_obj && (m_obj->upgrade_display_state == DevFirmwareUpgradingState::UpgradingFinished) && (last_forced_hint_status != m_obj->upgrade_display_state)) { - last_forced_hint_status = m_obj->upgrade_display_state; + if (upgrade_ptr && (upgrade_ptr->GetUpgradeState() == DevFirmwareUpgradeState::UpgradingFinished) && (last_forced_hint_status != upgrade_ptr->GetUpgradeState())) { + last_forced_hint_status = upgrade_ptr->GetUpgradeState(); m_show_forced_hint = true; } + if (m_obj && m_show_forced_hint) { - if (m_obj->upgrade_force_upgrade) { + auto upgrade_ptr = m_obj->GetUpgrade().lock(); + if (upgrade_ptr && upgrade_ptr->IsUpgradeForceUpgrade()) { m_show_forced_hint = false; //lock hint if (force_dlg == nullptr) { force_dlg = new SecondaryCheckDialog(this->GetParent(), wxID_ANY, _L("Update firmware"), SecondaryCheckDialog::ButtonStyle::CONFIRM_AND_CANCEL, wxDefaultPosition, wxDefaultSize); force_dlg->Bind(EVT_SECONDARY_CHECK_CONFIRM, [this](wxCommandEvent& e) { - if (m_obj) { - m_obj->command_upgrade_confirm(); - m_obj->upgrade_display_state = DevFirmwareUpgradingState::UpgradingInProgress; - m_obj->upgrade_display_hold_count = HOLD_COUNT_MAX; + auto upgrade_ptr = m_obj ? m_obj->GetUpgrade().lock() : nullptr; + if (upgrade_ptr) { + upgrade_ptr->CtrlUpgradeConfirm(); } }); } @@ -1458,18 +1590,21 @@ void UpgradePanel::update(MachineObject *obj) } //consistency upgrade - if (m_obj && (m_obj->upgrade_display_state == DevFirmwareUpgradingState::UpgradingFinished) && (last_consistency_hint_status != m_obj->upgrade_display_state)) { - last_consistency_hint_status = m_obj->upgrade_display_state; + if (upgrade_ptr && (upgrade_ptr->GetUpgradeState() == DevFirmwareUpgradeState::UpgradingFinished) && (last_consistency_hint_status != upgrade_ptr->GetUpgradeState())) { + last_consistency_hint_status = upgrade_ptr->GetUpgradeState(); m_show_consistency_hint = true; } + if (m_obj && m_show_consistency_hint) { - if (m_obj->upgrade_consistency_request) { + auto upgrade_ptr = m_obj->GetUpgrade().lock(); + if (upgrade_ptr && upgrade_ptr->IsUpgradeConsistencyRequest()) { m_show_consistency_hint = false; if (consistency_dlg == nullptr) { consistency_dlg = new SecondaryCheckDialog(this->GetParent(), wxID_ANY, _L("Update firmware"), SecondaryCheckDialog::ButtonStyle::CONFIRM_AND_CANCEL, wxDefaultPosition, wxDefaultSize); consistency_dlg->Bind(EVT_SECONDARY_CHECK_CONFIRM, [this](wxCommandEvent& e) { - if (m_obj) { - m_obj->command_consistency_upgrade_confirm(); + auto upgrade_ptr = m_obj ? m_obj->GetUpgrade().lock() : nullptr; + if (upgrade_ptr) { + upgrade_ptr->CtrlUpgradeConsistencyConfirm(); } }); } @@ -1703,5 +1838,19 @@ bool UpgradePanel::Show(bool show) } + + + // removed duplicate update_nozzle_rack (defined earlier) + + void MachineInfoPanel::on_nozzle_rack_update(wxCommandEvent &event) + { + if (!m_obj || !m_obj->GetNozzleSystem()) return; + auto rack = m_obj->GetNozzleSystem()->GetNozzleRack(); + if (!rack) return; + + wgtDeviceNozzleRackUpgradeDlg dlg(this, rack); + dlg.ShowModal(); + } + } } diff --git a/src/slic3r/GUI/UpgradePanel.hpp b/src/slic3r/GUI/UpgradePanel.hpp index 2fb0f8d..b82eba3 100644 --- a/src/slic3r/GUI/UpgradePanel.hpp +++ b/src/slic3r/GUI/UpgradePanel.hpp @@ -134,6 +134,12 @@ protected: wxStaticLine* m_extinguish_line_above = nullptr;; uiDeviceUpdateVersion* m_extinguish_version = nullptr; + /* nozzle_rack*/ + wxStaticLine * m_nozzle_rack_line_above{nullptr}; + wxStaticBitmap *m_nozzle_rack_img = nullptr; + wxBoxSizer *m_nozzle_rack_sizer{nullptr}; + wxStaticText *m_nozzle_rack_text{nullptr}; + /* upgrade widgets */ wxBoxSizer* m_upgrading_sizer; wxStaticText * m_staticText_upgrading_info; @@ -142,6 +148,8 @@ protected: wxStaticBitmap *m_upgrade_retry_img; wxStaticText * m_staticText_release_note; Button * m_button_upgrade_firmware; + Button * m_nozzle_rack_update_btn; + wxPanel* create_caption_panel(wxWindow *parent); AmsPanelHash m_amspanel_list; @@ -158,6 +166,7 @@ protected: ScalableBitmap upgrade_gray_icon; ScalableBitmap upgrade_green_icon; ScalableBitmap upgrade_yellow_icon; + ScalableBitmap m_img_nozzle_rack; int last_status = -1; std::string last_status_str = ""; @@ -212,16 +221,20 @@ private: void createCuttingWidgets(wxBoxSizer* main_left_sizer); void createLaserWidgets(wxBoxSizer* main_left_sizer); void createExtinguishWidgets(wxBoxSizer* main_left_sizer); + void createNozzleRackWidgets(wxBoxSizer* main_left_sizer); void update_air_pump(MachineObject* obj); void update_cut(MachineObject* obj); void update_laszer(MachineObject* obj); void update_extinguish(MachineObject* obj); + void update_nozzle_rack(MachineObject *obj); + void on_nozzle_rack_update(wxCommandEvent &event); void show_air_pump(bool show = true); void show_cut(bool show = true); void show_laszer(bool show = true); void show_extinguish(bool show = true); + void show_nozzle_rack(bool show = true); }; //enum UpgradeMode { @@ -242,8 +255,8 @@ protected: bool enable_select_firmware = false; bool m_need_update = false; //hint of force upgrade or consistency upgrade - DevFirmwareUpgradingState last_forced_hint_status = DevFirmwareUpgradingState::DC; - DevFirmwareUpgradingState last_consistency_hint_status = DevFirmwareUpgradingState::DC; + DevFirmwareUpgradeState last_forced_hint_status = DevFirmwareUpgradeState::DC; + DevFirmwareUpgradeState last_consistency_hint_status = DevFirmwareUpgradeState::DC; int last_status; bool m_show_forced_hint = true; bool m_show_consistency_hint = true; diff --git a/src/slic3r/GUI/UserManager.cpp b/src/slic3r/GUI/UserManager.cpp index 4b14503..7262a92 100644 --- a/src/slic3r/GUI/UserManager.cpp +++ b/src/slic3r/GUI/UserManager.cpp @@ -38,35 +38,33 @@ int UserManager::parse_json(std::string payload) try { if (j_pre.contains("bind")) { - if (j_pre["bind"].contains("command")) { +#if !QDT_RELEASE_TO_PUBLIC + BOOST_LOG_TRIVIAL(info) << "user message bind = " << payload; +#endif + std::string command = j_pre["bind"].contains("command") + ? j_pre["bind"]["command"].get() : ""; + std::string dev_id = j_pre["bind"].contains("dev_id") + ? j_pre["bind"]["dev_id"].get() : ""; + std::string result = j_pre["bind"].contains("result") + ? j_pre["bind"]["result"].get() : ""; + //bind + if (command == "bind" && result == "success") { + DeviceManager* dev = GUI::wxGetApp().getDeviceManager(); + if (!dev) { return -1; } - //bind - if (j_pre["bind"]["command"].get() == "bind") { - std::string dev_id; - std:; string result; - - if (j_pre["bind"].contains("dev_id")) { - dev_id = j_pre["bind"]["dev_id"].get(); - } - - if (j_pre["bind"].contains("result")) { - result = j_pre["bind"]["result"].get(); - } - - if (result == "success") { - DeviceManager* dev = GUI::wxGetApp().getDeviceManager(); - if (!dev) {return -1;} - - if (GUI::wxGetApp().m_ping_code_binding_dialog && GUI::wxGetApp().m_ping_code_binding_dialog->IsShown()) { - GUI::wxGetApp().m_ping_code_binding_dialog->EndModal(wxCLOSE); - GUI::MessageDialog msgdialog(nullptr, _L("Log in successful."), "", wxAPPLY | wxOK); - msgdialog.ShowModal(); - } - dev->update_user_machine_list_info(); - dev->set_selected_machine(dev_id); - return 0; - } + if (GUI::wxGetApp().m_ping_code_binding_dialog && GUI::wxGetApp().m_ping_code_binding_dialog->IsShown()) { + GUI::wxGetApp().m_ping_code_binding_dialog->EndModal(wxCLOSE); + GUI::MessageDialog msgdialog(nullptr, _L("Log in successful."), "", wxAPPLY | wxOK); + msgdialog.ShowModal(); } + dev->update_user_machine_list_info(); + dev->set_selected_machine(dev_id); + return 0; + } + else if (command == "unbind" && result == "success") { + DeviceManager* dev = GUI::wxGetApp().getDeviceManager(); + if (!dev) { return -1; } + dev->erase_user_machine(dev_id); } } } diff --git a/src/slic3r/GUI/WebViewDialog.cpp b/src/slic3r/GUI/WebViewDialog.cpp index c7ccb69..c0059b0 100644 --- a/src/slic3r/GUI/WebViewDialog.cpp +++ b/src/slic3r/GUI/WebViewDialog.cpp @@ -8,6 +8,7 @@ #include "../Utils/Http.hpp" #include +#include #include #include @@ -121,7 +122,6 @@ WebViewPanel::WebViewPanel(wxWindow *parent) m_browserLeft->SetMinSize(wxSize(FromDIP(224), -1)); m_browserLeft->SetMaxSize(wxSize(FromDIP(224), -1)); - // Create the webview m_browser = WebView::CreateWebView(this, UrlRight); if (m_browser == nullptr) { @@ -134,7 +134,7 @@ WebViewPanel::WebViewPanel(wxWindow *parent) if (m_browserMW == nullptr) { wxLogError("Could not init m_browserMW"); return; - } + } m_browserMW->Hide(); SetMakerworldModelID(""); m_onlinefirst = false; @@ -168,6 +168,7 @@ WebViewPanel::WebViewPanel(wxWindow *parent) m_browserWiki->Hide(); m_WikiFirst = false; + // Position m_home_web->Add(m_browserLeft, 0, wxEXPAND | wxALL, 0); m_home_web->Add(m_browser, 1, wxEXPAND | wxALL, 0); m_home_web->Add(m_browserMW, 1, wxEXPAND | wxALL, 0); @@ -298,7 +299,7 @@ WebViewPanel::~WebViewPanel() { BOOST_LOG_TRIVIAL(trace) << __FUNCTION__ << " Start"; SetEvtHandlerEnabled(false); - + delete m_tools_menu; if (m_LoginUpdateTimer != nullptr) { @@ -309,11 +310,11 @@ WebViewPanel::~WebViewPanel() BOOST_LOG_TRIVIAL(trace) << __FUNCTION__ << " End"; } -void WebViewPanel::ResetWholePage() -{ +void WebViewPanel::ResetWholePage() +{ std::string tmp_Region = wxGetApp().app_config->get_country_code(); if (tmp_Region == m_Region) return; - + m_Region = tmp_Region; //loginstatus @@ -598,7 +599,6 @@ void WebViewPanel::SendRecentList(int images) void WebViewPanel::SendDesignStaffpick(bool on) { - //1.9.7.52 static long long StaffPickMs = 0; auto now = std::chrono::system_clock::now(); @@ -627,11 +627,11 @@ void WebViewPanel::SendDesignStaffpick(bool on) BOOST_LOG_TRIVIAL(warning) << "get_mw_user_preference failed " + body; return; } - CallAfter([this, body] { + CallAfter([this, body] { json jPrefer = json::parse(body); int nRecommendStatus = jPrefer["recommendStatus"]; - if (nRecommendStatus != 1 && nRecommendStatus != 3) + if (nRecommendStatus != 1 && nRecommendStatus != 3) { // Default : Staff Pick get_design_staffpick(0, 10, [this](std::string body) { @@ -650,7 +650,7 @@ void WebViewPanel::SendDesignStaffpick(bool on) //Show Online Menu SetLeftMenuShow("online", 1); }); - }); + }); } else { //For U Pick get_4u_staffpick(0, 10, [this](std::string body) { @@ -669,12 +669,12 @@ void WebViewPanel::SendDesignStaffpick(bool on) //Show Online Menu SetLeftMenuShow("online", 1); }); - }); + }); } }); }); - } + } else { // Default : Staff Pick @@ -762,6 +762,45 @@ void WebViewPanel::get_wiki_search_result(std::string keyword) }).perform(); } +void WebViewPanel::get_academy_list(bool is_oversea) +{ + std::string url = "https://qidi3d.com/collections/3d-printers"; + if(is_oversea) { + url = "https://qidi3d.com/collections/3d-printers"; + } + Http http = Http::get(url); + http.header("accept", "application/json") + .header("Content-Type", "application/json") + .on_complete([this](std::string body, unsigned status) { + if (status != 200) { + BOOST_LOG_TRIVIAL(error) << "get academy list failed, status: " << status; + return; + } + CallAfter([this, body = std::move(body)] { + if (!m_browserWiki) return; + try { + //y75 + //json resp = json::parse(body); + //if (!resp.contains("data")) { + // BOOST_LOG_TRIVIAL(warning) << "academy list response missing data"; + // return; + //} + json resp; + resp["command"] = "academy_list_get"; + auto payload = from_u8(resp.dump(-1, ' ', true)); + WebView::RunScript(m_browserWiki, wxString::Format("HandleStudio(%s)", payload)); + } catch (const std::exception &e) { + BOOST_LOG_TRIVIAL(error) << "parse academy list failed: " << e.what(); + } + }); + }) + .on_error([](std::string body, std::string error, unsigned status) { + BOOST_LOG_TRIVIAL(error) << "get academy list error, status: " << status + << ", err: " << error << ", body: " << body; + }) + .perform(); +} + void WebViewPanel::SendMakerlabList( ) { try { @@ -777,15 +816,15 @@ void WebViewPanel::SendMakerlabList( ) auto body2 = from_u8(body); json jLab = json::parse(body2); - if (jLab.contains("list")) - { + if (jLab.contains("list")) + { int nSize = jLab["list"].size(); - if (nSize > 0) + if (nSize > 0) { body2.insert(1, "\"command\": \"homepage_makerlab_get\", "); RunScript(wxString::Format("window.postMessage(%s)", body2)); - SetLeftMenuShow("makerlab", 1); + SetLeftMenuShow("makerlab", 1); } } }); @@ -801,9 +840,9 @@ void WebViewPanel::SendMakerlabList( ) } } -void WebViewPanel::OpenModelDetail(std::string id, NetworkAgent *agent) -{ - SwitchLeftMenu("online"); +void WebViewPanel::OpenModelDetail(std::string id, NetworkAgent *agent) +{ + SwitchLeftMenu("online"); SetMakerworldModelID(id); } @@ -882,7 +921,7 @@ void WebViewPanel::OpenOneMakerlab(std::string url) NetworkAgent *agent = GUI::wxGetApp().getAgent(); if (!agent) return; - //if (!agent->is_user_login()) { + //if (!agent->is_user_login()) { // wxGetApp().CallAfter([this] { wxGetApp().handle_web_request("{\"sequence_id\":1,\"command\":\"homepage_login_or_register\"}"); }); //} @@ -987,14 +1026,14 @@ void WebViewPanel::SaveMakerlabStl(int SequenceID, std::string Base64Buf, std::s std::string strJS = JFile.dump(-1, ' ', false, json::error_handler_t::ignore); - wxGetApp().CallAfter([this, strJS] { + wxGetApp().CallAfter([this, strJS] { if (!m_browserML) return; WebView::RunScript(m_browserML, strJS); }); } -void WebViewPanel::UpdateMakerlabStatus( ) +void WebViewPanel::UpdateMakerlabStatus( ) { if (m_browserML == nullptr) return; @@ -1012,10 +1051,10 @@ void WebViewPanel::UpdateMakerlabStatus( ) if (wxGetApp().is_user_login()) { NetworkAgent *agent = GUI::wxGetApp().getAgent(); - if (agent == nullptr) { + if (agent == nullptr) { wxString UrlDisconnect = MakeDisconnectUrl("makerlab"); - m_browserML->LoadURL(UrlDisconnect); - return; + m_browserML->LoadURL(UrlDisconnect); + return; } std::string newticket; @@ -1029,7 +1068,7 @@ void WebViewPanel::UpdateMakerlabStatus( ) else { wxString UrlDisconnect = MakeDisconnectUrl("makerlab"); m_browserML->LoadURL(UrlDisconnect); - } + } } else { @@ -1119,10 +1158,10 @@ void WebViewPanel::UpdateMakerworldLoginStatus() } -void WebViewPanel::SetMakerworldPageLoginStatus(bool login ,wxString ticket) -{ +void WebViewPanel::SetMakerworldPageLoginStatus(bool login ,wxString ticket) +{ if (m_browserMW == nullptr) return; - + wxString mw_currenturl; if (m_online_LastUrl != "") { mw_currenturl = m_online_LastUrl; @@ -1131,14 +1170,13 @@ void WebViewPanel::SetMakerworldPageLoginStatus(bool login ,wxString ticket) mw_currenturl.Replace("modelid=", ""); } - //1.9.5 //If AgreeTerms, Redirect Other Url std::regex pattern("^https://.*/(.*/){0,1}agree-terms.*"); if (std::regex_match(mw_currenturl.ToStdString(), pattern)) { std::regex ParamPattern("agreeBackUrl=([^&]+)"); std::smatch match; std::string CurUrl = mw_currenturl.ToStdString(); - if (std::regex_search(CurUrl, match, ParamPattern)) + if (std::regex_search(CurUrl, match, ParamPattern)) { //std::cout << "Param Value: " << match[1] << std::endl; mw_currenturl = wxGetApp().url_decode(std::string(match[1])); @@ -1157,23 +1195,23 @@ void WebViewPanel::SetMakerworldPageLoginStatus(bool login ,wxString ticket) wxString mw_jumpurl = ""; bool b = GetJumpUrl(login, ticket, mw_currenturl, mw_jumpurl); - if (b) { + if (b) { m_browserMW->LoadURL(mw_jumpurl); m_online_LastUrl = ""; } } -void WebViewPanel::get_user_mw_4u_config(std::function callback) { +void WebViewPanel::get_user_mw_4u_config(std::function callback) { NetworkAgent *agent = GUI::wxGetApp().getAgent(); if (agent) - int ret = agent->get_mw_user_preference(callback); + int ret = agent->get_mw_user_preference(callback); } void WebViewPanel::get_4u_staffpick(int seed, int limit, std::function callback) { NetworkAgent *agent = GUI::wxGetApp().getAgent(); - if (agent) + if (agent) int ret = agent->get_mw_user_4ulist(seed,limit,callback); } @@ -1234,12 +1272,13 @@ void WebViewPanel::ShowUserPrintTask(bool bShow, bool bForce) //refresh url // auto host = wxGetApp().get_model_http_url(wxGetApp().app_config->get_country_code()); - // wxString language_code = wxString::FromUTF8(GetStudioLanguage()).BeforeFirst('_'); + //wxString language_code = wxString::FromUTF8(GetStudioLanguage()).BeforeFirst('_'); - // wxString mw_OffUrl = (boost::format("%1%%2%/studio/print-history?from=qidistudio") % host % language_code.mb_str()).str(); - // wxString Finalurl = wxString::Format("%sapi/sign-out?to=%s", host, UrlEncode("about:blank")); + //wxString mw_OffUrl = (boost::format("%1%%2%/studio/print-history?from=qidistudio") % host % language_code.mb_str()).str(); + //wxString Finalurl = wxString::Format("%sapi/sign-out?to=%s", host, UrlEncode("about:blank")); // m_browserPH->LoadURL(Finalurl); + SetPrintHistoryTaskID(0); m_TaskInfo = ""; @@ -1251,7 +1290,6 @@ void WebViewPanel::ShowUserPrintTask(bool bShow, bool bForce) } - void WebViewPanel::update_mode() { GetSizer()->Show(size_t(0), wxGetApp().app_config->get("internal_developer_mode") == "true"); @@ -1277,18 +1315,18 @@ void WebViewPanel::OnNavigationRequest(wxWebViewEvent& evt) evt.Veto(); return; } - } + } else { wxString surl = url; if (surl.find("?") != std::string::npos) { surl = surl.substr(0, surl.find("?")).Lower(); - } + } - if (surl.EndsWith(".zip") || - surl.EndsWith(".pdf") || - surl.EndsWith(".stl") || - surl.EndsWith(".3mf") || - surl.EndsWith(".xlsx") || + if (surl.EndsWith(".zip") || + surl.EndsWith(".pdf") || + surl.EndsWith(".stl") || + surl.EndsWith(".3mf") || + surl.EndsWith(".xlsx") || surl.EndsWith(".xls") || surl.EndsWith(".txt") || surl.EndsWith("qdscfg") || @@ -1329,14 +1367,14 @@ void WebViewPanel::OnNavigationRequest(wxWebViewEvent& evt) */ void WebViewPanel::OnNavigationComplete(wxWebViewEvent& evt) { - if (m_browserMW!=nullptr && evt.GetId() == m_browserMW->GetId()) - { + if (m_browserMW!=nullptr && evt.GetId() == m_browserMW->GetId()) + { std::string TmpNowUrl = m_browserMW->GetCurrentURL().ToStdString(); std::string mwHost = wxGetApp().get_model_http_url(wxGetApp().app_config->get_country_code()); if (TmpNowUrl.find(mwHost) != std::string::npos) m_onlinefirst = true; if (m_contentname == "online") { // conf save - SetWebviewShow("right", false); + SetWebviewShow("right", false); SetWebviewShow("online", true); } @@ -1354,8 +1392,8 @@ void WebViewPanel::OnNavigationComplete(wxWebViewEvent& evt) m_online_last_url = TmpNowUrl; } - if (m_browser != nullptr && evt.GetId() == m_browser->GetId()) - { + if (m_browser != nullptr && evt.GetId() == m_browser->GetId()) + { SwitchWebContent("home"); if (wxGetApp().app_config->get("staff_pick_switch") == "true") SendDesignStaffpick(true); @@ -1383,9 +1421,9 @@ void WebViewPanel::OnDocumentLoaded(wxWebViewEvent& evt) // Only notify if the document is the main frame, not a subframe if (m_browser!=nullptr && evt.GetId() == m_browser->GetId()) { if (wxGetApp().get_mode() == comDevelop) wxLogMessage("%s", "Document loaded; url='" + evt.GetURL() + "'"); - } - else if (m_browserLeft!=nullptr && evt.GetId() == m_browserLeft->GetId()) - { + } + else if (m_browserLeft!=nullptr && evt.GetId() == m_browserLeft->GetId()) + { m_leftfirst = true; } @@ -1414,9 +1452,8 @@ void WebViewPanel::OnNewWindow(wxWebViewEvent& evt) if (wxGetApp().get_mode() == comDevelop) wxLogMessage("%s", "New window; url='" + evt.GetURL() + "'" + flag); - //1.9.7.52 //If we handle new window events then just load them in local browser - if (m_tools_handle_new_window->IsChecked()) + if (m_tools_handle_new_window->IsChecked()) { wxLaunchDefaultBrowser(evt.GetURL()); } @@ -1688,25 +1725,25 @@ void WebViewPanel::OnError(wxWebViewEvent& evt) BOOST_LOG_TRIVIAL(trace) << __FUNCTION__ << ": [" << category << "] " << evt.GetString().ToUTF8().data(); - if (wxGetApp().get_mode() == comDevelop) + if (wxGetApp().get_mode() == comDevelop) { wxLogMessage("%s", "Error; url='" + evt.GetURL() + "', error='" + category + " (" + evt.GetString() + ")'"); - // Show the info bar with an error + // Show the info bar with an error } //m_info->ShowMessage(_L("An error occurred loading ") + evt.GetURL() + "\n" + "'" + category + "'", wxICON_ERROR); - if (evt.GetInt() == wxWEBVIEW_NAV_ERR_CONNECTION && evt.GetId() == m_browserMW->GetId()) - { + if (evt.GetInt() == wxWEBVIEW_NAV_ERR_CONNECTION && evt.GetId() == m_browserMW->GetId()) + { m_online_LastUrl = m_browserMW->GetCurrentURL(); - if (m_contentname == "online") - { + if (m_contentname == "online") + { wxString errurl = evt.GetURL(); wxString UrlDisconnect = MakeDisconnectUrl("online"); m_browserMW->LoadURL(UrlDisconnect); - + SetWebviewShow("makerlab", false); SetWebviewShow("online", true); SetWebviewShow("right", false); @@ -1790,7 +1827,6 @@ void WebViewPanel::SetPrintHistoryTaskID(int TaskID) m_print_history_LastUrl = (boost::format("%1%%2%/studio/print-history?from=qidistudio") % host % language_code.mb_str()).str(); } - void WebViewPanel::SwitchWebContent(std::string modelname, int refresh) { m_contentname = modelname; @@ -1799,7 +1835,7 @@ void WebViewPanel::SwitchWebContent(std::string modelname, int refresh) wxString strlang = GetStudioLanguage(); - if (modelname.compare("makersupply") == 0) + if (modelname.compare("makersupply") == 0) { std::string strRegion = wxGetApp().app_config->get_country_code(); wxString MakerSupplyUrl; @@ -1877,7 +1913,6 @@ void WebViewPanel::SwitchWebContent(std::string modelname, int refresh) wxGetApp().app_config->save(); wxGetApp().CallAfter([this] { ShowMenuNewTag("online", "0"); }); - } else if (modelname.compare("printhistory") == 0) { if (!m_printhistoryfirst) @@ -1971,7 +2006,7 @@ void WebViewPanel::SwitchLeftMenu(std::string strMenu) void WebViewPanel::CheckMenuNewTag() { std::string sClick = wxGetApp().app_config->get("homepage", "online_clicked"); - if (sClick.compare("1")==0) + if (sClick.compare("1")==0) ShowMenuNewTag("online", "0"); else ShowMenuNewTag("online", "1"); @@ -1985,7 +2020,7 @@ void WebViewPanel::CheckMenuNewTag() { } void WebViewPanel::ShowMenuNewTag(std::string menuname, std::string show) -{ +{ if (!m_browserLeft) return; if (menuname != "online" && menuname != "makerlab") return; @@ -1998,14 +2033,14 @@ void WebViewPanel::ShowMenuNewTag(std::string menuname, std::string show) if (show.compare("1") == 0) m_Res["show"] = 1; - else + else m_Res["show"] = 0; wxString strJS = wxString::Format("HandleStudio(%s)", m_Res.dump(-1, ' ', true)); WebView::RunScript(m_browserLeft, strJS); } -void WebViewPanel::SetLeftMenuShow(std::string menuname, int show) +void WebViewPanel::SetLeftMenuShow(std::string menuname, int show) { if (!m_browserLeft) return; @@ -2026,8 +2061,8 @@ void WebViewPanel::SetLeftMenuWidth(int nWidth) { m_browserLeft->SetMaxSize(wxSize(FromDIP(nWidth), -1)); } -void WebViewPanel::SetWebviewShow(wxString name, bool show) -{ +void WebViewPanel::SetWebviewShow(wxString name, bool show) +{ wxWebView *TmpWeb = nullptr; if (name == "left") TmpWeb = m_browserLeft; @@ -2041,7 +2076,7 @@ void WebViewPanel::SetWebviewShow(wxString name, bool show) TmpWeb = m_browserML; else if (name == "wiki") TmpWeb = m_browserWiki; - + if (TmpWeb != nullptr) { if (show) @@ -2051,8 +2086,8 @@ void WebViewPanel::SetWebviewShow(wxString name, bool show) } } -std::string WebViewPanel::GetStudioLanguage() -{ +std::string WebViewPanel::GetStudioLanguage() +{ std::string strLanguage = wxGetApp().app_config->get("language"); boost::trim(strLanguage); if (strLanguage.empty()) strLanguage = "en"; diff --git a/src/slic3r/GUI/WebViewDialog.hpp b/src/slic3r/GUI/WebViewDialog.hpp index 7e23caf..5fd6977 100644 --- a/src/slic3r/GUI/WebViewDialog.hpp +++ b/src/slic3r/GUI/WebViewDialog.hpp @@ -135,6 +135,7 @@ public: void UpdateMakerworldLoginStatus(); void SetMakerworldPageLoginStatus(bool login, wxString ticket = ""); void get_wiki_search_result(std::string keyword); + void get_academy_list(bool is_oversea); //Makerlab bool m_MakerLabFirst; diff --git a/src/slic3r/GUI/Widgets/AMSControl.cpp b/src/slic3r/GUI/Widgets/AMSControl.cpp index e01bde5..895cf04 100644 --- a/src/slic3r/GUI/Widgets/AMSControl.cpp +++ b/src/slic3r/GUI/Widgets/AMSControl.cpp @@ -1482,7 +1482,7 @@ void AMSControl::SetAmsStep(std::string ams_id, std::string canid, AMSPassRoadTy break; } else if (it.second == ams_id){ - length = left ? 124 : 232; + length = left ? 110 : 232; in_pair = true; break; } diff --git a/src/slic3r/GUI/Widgets/AMSItem.cpp b/src/slic3r/GUI/Widgets/AMSItem.cpp index 4f2c63c..6bd00f3 100644 --- a/src/slic3r/GUI/Widgets/AMSItem.cpp +++ b/src/slic3r/GUI/Widgets/AMSItem.cpp @@ -1095,7 +1095,7 @@ void AMSLib::render_generic_text(wxDC &dc) show_k_value = false; } else if (m_info.cali_idx == -1 || (m_obj && (CalibUtils::get_selected_calib_idx(m_obj->pa_calib_tab, m_info.cali_idx) == -1))) { - if (m_obj && m_obj->GetConfig()->SupportCalibrationPA_FlowAuto()) + if (m_obj && m_obj->GetConfig() && m_obj->GetConfig()->SupportCalibrationPA_FlowAuto()) show_k_value = false; else get_default_k_n_value(m_info.filament_id, m_info.k, m_info.n); @@ -1104,6 +1104,8 @@ void AMSLib::render_generic_text(wxDC &dc) show_k_value = false; } + wxString tooltip_text; + auto tmp_lib_colour = m_info.material_colour; change_the_opacity(tmp_lib_colour); @@ -1199,6 +1201,7 @@ void AMSLib::render_generic_text(wxDC &dc) } dc.DrawText(m_info.material_name, pot); } + tooltip_text += m_info.material_name; } //draw k&n @@ -1210,8 +1213,13 @@ void AMSLib::render_generic_text(wxDC &dc) auto tsize = dc.GetMultiLineTextExtent(str_k); auto pot_k = wxPoint((libsize.x - tsize.x) / 2, (libsize.y - tsize.y) / 2 - FromDIP(9) + tsize.y); dc.DrawText(str_k, pot_k); + + tooltip_text += "\n" + str_k; } } + if (GetToolTipText() != tooltip_text) { + SetToolTip(tooltip_text); + } } if (m_info.material_state == AMSCanType::AMS_CAN_TYPE_EMPTY) { @@ -1401,7 +1409,10 @@ void AMSLib::render_generic_lib(wxDC &dc) auto alpha = m_info.material_colour.Alpha(); int height = size.y; int curr_height = height * float(m_info.material_remain * 1.0 / 100.0); - if (curr_height != 0) { curr_height = std::max(curr_height, FromDIP(2)); }/*STUDIO-11323 too small to show, enlarge it*/ + if (m_info.material_remain < 10)// Display Optimization for Empty Consumable Remaining Quantity in AMS + { + curr_height = height * 0.1; + } dc.SetFont(::Label::Body_13); int top = height - curr_height; @@ -1413,7 +1424,19 @@ void AMSLib::render_generic_lib(wxDC &dc) } else if (alpha != 255 && alpha != 254) { if (transparent_changed) { + std::string rgb = (tmp_lib_colour.GetAsString(wxC2S_HTML_SYNTAX)).ToStdString(); + if (rgb.size() == 9) { + //delete alpha value + rgb = rgb.substr(0, rgb.size() - 2); + } + float alpha_f = 0.7 * tmp_lib_colour.Alpha() / 255.0; + std::vector replace; + replace.push_back(rgb); + std::string fill_replace = "fill-opacity=\"" + std::to_string(alpha_f); + replace.push_back(fill_replace); + m_bitmap_transparent = ScalableBitmap(this, "transparent_ams_lib", 76, false, false, true, replace); transparent_changed = false; + } dc.DrawBitmap(m_bitmap_transparent.bmp(), FromDIP(2), FromDIP(2)); } @@ -2635,6 +2658,9 @@ void AMSPreview::doRender(wxDC &dc) wxSize size = GetSize(); dc.SetPen(wxPen(*wxTRANSPARENT_PEN)); + BOOST_LOG_TRIVIAL(info) << "AMSPreview::doRender - AMS Info: " + << "ams_id = " << m_amsinfo.ams_id << ", ams_type = " << static_cast(m_amsinfo.ams_type) << ", cans_count = " << m_amsinfo.cans.size(); + // draw background if (wxGetApp().dark_mode()) { @@ -2655,7 +2681,14 @@ void AMSPreview::doRender(wxDC &dc) //four slot if (m_ams_item_type != AMSModel::EXT_AMS && m_ams_item_type != AMSModel::N3S_AMS){ left = FromDIP(8); + int can_idx = 0; for (std::vector::iterator iter = m_amsinfo.cans.begin(); iter != m_amsinfo.cans.end(); iter++) { + BOOST_LOG_TRIVIAL(info) << "AMSPreview::doRender - Can[" << can_idx << "]: " + << "can_id = \"" << iter->can_id << "\"" + << ", material_name = \"" << iter->material_name.ToStdString() << "\"" + << ", material_colour = " << iter->material_colour.GetAsString(2).ToStdString() + << ", filament_id=\"" << iter->filament_id << "\"" + << ", material_state=" << static_cast(iter->material_state); dc.SetPen(wxPen(*wxTRANSPARENT_PEN)); @@ -2714,6 +2747,7 @@ void AMSPreview::doRender(wxDC &dc) } } left += AMS_ITEM_CUBE_SIZE.x; + can_idx++; } //auto pot = wxPoint((size.x - m_four_slot_bitmap.GetBmpSize().x) / 2, (size.y - m_four_slot_bitmap.GetBmpSize().y) / 2); @@ -2731,6 +2765,13 @@ void AMSPreview::doRender(wxDC &dc) //single slot else if (m_amsinfo.cans.size() == 1) { auto iter = m_amsinfo.cans[0]; + BOOST_LOG_TRIVIAL(info) << "AMSPreview::doRender - Single Can: " + << "can_id = \"" << iter.can_id << "\"" + << ", material_name = \"" << iter.material_name.ToStdString() << "\"" + << ", material_colour = " << iter.material_colour.GetAsString(2).ToStdString() + << ", filament_id=\"" << iter.filament_id << "\"" + << ", material_state=" << static_cast(iter.material_state); + dc.SetPen(wxPen(*wxTRANSPARENT_PEN)); dc.SetBrush(StateColor::darkModeColorFor(AMS_CONTROL_DEF_BLOCK_BK_COLOUR)); wxSize rec_size = wxSize(FromDIP(16), FromDIP(24)); @@ -2796,6 +2837,11 @@ void AMSPreview::doRender(wxDC &dc) dc.DrawRoundedRectangle(rect, FromDIP(3)); } } + + if (m_amsinfo.cans.empty()) { + BOOST_LOG_TRIVIAL(info) << "AMSPreview::doRender - No cans data available for AMS: " << m_amsinfo.ams_id; + } + auto border_colour = AMS_CONTROL_BRAND_COLOUR; if (!wxWindow::IsEnabled()) { border_colour = AMS_CONTROL_DISABLE_COLOUR; } @@ -2940,7 +2986,7 @@ void AMSHumidity::doRender(wxDC& dc) dc.SetPen(wxPen(*wxTRANSPARENT_PEN)); dc.SetBrush(wxBrush(StateColor::darkModeColorFor(AMS_CONTROL_DEF_BLOCK_BK_COLOUR))); - + // left mode int humidity_display_idx = m_amsinfo.get_humidity_display_idx(); if (1 <= humidity_display_idx && humidity_display_idx <= 5) { diff --git a/src/slic3r/GUI/Widgets/AnimaController.cpp b/src/slic3r/GUI/Widgets/AnimaController.cpp index acf78c0..b64d5a5 100644 --- a/src/slic3r/GUI/Widgets/AnimaController.cpp +++ b/src/slic3r/GUI/Widgets/AnimaController.cpp @@ -78,6 +78,11 @@ void AnimaIcon::Stop() m_timer->Stop(); } +bool AnimaIcon::IsPlaying() +{ + return m_timer->IsRunning(); +} + void AnimaIcon::Enable() { if (m_bitmap) { m_bitmap->SetBitmap(m_image_enable); } diff --git a/src/slic3r/GUI/Widgets/AnimaController.hpp b/src/slic3r/GUI/Widgets/AnimaController.hpp index e2f67ff..70c1577 100644 --- a/src/slic3r/GUI/Widgets/AnimaController.hpp +++ b/src/slic3r/GUI/Widgets/AnimaController.hpp @@ -14,7 +14,7 @@ public: void Play(); void Stop(); void Enable(); - bool IsPlaying() const { return IsRunning(); }; + bool IsPlaying(); bool IsRunning() const; private: diff --git a/src/slic3r/GUI/Widgets/Button.cpp b/src/slic3r/GUI/Widgets/Button.cpp index 7b28795..c886652 100644 --- a/src/slic3r/GUI/Widgets/Button.cpp +++ b/src/slic3r/GUI/Widgets/Button.cpp @@ -196,6 +196,9 @@ void Button::paintEvent(wxPaintEvent& evt) void Button::render(wxDC& dc) { StaticBox::render(dc); + if (m_left_corner_white || m_right_corner_white) { + renderWhiteCorners(dc); + } int states = state_handler.states(); wxSize size = GetSize(); dc.SetBrush(*wxTRANSPARENT_BRUSH); @@ -293,6 +296,63 @@ void Button::render(wxDC& dc) } } +void Button::renderWhiteCorners(wxDC& dc) +{ + wxSize size = GetSize(); + int r = static_cast(radius); + wxColor parent_bg_color = StaticBox::GetParentBackgroundColor(GetParent()); + int states = state_handler.states(); + wxColor bg_color = background_color.colorForStates(states); + + auto drawWhiteCorners = [&](wxDC &dc) { + dc.SetPen(*wxTRANSPARENT_PEN); + dc.SetBrush(wxBrush(parent_bg_color)); + + if (m_left_corner_white) { + dc.DrawRectangle(0, 0, r, r); + dc.DrawRectangle(0, size.y - r, r, r); + + dc.SetBrush(wxBrush(bg_color)); + dc.DrawRoundedRectangle(0, 0, r * 2, r * 2, r); + dc.DrawRoundedRectangle(0, size.y - r * 2, r * 2, r * 2, r); + } + + if (m_right_corner_white) { + dc.DrawRectangle(size.x - r, 0, r, r); + dc.DrawRectangle(size.x - r, size.y - r, r, r); + + dc.SetBrush(wxBrush(bg_color)); + dc.DrawRoundedRectangle(size.x - r * 2, 0, r * 2, r * 2, r); + dc.DrawRoundedRectangle(size.x - r * 2, size.y - r * 2, r * 2, r * 2, r); + } + }; +#ifdef __WXMSW__ + auto renderWithAntialiasing = [&]() -> bool { + wxMemoryDC memdc(&dc); + if (!memdc.IsOk()) return false; + + wxBitmap bmp(size.x, size.y); + memdc.SelectObject(bmp); + memdc.Blit({0, 0}, size, &dc, {0, 0}); + + wxGCDC gcdc(memdc); + if (!gcdc.IsOk()) return false; + + drawWhiteCorners(gcdc); + + memdc.SelectObject(wxNullBitmap); + dc.DrawBitmap(bmp, 0, 0); + return true; + }; + + if (!renderWithAntialiasing()) { + drawWhiteCorners(dc); + } +#else + drawWhiteCorners(dc); +#endif +} + void Button::messureSize() { wxClientDC dc(this); diff --git a/src/slic3r/GUI/Widgets/Button.hpp b/src/slic3r/GUI/Widgets/Button.hpp index 45f6b53..4a81b62 100644 --- a/src/slic3r/GUI/Widgets/Button.hpp +++ b/src/slic3r/GUI/Widgets/Button.hpp @@ -21,6 +21,10 @@ class Button : public StaticBox bool isCenter = true; bool vertical = false; + bool m_left_corner_white = false; + bool m_right_corner_white = false; + bool grayed = false; + wxTipWindow* tipWindow = nullptr; static const int buttonWidth = 200; @@ -67,6 +71,14 @@ public: void Rescale(); + void SetLeftCornerWhite(bool white = true) { m_left_corner_white = white; } + void SetRightCornerWhite(bool white = true) { m_right_corner_white = white; } + + bool IsGrayed() { return grayed; } + void SetGrayed(bool gray) { grayed = gray; } + + wxRect GetTextRect() const { return textSize; } + protected: #ifdef __WIN32__ WXLRESULT MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) override; @@ -78,6 +90,7 @@ private: void paintEvent(wxPaintEvent& evt); void render(wxDC& dc); + void renderWhiteCorners(wxDC &dc); void messureSize(); diff --git a/src/slic3r/GUI/Widgets/FanControl.cpp b/src/slic3r/GUI/Widgets/FanControl.cpp index bc876c3..4272973 100644 --- a/src/slic3r/GUI/Widgets/FanControl.cpp +++ b/src/slic3r/GUI/Widgets/FanControl.cpp @@ -781,7 +781,7 @@ void FanControlPopupNew::UpdatePartSubMode() if (AIR_DUCT(m_data.curren_mode) == AIR_DUCT::AIR_DUCT_COOLING_FILT && m_data.IsSupportCoolingFilter()) { if (!m_cooling_filter_switch_panel) { m_cooling_filter_switch_panel = new FanControlNewSwitchPanel(m_sub_mode_panel, _L("Filter"), _L("Enabling filtration redirects the right fan to filter gas, which may reduce cooling performance.")); - m_cooling_filter_switch_panel->Bind(EVT_FANCTRL_SWITCH, [this] (wxCommandEvent& evt) + m_cooling_filter_switch_panel->Bind(EVT_FANCTRL_SWITCH, [this] (wxCommandEvent& evt) { if (m_obj && m_obj->is_in_printing()) { MessageDialog msg_wingow(nullptr, _L("Enabling filtration during printing may reduce cooling and affect print qulity. Please choose carefully"), "", wxICON_WARNING | wxCANCEL | wxOK); @@ -858,7 +858,6 @@ void FanControlPopupNew::update_fan_data(AIR_FUN id, int speed) { for (auto& part : m_data.parts) { auto part_id = part.id; - if (id == part_id) { part.state = speed; auto it = m_fan_control_list.find(part_id); @@ -1070,8 +1069,7 @@ FanControlNewSwitchPanel::FanControlNewSwitchPanel(wxWindow* parent, const wxStr wxSizer* m_label_sizer = new wxBoxSizer(wxHORIZONTAL); m_label_sizer->Add(label, 0, wxALIGN_LEFT, 0); - m_label_sizer->AddSpacer(FromDIP(10)); - m_label_sizer->Add(0, 1, wxEXPAND, FromDIP(10)); + m_label_sizer->AddStretchSpacer(1); m_label_sizer->Add(m_switch_btn, 0, wxALIGN_RIGHT, 0); Label* tips_label = new Label(this); @@ -1080,14 +1078,14 @@ FanControlNewSwitchPanel::FanControlNewSwitchPanel(wxWindow* parent, const wxStr tips_label->Wrap(FromDIP(400)); wxSizer* m_sizer_main = new wxBoxSizer(wxVERTICAL); - m_sizer_main->Add(m_label_sizer, 0, wxALL | wxALIGN_LEFT, FromDIP(5)); + m_sizer_main->Add(m_label_sizer, 0, wxALL | wxEXPAND, FromDIP(5)); m_sizer_main->Add(tips_label, 0, wxALL | wxALIGN_LEFT, FromDIP(5)); SetSizer(m_sizer_main); SetBackgroundColour(wxColour(248, 248, 248)); Layout(); - wxGetApp().UpdateDarkUIWin(this); + wxGetApp().UpdateDarkUIWin(this); } void FanControlNewSwitchPanel::SetSwitchOn(bool on) diff --git a/src/slic3r/GUI/Widgets/LinkLabel.hpp b/src/slic3r/GUI/Widgets/LinkLabel.hpp index 9531046..7f7e492 100644 --- a/src/slic3r/GUI/Widgets/LinkLabel.hpp +++ b/src/slic3r/GUI/Widgets/LinkLabel.hpp @@ -1,6 +1,8 @@ #ifndef slic3r_GUI_LinkLabel_hpp_ #define slic3r_GUI_LinkLabel_hpp_ +#include + #include "Label.hpp" wxDECLARE_EVENT(EVT_LINK_LABEL_LEFT_DOWN, wxCommandEvent); @@ -14,7 +16,7 @@ private: public: LinkLabel(wxWindow *parent, wxString const &text, std::string url, long style = 0, wxSize size = wxDefaultSize); - ~LinkLabel(){}; + ~LinkLabel() {}; void link(wxMouseEvent &evt); Label *getLabel(){return m_txt;}; diff --git a/src/slic3r/GUI/Widgets/MultiNozzleSync.cpp b/src/slic3r/GUI/Widgets/MultiNozzleSync.cpp new file mode 100644 index 0000000..22cc305 --- /dev/null +++ b/src/slic3r/GUI/Widgets/MultiNozzleSync.cpp @@ -0,0 +1,1171 @@ +#include "MultiNozzleSync.hpp" +#include "../GUI_App.hpp" +#include "../DeviceCore/DevManager.h" +#include "libslic3r/PresetBundle.hpp" +#include + + +namespace Slic3r::GUI{ + +wxDEFINE_EVENT(EVT_NOZZLE_SELECTED, wxCommandEvent); + +static const int LeftExtruderIdx = 0; +static const int RightExtruderIdx = 1; + +ManualNozzleCountDialog::ManualNozzleCountDialog(wxWindow *parent, NozzleVolumeType volume_type, int standard_count, int highflow_count, int max_nozzle_count, bool force_no_zero) + : GUI::DPIDialog(parent, wxID_ANY, "Set nozzle count", wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX), m_volume_type(volume_type) +{ + this->SetBackgroundColour(*wxWHITE); + std::string icon_path = (boost::format("%1%/images/QIDIStudioTitle.ico") % resources_dir()).str(); + SetIcon(wxIcon(encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO)); + + wxPanel *content = new wxPanel(this); + content->SetBackgroundColour(*wxWHITE); + + wxBitmap icon = create_scaled_bitmap("hotend_thumbnail", nullptr, FromDIP(60)); + auto *nozzle_icon = new wxStaticBitmap(content, wxID_ANY, icon); + + wxBoxSizer *content_sizer = new wxBoxSizer(wxHORIZONTAL); + content->SetSizer(content_sizer); + + wxBoxSizer *choice_sizer = new wxBoxSizer(wxVERTICAL); + + auto* label = new wxStaticText(content, wxID_ANY, _L("Please set nozzle count")); + choice_sizer->Add(label, 0, wxALL | wxALIGN_LEFT, FromDIP(10)); + + wxArrayString nozzle_choices; + for (int i = 0; i <= max_nozzle_count; ++i) nozzle_choices.Add(wxString::Format("%d", i)); + + bool show_standard = (volume_type == nvtStandard || volume_type == nvtHybrid); + bool show_highflow = (volume_type == nvtHighFlow || volume_type == nvtHybrid); + + if (show_standard) { + // Standard nozzle choice + auto *standard_label = new wxStaticText(content, wxID_ANY, _L(get_nozzle_volume_type_string(nvtStandard))); + choice_sizer->Add(standard_label, 0, wxALL | wxALIGN_LEFT, FromDIP(5)); + + m_standard_choice = new wxChoice(content, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(100), -1), nozzle_choices); + m_standard_choice->SetSelection(standard_count); + m_standard_choice->SetBackgroundColour(*wxWHITE); + choice_sizer->Add(m_standard_choice, 0, wxLEFT | wxBOTTOM | wxRIGHT, FromDIP(10)); + } + + if (show_highflow) { + // Highflow nozzle choice + auto *highflow_label = new wxStaticText(content, wxID_ANY, _L(get_nozzle_volume_type_string(nvtHighFlow))); + choice_sizer->Add(highflow_label, 0, wxALL | wxALIGN_LEFT, FromDIP(5)); + + m_highflow_choice = new wxChoice(content, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(100), -1), nozzle_choices); + m_highflow_choice->SetSelection(highflow_count); + m_highflow_choice->SetBackgroundColour(*wxWHITE); + choice_sizer->Add(m_highflow_choice, 0, wxLEFT | wxBOTTOM | wxRIGHT, FromDIP(10)); + } + + m_error_label = new Label(this, ""); + m_error_label->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#E14747"))); + m_error_label->SetFont(Label::Body_12); + m_error_label->Hide(); + + auto update_nozzle_error = [this, force_no_zero, content, max_nozzle_count](int standard_count, int highflow_count) { + int total_count = standard_count + highflow_count; + if (0 < total_count && total_count <= max_nozzle_count&& m_error_label->IsShown()) { + m_error_label->Hide(); + m_confirm_btn->Enable(); + content->Freeze(); + this->Layout(); + this->Fit(); + content->Thaw(); + } else if (total_count == 0 && force_no_zero || total_count > max_nozzle_count) { + wxString error_tip; + if (total_count == 0) + error_tip = _L("Error: Can not set both nozzle count to zero."); + else + error_tip = wxString::Format(_L("Error: Nozzle count can not exceed %d."), max_nozzle_count); + + m_error_label->SetLabel(error_tip); + m_error_label->Wrap(content->GetSize().x); + m_error_label->Show(); + m_confirm_btn->Disable(); + content->Freeze(); + this->Layout(); + this->Fit(); + content->Thaw(); + } + }; + + if (m_standard_choice) { + m_standard_choice->Bind(wxEVT_CHOICE, [this, update_nozzle_error](wxCommandEvent &e) { + int standard_count = m_standard_choice->GetSelection(); + int highflow_count = m_highflow_choice ? m_highflow_choice->GetSelection() : 0; + update_nozzle_error(standard_count, highflow_count); + e.Skip(); + }); + } + + if (m_highflow_choice) { + m_highflow_choice->Bind(wxEVT_CHOICE, [this, update_nozzle_error](wxCommandEvent &e) { + int standard_count = m_standard_choice ? m_standard_choice->GetSelection() : 0; + int highflow_count = m_highflow_choice->GetSelection(); + update_nozzle_error(standard_count, highflow_count); + e.Skip(); + }); + } + + content_sizer->Add(nozzle_icon, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(15)); + content_sizer->Add(choice_sizer, 0, wxALIGN_CENTRE_VERTICAL); + + StateColor btn_bg_green(std::pair(wxColour(144, 144, 144), StateColor::Disabled), std::pair(wxColour(27, 136, 68), StateColor::Pressed), + std::pair(wxColour(61, 203, 115), StateColor::Hovered), std::pair(wxColour(0, 174, 66), StateColor::Normal)); + + m_confirm_btn = new Button(this, _L("Confirm")); + m_confirm_btn->SetBackgroundColor(btn_bg_green); + m_confirm_btn->SetTextColor(StateColor::darkModeColorFor("#FFFFFE")); + m_confirm_btn->SetMinSize(wxSize(FromDIP(68), FromDIP(23))); + m_confirm_btn->SetMinSize(wxSize(FromDIP(68), FromDIP(23))); + m_confirm_btn->SetCornerRadius(12); + m_confirm_btn->Bind(wxEVT_LEFT_DOWN, [this](auto &e) { EndModal(wxID_OK); }); + + wxBoxSizer *mainSizer = new wxBoxSizer(wxVERTICAL); + mainSizer->Add(content, 1, wxEXPAND); + mainSizer->Add(m_error_label, 0, wxALL, FromDIP(5)); + mainSizer->Add(m_confirm_btn, 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, FromDIP(20)); + + SetSizerAndFit(mainSizer); + CentreOnParent(); + wxGetApp().UpdateDlgDarkUI(this); +} + +int ManualNozzleCountDialog::GetNozzleCount(NozzleVolumeType volume_type) const +{ + if(volume_type == nvtStandard) + return m_standard_choice ? m_standard_choice->GetSelection() : 0; + else if(volume_type == nvtHighFlow) + return m_highflow_choice ? m_highflow_choice->GetSelection() : 0; + else if(volume_type == nvtHybrid) + return (m_standard_choice ? m_standard_choice->GetSelection() : 0) + (m_highflow_choice ? m_highflow_choice->GetSelection() : 0); + return 0; +} + +void manuallySetNozzleCount(int extruder_id) +{ + auto& preset_bundle = GUI::wxGetApp().preset_bundle; + if (!preset_bundle) + return; + + auto full_config = GUI::wxGetApp().preset_bundle->full_config(); + auto extruder_max_nozzle_count = full_config.option("extruder_max_nozzle_count"); + if (!extruder_max_nozzle_count || !std::any_of(extruder_max_nozzle_count->values.begin(), extruder_max_nozzle_count->values.end(), [](int val) {return val > 1; })) + return; + + ConfigOptionEnumsGeneric* nozzle_volume_type_opt = full_config.option("nozzle_volume_type"); + NozzleVolumeType volume_type = NozzleVolumeType(nozzle_volume_type_opt->values[extruder_id]); + double nozzle_diameter = full_config.option("nozzle_diameter")->values[extruder_id]; + int standard_count = preset_bundle->extruder_nozzle_stat.get_extruder_nozzle_count(extruder_id, nvtStandard); + int highflow_count = preset_bundle->extruder_nozzle_stat.get_extruder_nozzle_count(extruder_id, nvtHighFlow); + + + bool force_no_zero = volume_type == nvtHybrid; + if(nozzle_volume_type_opt->values.size() > 1){ + int other_nozzle_count = preset_bundle->extruder_nozzle_stat.get_extruder_nozzle_count(1 - extruder_id); + force_no_zero |= (other_nozzle_count == 0); + } + + ManualNozzleCountDialog dialog(GUI::wxGetApp().plater(), volume_type, standard_count, highflow_count, extruder_max_nozzle_count->values[extruder_id], force_no_zero); + + if (dialog.ShowModal() == wxID_OK) { + int nozzle_count = dialog.GetNozzleCount(volume_type); + if (volume_type == nvtHybrid) { + setExtruderNozzleCount(preset_bundle, extruder_id, nvtStandard, dialog.GetNozzleCount(nvtStandard), true); + setExtruderNozzleCount(preset_bundle, extruder_id, nvtHighFlow, dialog.GetNozzleCount(nvtHighFlow), false); + } + else { + setExtruderNozzleCount(preset_bundle, extruder_id, volume_type, nozzle_count, true); + } + updateNozzleCountDisplay(preset_bundle, extruder_id, volume_type); + wxGetApp().plater()->update(); + } +} + +ExtruderBadge::ExtruderBadge(wxWindow* parent) : wxPanel(parent) +{ + wxSizer* main_sizer = new wxBoxSizer(wxVERTICAL); + SetBackgroundColour("#F8F8F8"); + wxBitmap icon = create_scaled_bitmap("extruder_badge_none_selected", nullptr, FromDIP(90)); + + auto extruder_label = new Label(this, _L("Extruder")); + + badget = new wxStaticBitmap(this, wxID_ANY, icon); + + m_diameter_list = { "0.4","0.4" }; + m_volume_type_list = { NozzleVolumeType::nvtStandard,NozzleVolumeType::nvtStandard }; + + left_diameter_desp = new Label(this, _L(m_diameter_list[LeftExtruderIdx])); + right_diameter_desp = new Label(this, _L(m_diameter_list[RightExtruderIdx])); + left_flow_desp = new Label(this, _L(get_nozzle_volume_type_string(m_volume_type_list[LeftExtruderIdx]))); + right_flow_desp = new Label(this, _L(get_nozzle_volume_type_string(m_volume_type_list[RightExtruderIdx]))); + + + wxBoxSizer* top_h = new wxBoxSizer(wxVERTICAL); + top_h->Add(extruder_label, 0, wxALIGN_CENTER | wxBOTTOM, FromDIP(10)); + + main_sizer->Add(top_h, 0, wxEXPAND | wxTOP, FromDIP(5)); + main_sizer->Add(badget, 0, wxALIGN_CENTER | wxTOP, FromDIP(5)); + + wxBoxSizer* left_extruder = new wxBoxSizer(wxVERTICAL); + + left_extruder->Add(left_diameter_desp, 0, wxALIGN_CENTER | wxLEFT, FromDIP(12)); + left_extruder->Add(left_flow_desp, 0, wxALIGN_CENTER | wxLEFT, FromDIP(12)); + + wxBoxSizer* right_extruder = new wxBoxSizer(wxVERTICAL); + right_extruder->Add(right_diameter_desp, 0, wxALIGN_CENTER | wxRIGHT, FromDIP(12)); + right_extruder->Add(right_flow_desp, 0, wxALIGN_CENTER | wxRIGHT, FromDIP(12)); + + wxBoxSizer* info_sizer = new wxBoxSizer(wxHORIZONTAL); + info_sizer->Add(left_extruder, 0); + info_sizer->AddStretchSpacer(); + info_sizer->Add(right_extruder, 0); + + main_sizer->Add(info_sizer, 0, wxEXPAND | wxTOP, FromDIP(5)); + + SetSizer(main_sizer); + Layout(); + Fit(); + wxGetApp().UpdateDarkUIWin(this); +} + +void ExtruderBadge::SetExtruderInfo(int extruder_id, const std::string& diameter, const NozzleVolumeType& volume_type) +{ + m_diameter_list[extruder_id] = diameter; + m_volume_type_list[extruder_id] = volume_type; + + if (extruder_id == LeftExtruderIdx) { + left_diameter_desp->SetLabel(diameter + " mm"); + left_flow_desp->SetLabel(_L(get_nozzle_volume_type_string(volume_type))); + } + else if (extruder_id == RightExtruderIdx) { + right_diameter_desp->SetLabel(diameter + " mm"); + right_flow_desp->SetLabel(_L(get_nozzle_volume_type_string(volume_type))); + } +} + +void ExtruderBadge::SetExtruderValid(bool right_on) +{ + if (!right_on) { + right_diameter_desp->SetLabel(""); + right_flow_desp->SetLabel(""); + } + std::string badge_name; + if (m_right_on) + badge_name = "extruder_badge_none_selected"; + else + badge_name = "extruder_badge_none_selected_single"; + wxBitmap icon = create_scaled_bitmap(badge_name, nullptr, FromDIP(90)); + badget->SetBitmap(icon); + + m_right_on = right_on; +} + +void ExtruderBadge::SetExtruderStatus(bool left_selected, bool right_selected) +{ + std::string badge_name; + if (m_right_on) { + if (left_selected && right_selected) + badge_name = "extruder_badge_both_selected"; + else if (left_selected) + badge_name = "extruder_badge_left_selected"; + else if (right_selected) + badge_name = "extruder_badge_right_selected"; + else + badge_name = "extruder_badge_none_selected"; + } + else if (left_selected) { + badge_name = "extruder_badge_left_selected_single"; + } + else { + badge_name = "extruder_badge_none_selected_single"; + } + + wxBitmap icon = create_scaled_bitmap(badge_name, nullptr, FromDIP(90)); + badget->SetBitmap(icon); + Layout(); +} + + +void ExtruderBadge::UnMarkRelatedItems(const NozzleOption& option) +{ + bool left_selected = true, right_selected = true; + + if (m_diameter_list[LeftExtruderIdx] == option.diameter && option.extruder_nozzle_stats.count(LeftExtruderIdx) + && option.extruder_nozzle_stats.at(LeftExtruderIdx).count(m_volume_type_list[LeftExtruderIdx]) + && option.extruder_nozzle_stats.at(LeftExtruderIdx).at(m_volume_type_list[LeftExtruderIdx])>0) + left_selected = false; + + if (m_diameter_list[RightExtruderIdx] == option.diameter && option.extruder_nozzle_stats.count(RightExtruderIdx) + && option.extruder_nozzle_stats.at(RightExtruderIdx).count(m_volume_type_list[RightExtruderIdx]) + && option.extruder_nozzle_stats.at(RightExtruderIdx).at(m_volume_type_list[RightExtruderIdx])>0) + right_selected = false; + + SetExtruderStatus(left_selected, right_selected); +} + +void ExtruderBadge::MarkRelatedItems(const NozzleOption& option) +{ + bool left_selected = false, right_selected = false; + + if (m_diameter_list[LeftExtruderIdx] == option.diameter && option.extruder_nozzle_stats.count(LeftExtruderIdx) + && option.extruder_nozzle_stats.at(LeftExtruderIdx).count(m_volume_type_list[LeftExtruderIdx]) + && option.extruder_nozzle_stats.at(LeftExtruderIdx).at(m_volume_type_list[LeftExtruderIdx]) > 0) + left_selected = true; + + if (m_diameter_list[RightExtruderIdx] == option.diameter && option.extruder_nozzle_stats.count(RightExtruderIdx) + && option.extruder_nozzle_stats.at(RightExtruderIdx).count(m_volume_type_list[RightExtruderIdx]) + && option.extruder_nozzle_stats.at(RightExtruderIdx).at(m_volume_type_list[RightExtruderIdx]) > 0) + right_selected = true; + + SetExtruderStatus(left_selected, right_selected); +} + +HotEndTable::HotEndTable(wxWindow* parent) : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize,wxBORDER_NONE) +{ + Bind(wxEVT_PAINT, &HotEndTable::OnPaint, this); + auto main_sizer = new wxBoxSizer(wxVERTICAL); + auto label = new Label(this, _L("Induction Hotend Rack")); + label->SetBackgroundColour("#F8F8F8"); + + m_arow_nozzle_box = CreateNozzleBox({ 0,2,4 }); + m_brow_nozzle_box = CreateNozzleBox({ 1,3,5 }); + main_sizer->Add(label, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP | wxBOTTOM, FromDIP(5)); + main_sizer->Add(m_arow_nozzle_box, 0, wxLEFT | wxRIGHT, FromDIP(5)); + main_sizer->Add(m_brow_nozzle_box, 0, wxLEFT | wxRIGHT, FromDIP(5)); + SetBackgroundColour("#F8F8F8"); + + SetSizer(main_sizer); + Layout(); + Fit(); + wxGetApp().UpdateDarkUIWin(this); +} + +void HotEndTable::UpdateRackInfo(std::weak_ptr rack) +{ + m_nozzle_rack = rack; + const auto& nozzle_rack = rack.lock(); + if (nozzle_rack) { + UpdateNozzleItems(m_nozzle_items, nozzle_rack); + } +} + +std::vector HotEndTable::FilterHotEnds(const NozzleOption& option) +{ + auto rack = m_nozzle_rack.lock(); + if (!rack) + return {}; + + std::vector nozzles_to_search; + + for (auto& item : option.extruder_nozzle_stats) { + for (auto& nozzle : item.second) { + HotEndAttr info; + info.diameter = option.diameter; + info.extruder_id = item.first; + info.volume_type = nozzle.first; + nozzles_to_search.emplace_back(info); + } + } + + std::vector filtered_nozzles; + + for (auto& info : nozzles_to_search) { + + float diameter = atof(info.diameter.c_str()); + NozzleFlowType flow = info.volume_type == nvtStandard ? NozzleFlowType::S_FLOW : + info.volume_type == nvtHighFlow ? NozzleFlowType::H_FLOW : NozzleFlowType::NONE_FLOWTYPE; + int extruder_id = 1 - info.extruder_id; //physical + + auto nozzles = rack->GetNozzleSystem()->CollectNozzles(extruder_id, flow, diameter); + + for (auto& nozzle : nozzles) { + if (nozzle.IsOnRack()) + filtered_nozzles.emplace_back(nozzle.GetNozzleId()); + } + } + + return filtered_nozzles; +} + +void HotEndTable::MarkRelatedItems(const NozzleOption& option) +{ + const static StateColor bg_green( + std::pair(wxColour("#DBFDE7"), StateColor::Normal) + ); + + const static StateColor bd_green( + std::pair(wxColour("#4479fb"), StateColor::Normal) + ); + auto filtered_nozzles = FilterHotEnds(option); + for (auto nozzle_id : filtered_nozzles) { + auto iter = m_nozzle_items.find(nozzle_id); + if (iter == m_nozzle_items.end()) + continue; + auto& item = iter->second; + item->SetBackgroundColor(bg_green); + item->SetBorderColor(bd_green); + for (auto child : item->GetChildren()) { + child->SetBackgroundColour("#DBFDE7"); + } + } + wxGetApp().UpdateDarkUIWin(this); +} + +void HotEndTable::UnMarkRelatedItems(const NozzleOption& option) +{ + static const wxColour bg_color("#EEEEEE"); + static const wxColour bd_color("#CECECE"); + const static StateColor bg_green( + std::pair(bg_color, StateColor::Normal) + ); + + const static StateColor bd_green( + std::pair(bd_color, StateColor::Normal) + ); + auto filtered_nozzles = FilterHotEnds(option); + for (auto nozzle_id : filtered_nozzles) { + auto iter = m_nozzle_items.find(nozzle_id); + if (iter == m_nozzle_items.end()) + continue; + auto& item = iter->second; + item->SetBackgroundColor(bg_green); + item->SetBorderColor(bd_green); + for (auto child : item->GetChildren()) { + child->SetBackgroundColour(bg_color); + } + } + wxGetApp().UpdateDarkUIWin(this); +} + + + +StaticBox* HotEndTable::CreateNozzleBox(const std::vector& nozzle_indices) +{ + StaticBox* nozzle_box = new StaticBox(this); + nozzle_box->SetBackgroundColour("#F8F8F8"); + nozzle_box->SetBorderColorNormal("#F8F8F8"); + nozzle_box->SetCornerRadius(0); + + wxSizer* h_sizer = new wxBoxSizer(wxHORIZONTAL); + for (auto idx : nozzle_indices) { + wgtDeviceNozzleRackNozzleItem* nozzle_item = new wgtDeviceNozzleRackNozzleItem(nozzle_box, idx); + nozzle_item->SetBackgroundColorNormal("#EEEEEE"); + for (auto& child : nozzle_item->GetChildren()) + child->SetBackgroundColour("#EEEEEE"); + m_nozzle_items[idx] = nozzle_item; + h_sizer->Add(nozzle_item, 0, wxALL, FromDIP(8)); + } + + nozzle_box->SetSizer(h_sizer); + + return nozzle_box; +} + +void HotEndTable::UpdateNozzleItems(const std::unordered_map& nozzle_items, std::shared_ptr nozzle_rack) +{ + for (auto& item : nozzle_items) + item.second->Update(nozzle_rack); +} + +void HotEndTable::OnPaint(wxPaintEvent& evt) +{ + wxPaintDC dc(this); + wxSize size = GetClientSize(); + + dc.SetPen(wxPen(wxColour("#EEEEEE"), 2)); + dc.SetBrush(*wxTRANSPARENT_BRUSH); + dc.DrawRoundedRectangle(0, 0, size.GetWidth()-2, size.GetHeight()-2, 5); +} + +NozzleListTable::NozzleListTable(wxWindow* parent) : wxPanel(parent,wxID_ANY,wxDefaultPosition,wxDefaultSize ,wxNO_BORDER) +{ + m_web_view = wxWebView::New(this, wxID_ANY, wxEmptyString, wxDefaultPosition,wxDefaultSize,wxString::FromAscii(wxWebViewBackendDefault),wxNO_BORDER); + m_web_view->AddScriptMessageHandler("nozzleListTable"); + m_web_view->EnableContextMenu(false); + fs::path filepath = fs::path(resources_dir()) / "web/flush/NozzleListTable.html"; + wxFileName fn(wxString::FromUTF8(filepath.string())); + wxString url = wxFileSystem::FileNameToURL(fn); + m_web_view->LoadURL(url); + + auto sizer = new wxBoxSizer(wxVERTICAL); + sizer->AddStretchSpacer(0); + sizer->Add(m_web_view, 1, wxEXPAND); + sizer->AddStretchSpacer(0); + SetSizer(sizer); + Layout(); + + m_web_view->Bind(wxEVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED, [this,sizer](wxWebViewEvent& evt) { + std::string message = evt.GetString().ToStdString(); + BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << "Received message: " << message; + try { + json j = json::parse(message); + if (j["msg"].get() == "init") { + auto table_obj_str = BuildTableObjStr(); + auto text_obj_str = BuildTextObjStr(); + CallAfter([table_obj_str, text_obj_str, this] { + wxString script1 = wxString::Format("initText(%s)", text_obj_str); + m_web_view->RunScript(script1); + wxString script2 = wxString::Format("initTable(%s)", table_obj_str); + m_web_view->RunScript(script2); + }); + } + else if (j["msg"].get() == "updateList") { + auto table_obj_str = BuildTableObjStr(); + + CallAfter([table_obj_str, this] { + wxString script1 = wxString::Format("updateTable(%s)", table_obj_str); + m_web_view->RunScript(script1); + }); + + } + else if (j["msg"].get() == "onSelect") { + int idx = j["index"].get(); + m_selected_idx = idx; + SendSelectionChangedEvent(); + } + else if (j["msg"].get() == "layout") { + int height = j["height"].get(); + int width = j["width"].get(); + wxSize table_size = wxSize(-1, FromDIP(height)); + m_web_view->SetSize(table_size); + m_web_view->SetMaxSize(table_size); + m_web_view->SetMinSize(table_size); + this->Layout(); + this->GetParent()->Layout(); + this->GetParent()->Fit(); + } + } + catch (...) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "Failed to parse json message: " << message; + } + }); + + wxGetApp().UpdateDarkUIWin(this); +} + +wxString NozzleListTable::BuildTableObjStr() +{ + json obj; + obj["darkmode"] = wxGetApp().dark_mode(); + obj["data"] = json::array(); + for(size_t idx = 0; idx < m_nozzle_options.size(); ++idx){ + const auto& option = m_nozzle_options[idx]; + json json_opt; + json_opt["diameter"] = option.diameter; + json_opt["is_selected"] = idx == m_selected_idx; + json_opt["darkmode"] = wxGetApp().dark_mode(); + + json extruders = json::object(); + for (const auto& [extruderId, nozzleInfo] : option.extruder_nozzle_stats) { + nlohmann::json nozzleData; + nozzleData["type"] = ""; + nozzleData["count"] = std::accumulate(nozzleInfo.begin(), nozzleInfo.end(), 0, [](int val, auto elem) {return val + elem.second; }); + extruders[std::to_string(extruderId)] = nozzleData; + } + json_opt["extruders"] = extruders; + obj["data"].push_back(json_opt); + } + return wxString::FromUTF8(obj.dump().c_str()); +} + +void NozzleListTable::SendSelectionChangedEvent() +{ + wxCommandEvent event(EVT_NOZZLE_SELECTED, GetId()); + event.SetInt(m_selected_idx); + event.SetEventObject(this); + ProcessWindowEvent(event); +} +wxString NozzleListTable::BuildTextObjStr() +{ + wxString nozzle_selection = _L("Nozzle Selection"); + wxString nozzle_list = _L("Available Nozzles"); + wxString Left = _L("Left"); + wxString Right = _L("Right"); + wxString highflow = _L(get_nozzle_volume_type_string(nvtHighFlow)); + wxString standard = _L(get_nozzle_volume_type_string(nvtStandard)); + + wxString text_obj = "{"; + text_obj += wxString::Format("\"nozzle_selection_label\":\"%s\",", nozzle_selection); + text_obj += wxString::Format("\"nozzle_list_label\":\"%s\",", nozzle_list); + text_obj += wxString::Format("\"left_label\":\"%s\",", Left); + text_obj += wxString::Format("\"right_label\":\"%s\",", Right); + text_obj += wxString::Format("\"highflow_label\":\"%s\",", highflow); + text_obj += wxString::Format("\"standard_label\":\"%s\",", standard); + text_obj += wxString::Format("\"language\":\"%s\"", wxGetApp().app_config->get_language_code()); + text_obj += "}"; + + return text_obj; +} + +void NozzleListTable::SetOptions(const std::vector& options,int default_select) +{ + m_nozzle_options = options; + m_selected_idx = default_select; + auto table_obj_str = BuildTableObjStr(); + wxString script1 = wxString::Format("updateTable(%s)", table_obj_str); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "update table " << script1; + +#if 1 + m_web_view->RunScript(script1); +#else + CallAfter([script1, this]() { + m_web_view->RunScript(script1); + }); +#endif +} + +MultiNozzleStatusTable::MultiNozzleStatusTable(wxWindow* parent): wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE) +{ + m_badge = new ExtruderBadge(this); + + SetBackgroundColour(wxColour("#F8F8F8")); + + auto main_sizer = new wxBoxSizer(wxVERTICAL); + + auto title_panel = new wxPanel(this); + title_panel->SetBackgroundColour(0xEEEEEE); + auto title_sizer = new wxBoxSizer(wxHORIZONTAL); + title_panel->SetSizer(title_sizer); + + Label* static_text = new Label(this, _L("Nozzle Info")); + static_text->SetFont(Label::Head_13); + static_text->SetBackgroundColour(0xEEEEEE); + + title_sizer->Add(static_text, 0, wxALIGN_CENTER | wxALL, FromDIP(5)); + + main_sizer->Add(title_panel, 0, wxEXPAND); + main_sizer->AddSpacer(10); + + wxSizer* nozzle_area_sizer = new wxBoxSizer(wxHORIZONTAL); + + m_table = new HotEndTable(this); + + nozzle_area_sizer->Add(m_badge, 0, wxLEFT | wxRIGHT, FromDIP(20)); + nozzle_area_sizer->Add(m_table, 0, wxRIGHT, FromDIP(10)); + + main_sizer->Add(nozzle_area_sizer); + main_sizer->AddSpacer(FromDIP(5)); + + SetSizer(main_sizer); + Layout(); + Fit(); + wxGetApp().UpdateDarkUIWin(this); +} + +void MultiNozzleStatusTable::MarkRelatedItems(const NozzleOption& option) +{ + m_table->MarkRelatedItems(option); + + m_badge->MarkRelatedItems(option); +} + +void MultiNozzleStatusTable::UnMarkRelatedItems(const NozzleOption& option) +{ + m_table->UnMarkRelatedItems(option); + + m_badge->UnMarkRelatedItems(option); +} + +void MultiNozzleStatusTable::UpdateRackInfo(std::weak_ptr rack) +{ + if (m_table) + m_table->UpdateRackInfo(rack); + if (m_badge) { + auto nozzle_rack = rack.lock(); + if (!nozzle_rack) + return; + auto nozzles_in_extruder = nozzle_rack->GetNozzleSystem()->GetExtNozzles(); + bool has_right = false; + for (auto& elem : nozzles_in_extruder) { + auto& nozzle = elem.second; + int extruder_id = nozzle.AtLeftExtruder() ? 0 : 1; + if (nozzle.AtRightExtruder()) + has_right = true; + NozzleVolumeType volume_type; + if (nozzle.m_nozzle_flow == NozzleFlowType::H_FLOW) + volume_type = nvtHighFlow; + else + volume_type = nvtStandard; + + m_badge->SetExtruderInfo(extruder_id, format_diameter_to_str(nozzle.GetNozzleDiameter()), volume_type); + } + m_badge->SetExtruderValid(has_right); + } +} + + +Slic3r::GUI::MultiNozzleSyncDialog::MultiNozzleSyncDialog(wxWindow* parent,std::weak_ptr rack) : DPIDialog(parent, wxID_ANY, "Sync Nozzle status",wxDefaultPosition, wxDefaultSize,wxDEFAULT_DIALOG_STYLE) +{ + m_nozzle_rack = rack; + wxSizer* main_sizer = new wxBoxSizer(wxVERTICAL); + SetBackgroundColour(*wxWHITE); + + m_tips = new Label(this, ""); + wxBoxSizer* label_sizer = new wxBoxSizer(wxHORIZONTAL); + label_sizer->Add(m_tips, 0, wxLEFT | wxRIGHT, FromDIP(25)); + main_sizer->Add(label_sizer, 0, wxTOP | wxBOTTOM, FromDIP(15)); + + m_list_table = new NozzleListTable(this); + + m_list_table->Bind(EVT_NOZZLE_SELECTED, [this](wxCommandEvent& evt) { + int idx = evt.GetInt(); + this->OnSelectRadio(idx); + }); + + main_sizer->Add(m_list_table, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(25)); + + m_nozzle_table = new MultiNozzleStatusTable(this); + wxBoxSizer* table_sizer = new wxBoxSizer(wxHORIZONTAL); + table_sizer->Add(m_nozzle_table, 0, wxLEFT | wxRIGHT, FromDIP(25)); + main_sizer->Add(table_sizer, 0, wxTOP | wxBOTTOM, FromDIP(15)); + + wxBoxSizer* button_sizer = new wxBoxSizer(wxHORIZONTAL); + + m_cancel_btn = new Button(this, _L("Cancel"), "", 0, 0, wxID_OK); + m_confirm_btn = new Button(this, _L("Confirm"), "", 0, 0, wxID_CANCEL); + + m_caution = new Label(this, _L("Caution: Mixing nozzle diameters in one print is not supported. If the selected size is only on one extruder, single-extruder printing will be enforced.")); + m_caution->SetForegroundColour("#909090"); + main_sizer->Add(m_caution, 0, wxLEFT | wxRIGHT, FromDIP(25)); + + StateColor btn_bg_green( + std::pair(wxColour(144, 144, 144), StateColor::Disabled), + std::pair(wxColour(27, 136, 68), StateColor::Pressed), + std::pair(wxColour(61, 203, 115), StateColor::Hovered), + std::pair(wxColour(0, 174, 66), StateColor::Normal) + ); + + StateColor btn_text_green( + std::pair(wxColour(255, 255, 254), StateColor::Normal) + ); + + + m_confirm_btn->SetBackgroundColor(btn_bg_green); + m_confirm_btn->SetMinSize(wxSize(FromDIP(55), FromDIP(24))); + m_confirm_btn->SetCornerRadius(FromDIP(12)); + m_confirm_btn->SetBackgroundColor(btn_bg_green); + m_confirm_btn->SetTextColor(btn_text_green); + m_confirm_btn->SetFocus(); + + m_cancel_btn->SetMinSize(wxSize(FromDIP(55), FromDIP(24))); + m_cancel_btn->SetCornerRadius(FromDIP(12)); + + button_sizer->AddStretchSpacer(); + button_sizer->Add(m_cancel_btn, 0, wxALL, FromDIP(10)); + button_sizer->Add(m_confirm_btn, 0, wxALL, FromDIP(10)); + + main_sizer->Add(button_sizer, 0, wxEXPAND | wxALL, FromDIP(10)); + + SetSizer(main_sizer); + + //main_sizer->SetSizeHints(this); + main_sizer->Fit(this); + + int table_width = m_nozzle_table->GetBestSize().GetWidth(); + m_tips->Wrap(table_width); + m_tips->SetMaxSize(wxSize(table_width, -1)); + + m_caution->Wrap(table_width); + m_caution->SetMaxSize(wxSize(table_width, -1)); + + Layout(); + + m_refresh_timer = new wxTimer(this); + Bind(wxEVT_TIMER, &MultiNozzleSyncDialog::OnRefreshTimer, this); + CenterOnParent(); + wxGetApp().UpdateDlgDarkUI(this); +} + + +void MultiNozzleSyncDialog::OnRackStatusReadingFinished(wxEvent& evt) { + if (!IsShown()) return; + + m_refreshing = false; + if (m_refresh_timer) + m_refresh_timer->Stop(); +#if 1 + if (!UpdateUi(m_nozzle_rack)) + EndModal(wxID_OK); +#else + if(!UpdateUoi(m_nozzle_rack)){ + CallAfter([this]() { + EndModal(wxID_OK); + }); + } +#endif +} + +void MultiNozzleSyncDialog::OnRefreshTimer(wxTimerEvent& evt){ + if(!m_refreshing) + return; + auto nozzle_rack = m_nozzle_rack.lock(); + if(!nozzle_rack) + return; + + int reading_count = nozzle_rack->GetReadingCount(); + int reading_idx = nozzle_rack->GetReadingIdx(); + + wxString tip = wxString::Format("Refresh %d/%d...", reading_idx, reading_count); + m_confirm_btn->SetLabel(tip); + m_confirm_btn->Fit(); + if (m_confirm_btn->GetParent()) + m_confirm_btn->GetParent()->Layout(); + + m_confirm_btn->Disable(); + m_cancel_btn->Disable(); +} + +void MultiNozzleSyncDialog::UpdateRackInfo(std::weak_ptr rack) +{ + m_nozzle_rack = rack; + UpdateUi(rack); +} + +void MultiNozzleSyncDialog::OnSelectRadio(int select_idx) +{ + if (m_nozzle_option_idx != -1) + m_nozzle_table->UnMarkRelatedItems(m_nozzle_option_values[m_nozzle_option_idx]); + m_nozzle_option_idx = select_idx; + m_nozzle_table->MarkRelatedItems(m_nozzle_option_values[m_nozzle_option_idx]); +} + +bool MultiNozzleSyncDialog::hasMultiDiameters(const std::vector& group_infos) +{ + if (group_infos.empty()) + return false; + return !std::all_of(group_infos.begin(), group_infos.end(), [val = group_infos.front()](auto& elem) {return val.diameter == elem.diameter; }); +} + + +std::vector MultiNozzleSyncDialog::GetNozzleOptions(const std::vector& nozzle_groups) +{ + std::vector options; + + std::set diameters; + std::multimap groups_mapped_for_diameter; + for (auto& nozzle_group : nozzle_groups) { + groups_mapped_for_diameter.insert({ nozzle_group.diameter,nozzle_group }); + } + +#if ENABLE_MIX_FLOW_PRINT + for (auto it = groups_mapped_for_diameter.begin(); it != groups_mapped_for_diameter.end(); ) { + NozzleOption option; + const auto& diameter = it->first; + auto range = groups_mapped_for_diameter.equal_range(diameter); + + option.diameter = diameter; + for (auto val_it = range.first; val_it != range.second; ++val_it) { + const auto& elem = val_it->second; + option.extruder_nozzle_stats[elem.extruder_id][elem.volume_type] += elem.nozzle_count; + } + options.emplace_back(std::move(option)); + it = range.second; + } +#else + for (auto it = groups_mapped_for_diameter.begin(); it != groups_mapped_for_diameter.end(); ) { + const auto& diameter = it->first; + auto range = groups_mapped_for_diameter.equal_range(diameter); + + std::vector> left_nozzles; + std::vector> right_nozzles; + + for (auto val_it = range.first; val_it != range.second; ++val_it) { + const auto& elem = val_it->second; + if (elem.extruder_id == 0) + left_nozzles.emplace_back(elem.volume_type, elem.nozzle_count); + else + right_nozzles.emplace_back(elem.volume_type, elem.nozzle_count); + } + + for (const auto& left_nozzle : left_nozzles.empty() ? std::vector>{{}} : left_nozzles) { + for (const auto& right_nozzle : right_nozzles.empty() ? std::vector>{{}} : right_nozzles) { + NozzleOption option; + option.diameter = diameter; + + if (!left_nozzles.empty()) { + option.extruder_nozzle_stats[0] = { left_nozzle.first, left_nozzle.second }; + } + + if (!right_nozzles.empty()) { + option.extruder_nozzle_stats[1] = { right_nozzle.first, right_nozzle.second }; + } + + options.emplace_back(std::move(option)); + } + } + + it = range.second; + } +#endif + return options; +} + +bool MultiNozzleSyncDialog::UpdateOptionList(std::weak_ptr rack, bool ignore_unknown, bool ignore_unreliable) +{ + const auto& nozzle_rack = rack.lock(); + if (!nozzle_rack) + return true; + bool has_unknown = nozzle_rack->HasUnknownNozzles() && !ignore_unknown; + bool has_unreliable = nozzle_rack->HasUnreliableNozzles() && !ignore_unreliable; + + if (has_unknown || has_unreliable) { + m_list_table->Hide(); + return true; + } + + m_list_table->Show(); + + m_nozzle_option_values.clear(); + + auto options = GetNozzleOptions(nozzle_rack->GetNozzleSystem()->GetNozzleGroups()); + + int recommend_idx = std::max_element(options.begin(), options.end(), [](const NozzleOption& opt1, const NozzleOption& opt2) { + int count1 = 0, count2 = 0; + for (auto elem : opt1.extruder_nozzle_stats) { + for (auto& stats : elem.second) + count1 += stats.second; + } + for (auto elem : opt2.extruder_nozzle_stats) { + for (auto& stats : elem.second) + count2 += stats.second; + } + return count1 < count2; + }) - options.begin(); + + m_nozzle_option_values = options; + OnSelectRadio(recommend_idx); + m_list_table->SetOptions(options,recommend_idx); + + if (!has_unknown && !has_unreliable && options.size() == 1) { + return false; + } + return true; +} + +void MultiNozzleSyncDialog::UpdateTip(std::weak_ptr rack, bool ignore_unknown, bool ignore_unreliable) +{ + const auto& nozzle_rack = rack.lock(); + if (!nozzle_rack) + return; + + bool has_unknown = nozzle_rack->HasUnknownNozzles() && !ignore_unknown; + bool has_unreliable = nozzle_rack->HasUnreliableNozzles() && !ignore_unreliable; + + if (has_unknown && has_unreliable) { + m_tips->SetLabel(_L("Unknown nozzle detected. Refresh to update info (unrefreshed nozzles will be excluded during slicing). Verify nozzle diameter & flow rate against displayed values.")); + m_caution->Hide(); + } + else if (has_unknown) { + m_tips->SetLabel(_L("Unknown nozzle detected. Refresh to update (unrefreshed nozzles will be skipped in slicing).")); + m_caution->Hide(); + } + else if (has_unreliable) { + m_tips->SetLabel(_L("Please confirm whether the required nozzle diameter and flow rate match the currently displayed values.")); + m_caution->Hide(); + } + else { + m_tips->SetLabel(_L("Your printer has different nozzles installed. Please select a nozzle for this print.")); + m_caution->Show(); + } +} + +int MultiNozzleSyncDialog::ShowModal() +{ + bool res = UpdateUi(m_nozzle_rack); + if (!res) + return wxID_OK; + + return DPIDialog::ShowModal(); + +} + +MultiNozzleSyncDialog::~MultiNozzleSyncDialog() +{ + if (auto rack = m_nozzle_rack.lock()) { + rack->Unbind(DEV_RACK_EVENT_READING_FINISHED, &MultiNozzleSyncDialog::OnRackStatusReadingFinished, this); + } + if (m_refresh_timer) + m_refresh_timer->Stop(); +} + +void MultiNozzleSyncDialog::UpdateButton(std::weak_ptr rack, bool ignore_unknown, bool ignore_unreliable) +{ + const auto& nozzle_rack = rack.lock(); + if (!nozzle_rack) + return; + + if (m_refreshing) + return; + + bool has_unknown = nozzle_rack->HasUnknownNozzles() && !ignore_unknown; + bool has_unreliable = nozzle_rack->HasUnreliableNozzles() && !ignore_unreliable; + + auto refresh_cmd = [rack, this]() { + auto nozzle_rack = m_nozzle_rack.lock(); + if (!nozzle_rack) + return; + nozzle_rack->CtrlRackReadAll(); + m_refreshing = true; + if (m_refresh_timer) + m_refresh_timer->Start(500); + nozzle_rack->Bind(DEV_RACK_EVENT_READING_FINISHED, &MultiNozzleSyncDialog::OnRackStatusReadingFinished, this); + }; + + auto trust_cmd = [rack, this]() { + auto nozzle_rack = rack.lock(); + if (!nozzle_rack) + return; + nozzle_rack->CtrlRackConfirmAll(); + UpdateUi(rack, true, false); + }; + + auto ignore_opt = [rack,this]() { + if (!UpdateUi(rack, true, true)) + EndModal(wxID_OK); + }; + + m_cancel_btn->Enable(); + m_confirm_btn->Enable(); + + if (has_unknown && has_unreliable) { + m_cancel_btn->Show(); + m_confirm_btn->Show(); + + m_cancel_btn->SetLabel(_L("Ignore")); + m_confirm_btn->SetLabel(_L("Refresh")); + + m_cancel_btn->Bind(wxEVT_LEFT_DOWN, [this, rack, ignore_opt](auto& e) {ignore_opt(); }); + m_confirm_btn->Bind(wxEVT_LEFT_DOWN, [this, rack, refresh_cmd](auto& e) {refresh_cmd(); }); + } + else if (has_unknown) { + m_cancel_btn->Show(); + m_confirm_btn->Show(); + + m_cancel_btn->SetLabel(_L("Ignore")); + m_confirm_btn->SetLabel(_L("Refresh")); + + m_cancel_btn->Bind(wxEVT_LEFT_DOWN, [this, rack, ignore_opt](auto& e) {ignore_opt(); }); + m_confirm_btn->Bind(wxEVT_LEFT_DOWN, [this, rack, refresh_cmd](auto& e) {refresh_cmd(); }); + } + else if (has_unreliable) { + m_cancel_btn->Show(); + m_confirm_btn->Show(); + + m_cancel_btn->SetLabel(_L("Refresh")); + m_confirm_btn->SetLabel(_L("Confirm")); + + m_cancel_btn->Bind(wxEVT_LEFT_DOWN, [this, rack, refresh_cmd](auto& e) {refresh_cmd(); }); + m_confirm_btn->Bind(wxEVT_LEFT_DOWN, [this, rack, trust_cmd](auto& e) {trust_cmd(); }); + + } + else { + m_cancel_btn->Hide(); + m_confirm_btn->Show(); + m_confirm_btn->SetLabel(_L("OK")); + m_confirm_btn->Bind(wxEVT_LEFT_DOWN, [this](auto& e) {EndModal(wxID_OK); }); + } + +} + +bool MultiNozzleSyncDialog::UpdateUi(std::weak_ptr rack, bool ignore_unknown, bool ignore_unreliable) +{ + m_nozzle_table->UpdateRackInfo(rack); + bool res = UpdateOptionList(rack, ignore_unknown, ignore_unreliable); + if (!res) + return false; + + UpdateTip(rack, ignore_unknown, ignore_unreliable); + UpdateButton(rack, ignore_unknown, ignore_unreliable); + + Layout(); + + int table_width = m_nozzle_table->GetBestSize().GetWidth(); + m_tips->Wrap(table_width); + m_tips->SetMaxSize(wxSize(table_width, -1)); + m_caution->Wrap(table_width); + m_caution->SetMaxSize(wxSize(table_width, -1)); + Fit(); + return true; +} + + +std::optional tryPopUpMultiNozzleDialog(MachineObject* obj) +{ + if (!obj) + return std::nullopt; + auto rack = obj->GetNozzleSystem()->GetNozzleRack(); + if (!rack || !rack->IsSupported()) + return std::nullopt; + MultiNozzleSyncDialog dialog(wxGetApp().plater_,rack); + + bool has_unreliable = rack->HasUnreliableNozzles(); + bool has_unknown = rack->HasUnknownNozzles(); + + auto config = wxGetApp().preset_bundle->printers.get_edited_preset().config; + + if (dialog.ShowModal() == wxID_OK) { + auto selected_option = dialog.GetSelectedOption(); + if (!selected_option) + return std::nullopt; + auto& preset_bundle = GUI::wxGetApp().preset_bundle; + auto& project_config = preset_bundle->project_config; + + ConfigOptionEnumsGeneric* nozzle_volume_type_opt = project_config.option("nozzle_volume_type"); + + // write to preset bundle config + for (int extruder_id = 0; extruder_id < 2; ++extruder_id) { + NozzleVolumeType volume_type; + int nozzle_count; + bool clear_all = true; + if (!selected_option->extruder_nozzle_stats.count(extruder_id)) { + nozzle_count = 0; + for(size_t idx = 0 ; idx < nvtHybrid; ++idx){ + volume_type = static_cast(idx); + setExtruderNozzleCount(preset_bundle, extruder_id, volume_type, nozzle_count, clear_all); + clear_all = false; + } + } + else { + for (auto& stat : selected_option->extruder_nozzle_stats[extruder_id]) { + volume_type = stat.first; + nozzle_count = stat.second; + setExtruderNozzleCount(preset_bundle, extruder_id, volume_type, nozzle_count, clear_all); + clear_all = false; + } + } + } + preset_bundle->extruder_nozzle_stat.set_nozzle_data_flag(ExtruderNozzleStat::ndfMachine); + return selected_option; + } + + return std::nullopt; +} + +void setExtruderNozzleCount(PresetBundle* preset_bundle, int extruder_id, NozzleVolumeType volume_type, int nozzle_count, bool clear_all) +{ + if (!preset_bundle) + return; + if (volume_type == NozzleVolumeType::nvtHybrid) + return; + + preset_bundle->extruder_nozzle_stat.set_extruder_nozzle_count(extruder_id, volume_type, nozzle_count, clear_all); +} + +void updateNozzleCountDisplay(PresetBundle* preset_bundle, int extruder_id, NozzleVolumeType volume_type) +{ + int nozzle_count = preset_bundle->extruder_nozzle_stat.get_extruder_nozzle_count(extruder_id, volume_type); + + auto nozzle_count_opt = preset_bundle->printers.get_edited_preset().config.option("extruder_max_nozzle_count"); + bool support_multi_nozzle = std::any_of(nozzle_count_opt->values.begin(), nozzle_count_opt->values.end(), [](int val) {return val > 1; }); + + int display_count = support_multi_nozzle ? nozzle_count : -1; + wxGetApp().plater()->sidebar().set_extruder_nozzle_count(extruder_id, display_count); + +} + +} \ No newline at end of file diff --git a/src/slic3r/GUI/Widgets/MultiNozzleSync.hpp b/src/slic3r/GUI/Widgets/MultiNozzleSync.hpp new file mode 100644 index 0000000..9f883d1 --- /dev/null +++ b/src/slic3r/GUI/Widgets/MultiNozzleSync.hpp @@ -0,0 +1,195 @@ +#ifndef MULTI_NOZZLE_SYNC_HPP +#define MULTI_NOZZLE_SYNC_HPP + +#include "../wxExtensions.hpp" +#include "../MsgDialog.hpp" +#include "libslic3r/PrintConfig.hpp" +#include "libslic3r/MultiNozzleUtils.hpp" +#include "slic3r/GUI/DeviceCore/DevNozzleRack.h" +#include "slic3r/GUI/DeviceTab/wgtDeviceNozzleRack.h" +#include "slic3r/GUI/Widgets/RadioBox.hpp" +#include + +#define ENABLE_MIX_FLOW_PRINT 1 + +namespace Slic3r { + class PresetBundle; +} + +namespace Slic3r::GUI { + +#if ENABLE_MIX_FLOW_PRINT + struct NozzleOption + { + std::string diameter; + std::unordered_map> extruder_nozzle_stats; + }; +#else + struct NozzleOption + { + std::string diameter; + std::unordered_map> extruder_nozzle_stats; + }; +#endif + +class ManualNozzleCountDialog : public DPIDialog +{ +public: + ManualNozzleCountDialog(wxWindow *parent, NozzleVolumeType volume_type, int standard_count, int highflow_count,int max_nozzle_count, bool force_no_zero); + ~ManualNozzleCountDialog() {}; + virtual void on_dpi_changed(const wxRect& suggested_rect) {}; + int GetNozzleCount(NozzleVolumeType volume_type) const; +private: + wxChoice* m_standard_choice { nullptr }; + wxChoice* m_highflow_choice { nullptr }; + Button* m_confirm_btn{ nullptr }; + Label* m_error_label{ nullptr }; + NozzleVolumeType m_volume_type; +}; + + +class ExtruderBadge :public wxPanel +{ +public: + ExtruderBadge(wxWindow* parent); + void SetExtruderInfo(int extruder_id, const string& label, const NozzleVolumeType& flow); + void UnMarkRelatedItems(const NozzleOption& option); + void MarkRelatedItems(const NozzleOption& option); + void SetExtruderValid(bool right_on); +private: + void SetExtruderStatus(bool left_selected, bool right_selected); + + bool m_right_on{ true }; + wxStaticBitmap* badget; + Label* left; + Label* right; + Label* left_diameter_desp; + Label* right_diameter_desp; + Label* left_flow_desp; + Label* right_flow_desp; + + std::vector m_diameter_list; + std::vector m_volume_type_list; +}; + +class HotEndTable :public wxPanel +{ +public: + HotEndTable(wxWindow* parent); + void UpdateRackInfo(std::weak_ptr rack); + void MarkRelatedItems(const NozzleOption& option); + void UnMarkRelatedItems(const NozzleOption& option); +private: + StaticBox* CreateNozzleBox(const std::vector& nozzle_indices); + void UpdateNozzleItems(const std::unordered_map& nozzle_items, + std::shared_ptr nozzle_rack); + +private: + struct HotEndAttr { + std::string diameter; + int extruder_id; + NozzleVolumeType volume_type; + }; + + std::vector FilterHotEnds(const NozzleOption& option); + +private: + StaticBox* m_arow_nozzle_box{ nullptr }; + StaticBox* m_brow_nozzle_box{ nullptr }; + std::unordered_map m_nozzle_items; + std::weak_ptr m_nozzle_rack; + void OnPaint(wxPaintEvent& event); +}; + + +wxDECLARE_EVENT(EVT_NOZZLE_SELECTED, wxCommandEvent); + +class NozzleListTable : public wxPanel +{ +public: + NozzleListTable(wxWindow* parent); + int GetSelectIdx(); + void SetOptions(const std::vector& options,int default_select); +private: + wxString BuildTableObjStr(); + wxString BuildTextObjStr(); + std::vector m_nozzle_options; + + void SendSelectionChangedEvent(); + + wxWebView* m_web_view; + + int m_selected_idx; +}; + +class MultiNozzleStatusTable : public wxPanel +{ +public: + MultiNozzleStatusTable(wxWindow* parent); + void UpdateRackInfo(std::weak_ptr rack); + void MarkRelatedItems(const NozzleOption& option); + void UnMarkRelatedItems(const NozzleOption& option); +private: + ExtruderBadge* m_badge; + HotEndTable* m_table; +}; + + +class MultiNozzleSyncDialog : public DPIDialog +{ +public: + MultiNozzleSyncDialog(wxWindow* parent, std::weak_ptr rack); + virtual void on_dpi_changed(const wxRect& suggested_rect) {}; + std::vector GetNozzleOptions(const std::vector& group_infos); + + std::optional GetSelectedOption() { + if (m_nozzle_option_idx < 0 || m_nozzle_option_idx >= m_nozzle_option_values.size()) + return std::nullopt; + return m_nozzle_option_values[m_nozzle_option_idx]; + } + + int ShowModal() override; + ~MultiNozzleSyncDialog() override; +private: + void UpdateRackInfo(std::weak_ptr rack); + + bool hasMultiDiameters(const std::vector& group_infos); + void OnSelectRadio(int select_idx); + + bool UpdateUi(std::weak_ptr rack, bool ignore_unknown=false, bool ignore_unreliable=false); + + bool UpdateOptionList(std::weak_ptr rack, bool ignore_unknown, bool ignore_unreliable); + void UpdateTip(std::weak_ptr rack, bool ignore_unknown, bool ignore_unreliable); + void UpdateButton(std::weak_ptr rack, bool ignore_unknown, bool ignore_unreliable); + void OnRackStatusReadingFinished(wxEvent& evt); + void OnRefreshTimer(wxTimerEvent& event); + +private: + MultiNozzleStatusTable* m_nozzle_table; + NozzleListTable* m_list_table; + std::vector m_nozzle_option_values; + int m_nozzle_option_idx{ -1 }; + bool m_refreshing{ false }; + + std::weak_ptr m_nozzle_rack; + Label* m_tips; + Label* m_caution; + + wxTimer* m_refresh_timer {nullptr}; + size_t m_rack_event_token; + Button* m_cancel_btn; + Button* m_confirm_btn; +}; + + +std::optional tryPopUpMultiNozzleDialog(MachineObject* obj); + +void setExtruderNozzleCount(PresetBundle* preset_bundle, int extruder_id, NozzleVolumeType type, int nozzle_count, bool clear_before_set); + +void updateNozzleCountDisplay(PresetBundle* preset_bundle, int extruder_id, NozzleVolumeType volume_type); + +void manuallySetNozzleCount(int extruder_id); + +} // namespace Slic3r + +#endif \ No newline at end of file diff --git a/src/slic3r/GUI/Widgets/StateColor.cpp b/src/slic3r/GUI/Widgets/StateColor.cpp index a38e05c..67fcdeb 100644 --- a/src/slic3r/GUI/Widgets/StateColor.cpp +++ b/src/slic3r/GUI/Widgets/StateColor.cpp @@ -12,6 +12,7 @@ static std::map gDarkColors{ {"#D01B1B", "#BB2A3A"}, {"#262E30", "#EFEFF0"}, {"#2C2C2E", "#B3B3B4"}, + {"#E5E7EB", "#374151"},/*gray200 -> gray800*/ {"#6B6B6B", "#818183"}, {"#ACACAC", "#54545A"}, {"#EEEEEE", "#4C4C55"}, @@ -190,3 +191,11 @@ bool StateColor::setColorForStates(wxColour const &color, int states) } void StateColor::setTakeFocusedAsHovered(bool set) { takeFocusedAsHovered_ = set; } + +StateColor StateColor::createButtonStyleGray() +{ + return StateColor(std::pair(wxColour(206, 206, 206), StateColor::Pressed), + std::pair(*wxWHITE, StateColor::Focused), + std::pair(wxColour(238, 238, 238), StateColor::Hovered), + std::pair(*wxWHITE, StateColor::Normal)); +} diff --git a/src/slic3r/GUI/Widgets/StateColor.hpp b/src/slic3r/GUI/Widgets/StateColor.hpp index d6557da..9db723a 100644 --- a/src/slic3r/GUI/Widgets/StateColor.hpp +++ b/src/slic3r/GUI/Widgets/StateColor.hpp @@ -5,6 +5,12 @@ #include +#define WXCOLOUR_GREY700 wxColour(107, 107, 107) +#define WXCOLOUR_GREY500 wxColour(158, 158, 158) +#define WXCOLOUR_GREY400 wxColour("#CECECE") +#define WXCOLOUR_GREY300 wxColour(238, 238, 238) +#define WXCOLOUR_GREY200 wxColour(248, 248, 248) + class StateColor { public: @@ -28,6 +34,9 @@ public: static std::map const & GetDarkMap(); static wxColour darkModeColorFor(wxColour const &color); static wxColour lightModeColorFor(wxColour const &color); + + // Button style + static StateColor createButtonStyleGray(); public: template diff --git a/src/slic3r/GUI/Widgets/StaticGroup.cpp b/src/slic3r/GUI/Widgets/StaticGroup.cpp index c53298a..c9128f2 100644 --- a/src/slic3r/GUI/Widgets/StaticGroup.cpp +++ b/src/slic3r/GUI/Widgets/StaticGroup.cpp @@ -1,7 +1,12 @@ #include "StaticGroup.hpp" +#include "Label.hpp" -StaticGroup::StaticGroup(wxWindow *parent, wxWindowID id, const wxString &label) - : wxStaticBox(parent, id, label) +StaticGroup::StaticGroup(wxWindow *parent, wxWindowID id) +#ifdef __WXOSX__ + : wxStaticBox(parent, id, ".") +#else + : wxStaticBox(parent, id, "") +#endif { SetBackgroundColour(*wxWHITE); SetForegroundColour("#CECECE"); @@ -11,8 +16,15 @@ StaticGroup::StaticGroup(wxWindow *parent, wxWindowID id, const wxString &label) #endif } +bool StaticGroup::Show(bool show) +{ + bool ret = wxStaticBox::Show(show); + return ret; +} + void StaticGroup_layoutBadge(void * group, void * badge); + void StaticGroup::ShowBadge(bool show) { #ifdef __WXMSW__ diff --git a/src/slic3r/GUI/Widgets/StaticGroup.hpp b/src/slic3r/GUI/Widgets/StaticGroup.hpp index df7e576..cb92342 100644 --- a/src/slic3r/GUI/Widgets/StaticGroup.hpp +++ b/src/slic3r/GUI/Widgets/StaticGroup.hpp @@ -8,11 +8,12 @@ class StaticGroup : public wxStaticBox { public: - StaticGroup(wxWindow *parent, wxWindowID id, const wxString &label); + StaticGroup(wxWindow *parent, wxWindowID id); public: void ShowBadge(bool show); void SetBorderColor(const wxColour &color); + bool Show(bool show=true) override; private: #ifdef __WXMSW__ void OnPaint(wxPaintEvent &evt); diff --git a/src/slic3r/GUI/Widgets/SwitchButton.cpp b/src/slic3r/GUI/Widgets/SwitchButton.cpp index 7760254..d65dcea 100644 --- a/src/slic3r/GUI/Widgets/SwitchButton.cpp +++ b/src/slic3r/GUI/Widgets/SwitchButton.cpp @@ -15,6 +15,7 @@ #include wxDEFINE_EVENT(wxCUSTOMEVT_SWITCH_POS, wxCommandEvent); +wxDEFINE_EVENT(wxCUSTOMEVT_MULTISWITCH_SELECTION, wxCommandEvent); wxDEFINE_EVENT(wxEXPAND_LEFT_DOWN, wxCommandEvent); SwitchButton::SwitchButton(wxWindow* parent, wxWindowID id) @@ -735,6 +736,225 @@ void ExpandButtonHolder::doRender(wxDC& dc) } } +MultiSwitchButton::MultiSwitchButton(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, long style) + : StaticBox(parent, id, pos, size, style) + , sel(-1) + , m_bg_color(StateColor( + std::make_pair(0xE8E8E8, (int) StateColor::NotChecked), + std::make_pair(0x4479fb, (int) StateColor::Normal))) + , m_bg_color_grayed(StateColor( + std::make_pair(0xE8E8E8, (int) StateColor::NotChecked), + std::make_pair(0x6DC48D, (int) StateColor::Normal))) + , m_text_color(StateColor( + std::make_pair(0x6B6B6B, (int) StateColor::NotChecked), + std::make_pair(0xFFFFFE, (int) StateColor::Normal))) + , m_text_color_grayed(StateColor( + std::make_pair(0x999999, (int) StateColor::NotChecked), + std::make_pair(0x99DFB2, (int) StateColor::Normal))) + , m_button_radius(10.0) + , m_button_padding(10, 6) +{ + SetCornerRadius(m_button_radius); + SetBorderWidth(0); + + sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->AddSpacer(8); + auto hsizer = new wxBoxSizer(wxVERTICAL); + hsizer->Add(sizer, 1, wxEXPAND | wxTOP | wxBOTTOM, 0); + SetSizer(hsizer); + SetMinSize(wxSize(-1, 20)); + + Bind(wxEVT_COMMAND_BUTTON_CLICKED, &MultiSwitchButton::button_clicked, this); + + SetFont(Label::Body_12); +} + +MultiSwitchButton::~MultiSwitchButton() +{ + DeleteAllOptions(); +} + +int MultiSwitchButton::AppendOption(const wxString &option, void *clientData) +{ + Button *btn = new Button(); + btn->Create(this, option, "", wxBORDER_NONE); + btn->SetFont(GetFont()); + + int states = state_handler.states(); + wxColor color = m_bg_color.colorForStates(states); + btn->SetBackgroundColour(color); + btn->SetBackgroundColor(m_bg_color); + btn->SetTextColor(m_text_color); + btn->SetCornerRadius(m_button_radius); + btn->SetPaddingSize(m_button_padding); + btn->SetClientData(clientData); + + btns.push_back(btn); + + if (btns.size() > 1) { sizer->AddSpacer(0); } + sizer->Add(btn, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL); + wxSize textSize = btn->GetTextExtent(option); + wxSize minSize = wxSize(textSize.x + m_button_padding.x * 2 + 6, -1); + btn->SetMinSize(minSize); + + return btns.size() - 1; +} + +void MultiSwitchButton::SetOptions(const std::vector& options) +{ + DeleteAllOptions(); + for (const auto& option : options) { + AppendOption(option); + } + sizer->AddSpacer(0); + if (btns.size() == 1) { + btns[0]->SetLeftCornerWhite(); + btns[0]->SetRightCornerWhite(); + } else if (btns.size() > 1) { + btns.front()->SetLeftCornerWhite(); + btns.back()->SetRightCornerWhite(); + } + Layout(); + Refresh(); +} + +void MultiSwitchButton::DeleteAllOptions() +{ + sel = -1; + for (auto btn : btns) { + if (btn) { + btn->Destroy(); + } + } + btns.clear(); + sizer->Clear(true); + sizer->AddSpacer(0); +} + +unsigned int MultiSwitchButton::GetCount() const +{ + return btns.size(); +} + +int MultiSwitchButton::GetSelection() const +{ + return sel; +} + +void MultiSwitchButton::SetSelection(int index) +{ + if (index < 0 || index >= (int) btns.size() || index == sel) { + return; + } + + sel = index; + update_button_styles(); + send_selection_event(); + Refresh(); +} + +wxString MultiSwitchButton::GetSelectedText() const +{ + if (sel >= 0 && sel < (int)btns.size()) { + return btns[sel]->GetLabel(); + } + return wxString(); +} + +wxString MultiSwitchButton::GetOptionText(unsigned int index) const +{ + return index < btns.size() ? btns[index]->GetLabel() : wxString(); +} + +void MultiSwitchButton::SetOptionText(unsigned int index, const wxString &text) +{ + if (index >= btns.size()) return; + btns[index]->SetLabel(text); +} + +void *MultiSwitchButton::GetOptionData(unsigned int index) const +{ + if (index >= btns.size()) return nullptr; + return btns[index]->GetClientData(); +} + +void MultiSwitchButton::SetOptionData(unsigned int index, void *client) +{ + if (index >= btns.size()) return; + btns[index]->SetClientData(client); +} + +void MultiSwitchButton::update_button_styles() +{ + for (int i = 0; i < (int) btns.size(); ++i) { + btns[i]->SetValue(i == sel); + + auto bg_color = btns[i]->IsGrayed() ? m_bg_color_grayed : m_bg_color; + auto text_color = btns[i]->IsGrayed() ? m_text_color_grayed : m_text_color; + btns[i]->SetBackgroundColor(bg_color); + btns[i]->SetTextColor(text_color); + btns[i]->Refresh(); + } +} + +void MultiSwitchButton::SetBackgroundColor(const StateColor &color) +{ + m_bg_color = color; + update_button_styles(); +} + +void MultiSwitchButton::SetTextColor(const StateColor &color) +{ + m_text_color = color; + update_button_styles(); +} + +void MultiSwitchButton::SetButtonCornerRadius(double radius) +{ + m_button_radius = radius; + SetCornerRadius(radius); + for (auto *btn : btns) { + btn->SetCornerRadius(radius); + } + Layout(); + Refresh(); +} + +void MultiSwitchButton::SetButtonPadding(const wxSize &padding) +{ + m_button_padding = padding; + for (auto *btn : btns) { + btn->SetPaddingSize(padding); + } + Layout(); + Refresh(); +} + +void MultiSwitchButton::Rescale() +{ + for (auto *btn : btns) { + btn->Rescale(); + } +} + +void MultiSwitchButton::button_clicked(wxCommandEvent &event) +{ + SetFocus(); + auto btn = event.GetEventObject(); + auto iter = std::find(btns.begin(), btns.end(), btn); + SetSelection(iter == btns.end() ? -1 : iter - btns.begin()); +} + +bool MultiSwitchButton::send_selection_event() +{ + wxCommandEvent evt(wxCUSTOMEVT_MULTISWITCH_SELECTION, GetId()); + evt.SetEventObject(this); + evt.SetInt(sel); + evt.SetString(GetSelectedText()); + GetEventHandler()->ProcessEvent(evt); + return true; +} + DeviceSwitchButton::DeviceSwitchButton(wxWindow* parent, const wxString& label, wxWindowID id) : m_on(this, "toggle_on", 28, 16) , m_off(this, "toggle_off", 28, 16) diff --git a/src/slic3r/GUI/Widgets/SwitchButton.hpp b/src/slic3r/GUI/Widgets/SwitchButton.hpp index 41ac896..07b5853 100644 --- a/src/slic3r/GUI/Widgets/SwitchButton.hpp +++ b/src/slic3r/GUI/Widgets/SwitchButton.hpp @@ -9,6 +9,7 @@ #include "Button.hpp" wxDECLARE_EVENT(wxCUSTOMEVT_SWITCH_POS, wxCommandEvent); +wxDECLARE_EVENT(wxCUSTOMEVT_MULTISWITCH_SELECTION, wxCommandEvent); wxDECLARE_EVENT(wxEXPAND_LEFT_DOWN, wxCommandEvent); class SwitchButton : public wxBitmapToggleButton @@ -158,6 +159,70 @@ private: void doRender(wxDC& dc); }; +class MultiSwitchButton : public StaticBox +{ + std::vector