diff --git a/src/slic3r/GUI/AmsMappingPopup.cpp b/src/slic3r/GUI/AmsMappingPopup.cpp index d48c340..ff54185 100644 --- a/src/slic3r/GUI/AmsMappingPopup.cpp +++ b/src/slic3r/GUI/AmsMappingPopup.cpp @@ -406,6 +406,7 @@ void AmsMapingPopup::on_left_down(wxMouseEvent &evt) if (pos.x > p_rect.x && pos.y > p_rect.y && pos.x < (p_rect.x + item->GetSize().x) && pos.y < (p_rect.y + item->GetSize().y)) { if (item->m_tray_data.type == TrayType::NORMAL && !is_match_material(item->m_tray_data.filament_type)) return; + if (item->m_tray_data.type == TrayType::EMPTY) return; item->send_event(m_current_filament_id); Dismiss(); } @@ -610,6 +611,7 @@ void AmsMapingPopup::add_ams_mapping(std::vector tray_data, wxWindow* if (tray_data[i].type == EMPTY) { m_mapping_item->set_data(wxColour(0xCE, 0xCE, 0xCE), "-", tray_data[i]); m_mapping_item->Bind(wxEVT_LEFT_DOWN, [this, tray_data, i, m_mapping_item](wxMouseEvent &e) { + return; //not allowed to map to empty slots m_mapping_item->send_event(m_current_filament_id); Dismiss(); }); diff --git a/src/slic3r/GUI/CaliHistoryDialog.cpp b/src/slic3r/GUI/CaliHistoryDialog.cpp index 4a4c60b..414f526 100644 --- a/src/slic3r/GUI/CaliHistoryDialog.cpp +++ b/src/slic3r/GUI/CaliHistoryDialog.cpp @@ -202,9 +202,8 @@ void HistoryWindow::update(MachineObject* obj) { if (!obj) return; - if (obj->cali_version != history_version) { + if (obj->cali_version != obj->last_cali_version) { if (obj->has_get_pa_calib_tab) { - history_version = obj->cali_version; reqeust_history_result(obj); } } diff --git a/src/slic3r/GUI/CaliHistoryDialog.hpp b/src/slic3r/GUI/CaliHistoryDialog.hpp index 8f7b49a..b8ef766 100644 --- a/src/slic3r/GUI/CaliHistoryDialog.hpp +++ b/src/slic3r/GUI/CaliHistoryDialog.hpp @@ -36,7 +36,6 @@ protected: bool& m_show_history_dialog; std::vector m_calib_results_history; MachineObject* curr_obj { nullptr }; - int history_version = -1; }; class EditCalibrationHistoryDialog : public DPIDialog diff --git a/src/slic3r/GUI/CreatePresetsDialog.cpp b/src/slic3r/GUI/CreatePresetsDialog.cpp index 612f439..debca10 100644 --- a/src/slic3r/GUI/CreatePresetsDialog.cpp +++ b/src/slic3r/GUI/CreatePresetsDialog.cpp @@ -37,8 +37,8 @@ namespace Slic3r { namespace GUI { static const std::vector filament_vendors = {"Polymaker", "OVERTURE", "Kexcelled", "HATCHBOX", "eSUN", "SUNLU", "Prusament", "Creality", "Protopasta", - "Anycubic", "Basf", "ELEGOO", "INLAND", "FLASHFORGE", "AMOLEN", "MIKA3D", "3DXTECH", "Duramic", - "Priline", "Eryone", "3Dgunius", "Novamaker", "Justmaker", "Giantarm", "iProspect"}; + "Anycubic", "Basf", "ELEGOO", "INLAND", "FLASHFORGE", "FusRock", "AMOLEN", "MIKA3D", "3DXTECH", + "Duramic", "Priline", "Eryone", "3Dgunius", "Novamaker", "Justmaker", "Giantarm", "iProspect", "LDO"}; static const std::vector filament_types = {"PLA", "PLA+", "PLA Tough", "PETG", "ABS", "ASA", "FLEX", "HIPS", "PA", "PACF", "NYLON", "PVA", "PC", "PCABS", "PCTG", "PCCF", "PP", "PEI", "PET", "PETG", diff --git a/src/slic3r/GUI/DeviceManager.cpp b/src/slic3r/GUI/DeviceManager.cpp index 88bf961..cb79f72 100644 --- a/src/slic3r/GUI/DeviceManager.cpp +++ b/src/slic3r/GUI/DeviceManager.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include "fast_float/fast_float.h" #define CALI_DEBUG @@ -175,6 +176,57 @@ std::string to_string_nozzle_diameter(float nozzle_diameter) return "0"; } +void sanitizeToUtf8(std::string& str) { + std::string result; + size_t i = 0; + + while (i < str.size()) { + unsigned char c = str[i]; + size_t remainingBytes = 0; + bool valid = true; + + if ((c & 0x80) == 0x00) { // 1-byte character (ASCII) + remainingBytes = 0; + } + else if ((c & 0xE0) == 0xC0) { // 2-byte character + remainingBytes = 1; + } + else if ((c & 0xF0) == 0xE0) { // 3-byte character + remainingBytes = 2; + } + else if ((c & 0xF8) == 0xF0) { // 4-byte character + remainingBytes = 3; + } + else { + valid = false; // Invalid first byte + } + + if (valid && i + remainingBytes < str.size()) { + for (size_t j = 1; j <= remainingBytes; ++j) { + if ((str[i + j] & 0xC0) != 0x80) { + valid = false; // Invalid continuation byte + break; + } + } + } + else { + valid = false; // Truncated character + } + + if (valid) { + // Append valid UTF-8 character + result.append(str, i, remainingBytes + 1); + i += remainingBytes + 1; + } + else { + // Replace invalid character with space + result += ' '; + ++i; // Skip the invalid byte + } + } + str = std::move(result); +} + namespace Slic3r { /* Common Functions */ @@ -1683,7 +1735,12 @@ int MachineObject::command_auto_leveling() int MachineObject::command_go_home() { - return this->publish_gcode("G28 \n"); + if (this->is_in_printing()) { + return this->publish_gcode("G28 X\n"); + } + else { + return this->publish_gcode("G28 \n"); + } } int MachineObject::command_control_fan(FanType fan_type, bool on_off) @@ -2555,7 +2612,7 @@ void MachineObject::reset_update_time() { BOOST_LOG_TRIVIAL(trace) << "reset reset_update_time, dev_id =" << dev_id; last_update_time = std::chrono::system_clock::now(); - subscribe_counter = 3; + subscribe_counter = SUBSCRIBE_RETRY_COUNT; } void MachineObject::reset() @@ -2579,7 +2636,7 @@ void MachineObject::reset() nozzle_diameter = 0.0f; network_wired = false; dev_connection_name = ""; - subscribe_counter = 3; + subscribe_counter = SUBSCRIBE_RETRY_COUNT; job_id_ = ""; m_plate_index = -1; @@ -2685,29 +2742,29 @@ bool MachineObject::is_camera_busy_off() return false; } -int MachineObject::publish_json(std::string json_str, int qos) +int MachineObject::publish_json(std::string json_str, int qos, int flag) { if (is_lan_mode_printer()) { - return local_publish_json(json_str, qos); + return local_publish_json(json_str, qos, flag); } else { - return cloud_publish_json(json_str, qos); + return cloud_publish_json(json_str, qos, flag); } } -int MachineObject::cloud_publish_json(std::string json_str, int qos) +int MachineObject::cloud_publish_json(std::string json_str, int qos, int flag) { int result = -1; if (m_agent) - result = m_agent->send_message(dev_id, json_str, qos); + result = m_agent->send_message(dev_id, json_str, qos, flag); return result; } -int MachineObject::local_publish_json(std::string json_str, int qos) +int MachineObject::local_publish_json(std::string json_str, int qos, int flag) { int result = -1; if (m_agent) { - result = m_agent->send_message_to_printer(dev_id, json_str, qos); + result = m_agent->send_message_to_printer(dev_id, json_str, qos, flag); } return result; } @@ -2755,10 +2812,24 @@ int MachineObject::parse_json(std::string payload, bool key_field_only) /* update last received time */ last_update_time = std::chrono::system_clock::now(); + json j_pre; + bool parse_ok = false; + try { + j_pre = json::parse(payload); + parse_ok = true; + } + catch(...) { + parse_ok = false; + /* post process payload */ + sanitizeToUtf8(payload); + BOOST_LOG_TRIVIAL(info) << "parse_json: sanitize to utf8"; + } + try { bool restored_json = false; json j; - json j_pre = json::parse(payload); + if (!parse_ok) + j_pre = json::parse(payload); CNumericLocalesSetter locales_setter; if (j_pre.empty()) { return 0; @@ -4663,15 +4734,24 @@ int MachineObject::parse_json(std::string payload, bool key_field_only) } } else if (jj["command"].get() == "extrusion_cali_get") { + std::string str = jj.dump(); + BOOST_LOG_TRIVIAL(info) << "extrusion_cali_get: " << str; + reset_pa_cali_history_result(); + bool is_succeed = true; if (jj.contains("result") && jj.contains("reason")) { if (jj["result"].get() == "fail") { - auto err_code = jj["err_code"].get(); - print_error = err_code; + if (jj.contains("err_code")) { + auto err_code = jj["err_code"].get(); + print_error = err_code; + } + is_succeed = false; } } - reset_pa_cali_history_result(); - has_get_pa_calib_tab = true; + if (is_succeed) { + last_cali_version = cali_version; + has_get_pa_calib_tab = true; + } if (jj.contains("nozzle_diameter")) { if (jj["nozzle_diameter"].is_number_float()) { @@ -4690,11 +4770,6 @@ int MachineObject::parse_json(std::string payload, bool key_field_only) if (jj.contains("filaments") && jj["filaments"].is_array()) { try { -#ifdef CALI_DEBUG - std::string str = jj.dump(); - BOOST_LOG_TRIVIAL(info) << "extrusion_cali_get: " << str; -#endif - for (auto it = jj["filaments"].begin(); it != jj["filaments"].end(); it++) { PACalibResult pa_calib_result; pa_calib_result.filament_id = (*it)["filament_id"].get(); @@ -4733,23 +4808,25 @@ int MachineObject::parse_json(std::string payload, bool key_field_only) // notify cali history to update } else if (jj["command"].get() == "extrusion_cali_get_result") { + std::string str = jj.dump(); + BOOST_LOG_TRIVIAL(info) << "extrusion_cali_get_result: " << str; + reset_pa_cali_result(); + bool is_succeed = true; if (jj.contains("result") && jj.contains("reason")) { if (jj["result"].get() == "fail") { - auto err_code = jj["err_code"].get(); - print_error = err_code; + if (jj.contains("err_code")) { + auto err_code = jj["err_code"].get(); + print_error = err_code; + is_succeed = false; + } } } - reset_pa_cali_result(); - get_pa_calib_result = true; + if (is_succeed) + get_pa_calib_result = true; if (jj.contains("filaments") && jj["filaments"].is_array()) { try { -#ifdef CALI_DEBUG - std::string str = jj.dump(); - BOOST_LOG_TRIVIAL(info) << "extrusion_cali_get_result: " << str; -#endif - for (auto it = jj["filaments"].begin(); it != jj["filaments"].end(); it++) { PACalibResult pa_calib_result; pa_calib_result.tray_id = (*it)["tray_id"].get(); @@ -4936,7 +5013,7 @@ int MachineObject::publish_gcode(std::string gcode_str) t["gcode"] = j.dump(); m_agent->track_event("cmd_gcode_line", t.dump()); } - return publish_json(j.dump()); + return publish_json(j.dump(), 0); } QDTSubTask* MachineObject::get_subtask() @@ -5334,8 +5411,9 @@ int MachineObject::get_flag_bits(int num, int start, int count) void MachineObject::update_printer_preset_name(const std::string &nozzle_diameter_str) { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " " << __LINE__ << "start update preset_name"; - auto preset_boundle = Slic3r::GUI::wxGetApp().preset_bundle; - auto printer_set = preset_boundle->get_printer_names_by_printer_type_and_nozzle(MachineObject::get_preset_printer_model_name(this->printer_type), nozzle_diameter_str); + auto preset_bundle = Slic3r::GUI::wxGetApp().preset_bundle; + if (!preset_bundle) return; + auto printer_set = preset_bundle->get_printer_names_by_printer_type_and_nozzle(MachineObject::get_preset_printer_model_name(this->printer_type), nozzle_diameter_str); if (printer_set.size() > 0) m_printer_preset_name = *printer_set.begin(); else @@ -5732,13 +5810,13 @@ bool DeviceManager::set_selected_machine(std::string dev_id, bool need_disconnec } } else { BOOST_LOG_TRIVIAL(info) << "static: set_selected_machine: same dev_id = empty"; - m_agent->set_user_selected_machine(""); it->second->reset(); #if !QDT_RELEASE_TO_PUBLIC it->second->connect(false, Slic3r::GUI::wxGetApp().app_config->get("enable_ssl_for_mqtt") == "true" ? true : false); #else it->second->connect(false, it->second->local_use_ssl_for_mqtt); #endif + m_agent->set_user_selected_machine(dev_id); it->second->set_lan_mode_connection_state(true); } } diff --git a/src/slic3r/GUI/DeviceManager.hpp b/src/slic3r/GUI/DeviceManager.hpp index 46aaeb5..4c3fca1 100644 --- a/src/slic3r/GUI/DeviceManager.hpp +++ b/src/slic3r/GUI/DeviceManager.hpp @@ -40,6 +40,7 @@ #define VIRTUAL_TRAY_ID 254 #define START_SEQ_ID 20000 #define END_SEQ_ID 30000 +#define SUBSCRIBE_RETRY_COUNT 5 inline int correct_filament_temperature(int filament_temp) { @@ -627,6 +628,7 @@ public: bool is_support_layer_num { false }; bool nozzle_blob_detection_enabled{ false }; + int last_cali_version = -1; int cali_version = -1; float cali_selected_nozzle_dia { 0.0 }; // 1: record when start calibration in preset page @@ -961,9 +963,9 @@ public: /* Msg for display MsgFn */ typedef std::function MsgFn; - int publish_json(std::string json_str, int qos = 0); - int cloud_publish_json(std::string json_str, int qos = 0); - int local_publish_json(std::string json_str, int qos = 0); + int publish_json(std::string json_str, int qos = 0, int flag = 0); + int cloud_publish_json(std::string json_str, int qos = 0, int flag = 0); + int local_publish_json(std::string json_str, int qos = 0, int flag = 0); int parse_json(std::string payload, bool key_filed_only = false); int publish_gcode(std::string gcode_str); diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 910aa39..87f7012 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -751,7 +751,8 @@ const std::vector GCodeViewer::Extrusion_Role_Colors {{ { 0.00f, 0.50f, 0.00f, 1.0f }, // erSupportMaterialInterface { 0.00f, 0.25f, 0.00f, 1.0f }, // erSupportTransition { 0.70f, 0.89f, 0.67f, 1.0f }, // erWipeTower - { 0.37f, 0.82f, 0.58f, 1.0f } // erCustom + { 0.37f, 0.82f, 0.58f, 1.0f }, // erCustom + { 0.85f, 0.65f, 0.95f, 1.0f } // erFlush }}; const std::vector GCodeViewer::Options_Colors {{ @@ -3096,6 +3097,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result, const last_travel_s_id = move_id; } + else if (move.type == EMoveType::Unretract && move.extrusion_role == ExtrusionRole::erFlush) { + m_roles.emplace_back(move.extrusion_role); + } } // roles -> remove duplicates diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 1b9ce07..b075e7f 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4572,8 +4572,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // Detection of doubleclick on text to open emboss edit window auto type = m_gizmos.get_current_type(); if (evt.LeftDClick() && !m_hover_volume_idxs.empty() && - (type == GLGizmosManager::EType::Undefined //||type == GLGizmosManager::EType::Text || - //type == GLGizmosManager::EType::Svg + (type == GLGizmosManager::EType::Undefined || + type == GLGizmosManager::EType::Text || + type == GLGizmosManager::EType::Svg )) { for (int hover_volume_id : m_hover_volume_idxs) { const GLVolume &hover_gl_volume = *m_volumes.volumes[hover_volume_id]; @@ -4586,16 +4587,26 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) continue; const ModelVolume *hover_volume = hover_object->volumes[hover_volume_idx]; - /* if (hover_volume->text_configuration.has_value()) { + if (hover_volume->is_text()) { + m_selection.add_volumes(Selection::EMode::Volume, {(unsigned) hover_volume_id}); + if (type == GLGizmosManager::EType::Text) + m_gizmos.open_gizmo(GLGizmosManager::EType::Text); // close text + wxGetApp().obj_list()->update_selections(); + m_gizmos.open_gizmo(GLGizmosManager::EType::Text); + return; + } + /* else if (hover_volume->text_configuration.has_value()) { m_selection.add_volumes(Selection::EMode::Volume, {(unsigned) hover_volume_id}); if (type != GLGizmosManager::EType::Emboss) m_gizmos.open_gizmo(GLGizmosManager::EType::Emboss); wxGetApp().obj_list()->update_selections(); return; - } else*/ if (hover_volume->emboss_shape.has_value()) { + }*/ + else if(hover_volume->emboss_shape.has_value()){ m_selection.add_volumes(Selection::EMode::Volume, {(unsigned) hover_volume_id}); - if (type != GLGizmosManager::EType::Svg) - m_gizmos.open_gizmo(GLGizmosManager::EType::Svg); + if (type == GLGizmosManager::EType::Svg) + m_gizmos.open_gizmo(GLGizmosManager::EType::Svg);// close svg wxGetApp().obj_list()->update_selections(); + m_gizmos.open_gizmo(GLGizmosManager::EType::Svg); return; } } diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index 5b4aeaa..fdcb61c 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -33,7 +33,7 @@ wxDEFINE_EVENT(EVT_GLTOOLBAR_PRINT_SELECT, SimpleEvent); wxDEFINE_EVENT(EVT_GLTOOLBAR_SEND_TO_PRINTER, SimpleEvent); wxDEFINE_EVENT(EVT_GLTOOLBAR_SEND_TO_PRINTER_ALL, SimpleEvent); wxDEFINE_EVENT(EVT_GLTOOLBAR_PRINT_MULTI_MACHINE, SimpleEvent); - +wxDEFINE_EVENT(EVT_GLTOOLBAR_SEND_MULTI_APP, SimpleEvent); wxDEFINE_EVENT(EVT_GLTOOLBAR_ADD, SimpleEvent); wxDEFINE_EVENT(EVT_GLTOOLBAR_DELETE, SimpleEvent); diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp index ebcf25e..a58ceaa 100644 --- a/src/slic3r/GUI/GLToolbar.hpp +++ b/src/slic3r/GUI/GLToolbar.hpp @@ -34,7 +34,7 @@ wxDECLARE_EVENT(EVT_GLTOOLBAR_PRINT_SELECT, SimpleEvent); wxDECLARE_EVENT(EVT_GLTOOLBAR_SEND_TO_PRINTER, SimpleEvent); wxDECLARE_EVENT(EVT_GLTOOLBAR_SEND_TO_PRINTER_ALL, SimpleEvent); wxDECLARE_EVENT(EVT_GLTOOLBAR_PRINT_MULTI_MACHINE, SimpleEvent); - +wxDECLARE_EVENT(EVT_GLTOOLBAR_SEND_MULTI_APP, SimpleEvent); wxDECLARE_EVENT(EVT_GLTOOLBAR_ADD, SimpleEvent); wxDECLARE_EVENT(EVT_GLTOOLBAR_DELETE, SimpleEvent); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 3dce217..406bd78 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -1313,6 +1313,8 @@ void GUI_App::post_init() this->check_track_enable(); } + + this->check_cert(); }); } @@ -2032,6 +2034,8 @@ void GUI_App::init_networking_callbacks() obj->command_get_version(); obj->erase_user_access_code(); obj->command_get_access_code(); + if (m_agent) + m_agent->install_device_cert(obj->dev_id, obj->is_lan_mode_printer()); if (!is_enable_multi_machine()) { GUI::wxGetApp().sidebar().load_ams_list(obj->dev_id, obj); } @@ -2075,11 +2079,12 @@ void GUI_App::init_networking_callbacks() event.SetString(obj->dev_id); GUI::wxGetApp().sidebar().load_ams_list(obj->dev_id, obj); } else if (state == ConnectStatus::ConnectStatusFailed) { - obj->set_access_code(""); - obj->erase_user_access_code(); + m_device_manager->localMachineList.erase(obj->dev_id); m_device_manager->set_selected_machine("", true); wxString text; if (msg == "5") { + obj->set_access_code(""); + obj->erase_user_access_code(); text = wxString::Format(_L("Incorrect password")); wxGetApp().show_dialog(text); } else { @@ -2088,9 +2093,9 @@ void GUI_App::init_networking_callbacks() } event.SetInt(-1); } else if (state == ConnectStatus::ConnectStatusLost) { - obj->set_access_code(""); - obj->erase_user_access_code(); - m_device_manager->localMachineList.erase(obj->dev_id); + //obj->set_access_code(""); + //obj->erase_user_access_code(); + //m_device_manager->localMachineList.erase(obj->dev_id); m_device_manager->set_selected_machine("", true); event.SetInt(-1); BOOST_LOG_TRIVIAL(info) << "set_on_local_connect_fn: state = lost"; @@ -2130,6 +2135,8 @@ void GUI_App::init_networking_callbacks() CallAfter([this, dev_id, msg] { if (m_is_closing) return; + this->process_network_msg(dev_id, msg); + MachineObject* obj = this->m_device_manager->get_user_machine(dev_id); if (obj) { obj->is_ams_need_update = false; @@ -2181,7 +2188,8 @@ void GUI_App::init_networking_callbacks() CallAfter([this, dev_id, msg] { if (m_is_closing) return; - + + this->process_network_msg(dev_id, msg); MachineObject* obj = m_device_manager->get_my_machine(dev_id); if (!obj || !obj->is_lan_mode_printer()) { obj = m_device_manager->get_local_machine(dev_id); @@ -2446,7 +2454,7 @@ void GUI_App::on_start_subscribe_again(std::string dev_id) BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": dev_id=" << obj->dev_id; } }); - start_subscribe_timer->Start(4000, wxTIMER_ONE_SHOT); + start_subscribe_timer->Start(5000, wxTIMER_ONE_SHOT); } std::string GUI_App::get_local_models_path() @@ -4118,6 +4126,7 @@ std::string GUI_App::handle_web_request(std::string cmd) { try { //QDS use nlohmann json format + BOOST_LOG_TRIVIAL(info) << "handle_web_request: " << cmd; std::stringstream ss(cmd), oss; pt::ptree root, response; pt::read_json(ss, root); @@ -4128,6 +4137,7 @@ std::string GUI_App::handle_web_request(std::string cmd) boost::optional command = root.get_optional("command"); if (command.has_value()) { std::string command_str = command.value(); + BOOST_LOG_TRIVIAL(info) << "handle_web_request: " << command_str; if (command_str.compare("request_project_download") == 0) { if (root.get_child_optional("data") != boost::none) { pt::ptree data_node = root.get_child("data"); @@ -4160,6 +4170,13 @@ std::string GUI_App::handle_web_request(std::string cmd) #endif } else if (command_str.compare("homepage_login_or_register") == 0) { + //Check Plugin + bool bValid = is_compatibility_version(); + if (!bValid) { + CallAfter([this] { handle_web_request("{\"sequence_id\":1,\"command\":\"homepage_need_networkplugin\"}"); + }); + return ""; + } if (root.get_child_optional("makerworld_model_id") != boost::none) { boost::optional ModelID = root.get_optional("makerworld_model_id"); @@ -4202,11 +4219,11 @@ std::string GUI_App::handle_web_request(std::string cmd) } //ZY1 /*else if (command_str.compare("modelmall_model_advise_get") == 0) { + CallAfter([this] { if (mainframe && this->app_config->get("staff_pick_switch") == "true") { - if (mainframe->m_webview) { - mainframe->m_webview->SendDesignStaffpick(has_model_mall()); - } - } + if (mainframe->m_webview) { mainframe->m_webview->SendDesignStaffpick(has_model_mall()); } + } + }); }*/ else if (command_str.compare("modelmall_model_open") == 0) { if (root.get_child_optional("data") != boost::none) { @@ -4327,7 +4344,9 @@ std::string GUI_App::handle_web_request(std::string cmd) } } else if (command_str.compare("homepage_makerlab_get") == 0) { + CallAfter([this] { if (mainframe && mainframe->m_webview) { mainframe->m_webview->SendMakerlabList(); } + }); } else if (command_str.compare("homepage_makerlab_open") == 0) { if (root.get_child_optional("url") != boost::none) { @@ -4395,8 +4414,50 @@ std::string GUI_App::handle_web_request(std::string cmd) } else if (command_str.compare("homepage_printhistory_get")==0) { - if (mainframe && mainframe->m_webview) { - mainframe->m_webview->ShowUserPrintTask(true); + CallAfter([this] { + if (mainframe && mainframe->m_webview) { mainframe->m_webview->ShowUserPrintTask(true); } + }); + } + else if (command_str.compare("homepage_leftmenu_change_width") == 0) { + int NewWidth = 214; + if (root.get_child_optional("width") != boost::none) NewWidth = root.get("width"); + + if (mainframe && mainframe->m_webview) + { + mainframe->m_webview->SetLeftMenuWidth(NewWidth); + mainframe->m_webview->Layout(); + } + } + else if (command_str.compare("homepage_makerlab_open_3mf_binary") == 0) { + if (root.get_child_optional("3mf") != boost::none) { + std::string str3MFBase64 = root.get_optional("3mf").value(); + + std::string str3MFName = "makerlab"; + if (root.get_child_optional("3mf_name") != boost::none) + { + std::string strTmp = from_u8(root.get_optional("3mf_name").value()).ToStdString(); + if (strTmp != "") str3MFName = strTmp; + } + + if (mainframe && mainframe->m_webview) + { + mainframe->m_webview->OpenMakerlab3mf(str3MFBase64,str3MFName); + } + } + } + else if (command_str.compare("homepage_makerlab_stl_download")==0) + { + if (root.get_child_optional("file_data") != boost::none && root.get_child_optional("sequence_id") != boost::none) { + int SeqID = root.get_optional("sequence_id").value(); + std::string strSTLBase64 = root.get_optional("file_data").value(); + + std::string strSTLName = "makerlab"; + if (root.get_child_optional("file_name") != boost::none) { + std::string strTmp = from_u8(root.get_optional("file_name").value()).ToStdString(); + if (strTmp != "") strSTLName = strTmp; + } + + if (mainframe && mainframe->m_webview) { mainframe->m_webview->SaveMakerlabStl(SeqID,strSTLBase64, strSTLName); } } } } @@ -4519,16 +4580,22 @@ void GUI_App::on_http_error(wxCommandEvent &evt) // Version limit if (code == HttpErrorVersionLimited) { - if (!m_show_http_errpr_msgdlg) { + if (!m_show_error_msgdlg) { MessageDialog msg_dlg(nullptr, _L("The QIDI Studio version is too old to enable cloud service. Please download the latest version from QIDI Tech website."), "", wxAPPLY | wxOK); - m_show_http_errpr_msgdlg = true; + m_show_error_msgdlg = true; auto modal_result = msg_dlg.ShowModal(); - if (modal_result == wxOK || modal_result == wxCLOSE) { - m_show_http_errpr_msgdlg = false; - return; - } + m_show_error_msgdlg = false; + return; + } + } + else if (status == 400 && code == HttpErrorCertRevoked) { + if (!m_show_error_msgdlg) { + MessageDialog msg_dlg(nullptr, _L("Your software certificate has been revoked, please update Bambu Studio software."), "", wxAPPLY | wxOK); + m_show_error_msgdlg = true; + auto modal_result = msg_dlg.ShowModal(); + m_show_error_msgdlg = false; + return; } - } // request login @@ -4536,15 +4603,12 @@ void GUI_App::on_http_error(wxCommandEvent &evt) if (m_agent) { if (m_agent->is_user_login()) { this->request_user_logout(); - - if (!m_show_http_errpr_msgdlg) { + if (!m_show_error_msgdlg) { MessageDialog msg_dlg(nullptr, _L("Login information expired. Please login again."), "", wxAPPLY | wxOK); - m_show_http_errpr_msgdlg = true; + m_show_error_msgdlg = true; auto modal_result = msg_dlg.ShowModal(); - if (modal_result == wxOK || modal_result == wxCLOSE) { - m_show_http_errpr_msgdlg = false; - return; - } + m_show_error_msgdlg = false; + return; } } } @@ -4569,7 +4633,11 @@ void GUI_App::on_set_selected_machine(wxCommandEvent &evt) { DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager(); if (dev) { - dev->set_selected_machine(m_agent->get_user_selected_machine()); + auto dev_id = m_agent->get_user_selected_machine(); + + if (dev->get_user_machine(dev_id)) { + dev->set_selected_machine(dev_id); + } } } @@ -4926,6 +4994,89 @@ void GUI_App::check_beta_version() }).perform(); } +void GUI_App::check_cert() +{ + m_check_cert_thread = Slic3r::create_thread( + [this]{ + if (m_agent) + m_agent->check_cert(); + }); + BOOST_LOG_TRIVIAL(info) << "check_cert"; +} + +void GUI_App::process_network_msg(std::string dev_id, std::string msg) +{ + if (dev_id.empty()) { + if (msg == "wait_info") { + BOOST_LOG_TRIVIAL(info) << "process_network_msg, wait_info"; + Slic3r::DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager(); + if (!dev) + return; + MachineObject* obj = dev->get_selected_machine(); + if (obj) + m_agent->install_device_cert(obj->dev_id, obj->is_lan_mode_printer()); + if (!m_show_error_msgdlg) { + MessageDialog msg_dlg(nullptr, _L("Retrieving printer information, please try again later."), "", wxAPPLY | wxOK); + m_show_error_msgdlg = true; + auto modal_result = msg_dlg.ShowModal(); + m_show_error_msgdlg = false; + } + } + else if (msg == "update_studio") { + BOOST_LOG_TRIVIAL(info) << "process_network_msg, update_studio"; + if (!m_show_error_msgdlg) { + MessageDialog msg_dlg(nullptr, _L("Please try updating Bambu Studio and then try again."), "", wxAPPLY | wxOK); + m_show_error_msgdlg = true; + auto modal_result = msg_dlg.ShowModal(); + m_show_error_msgdlg = false; + } + } + else if (msg == "update_fixed_studio") { + BOOST_LOG_TRIVIAL(info) << "process_network_msg, update_fixed_studio"; + if (!m_show_error_msgdlg) { + MessageDialog msg_dlg(nullptr, _L("Please try updating Bambu Studio and then try again."), "", wxAPPLY | wxOK); + m_show_error_msgdlg = true; + auto modal_result = msg_dlg.ShowModal(); + m_show_error_msgdlg = false; + } + } + else if (msg == "cert_expired") { + BOOST_LOG_TRIVIAL(info) << "process_network_msg, cert_expired"; + if (!m_show_error_msgdlg) { + MessageDialog msg_dlg(nullptr, _L("The certificate has expired. Please check the time settings or update Bambu Studio and try again."), "", wxAPPLY | wxOK); + m_show_error_msgdlg = true; + auto modal_result = msg_dlg.ShowModal(); + m_show_error_msgdlg = false; + } + } + else if (msg == "cert_revoked") { + BOOST_LOG_TRIVIAL(info) << "process_network_msg, cert_revoked"; + if (!m_show_error_msgdlg) { + MessageDialog msg_dlg(nullptr, _L("The certificate is no longer valid and the printing functions are unavailable. If you need printing. Please visit the official website at https://bambulab.com/ to download and update."), "", wxAPPLY | wxOK); + m_show_error_msgdlg = true; + auto modal_result = msg_dlg.ShowModal(); + m_show_error_msgdlg = false; + } + } + else if (msg == "update_firmware_studio") { + BOOST_LOG_TRIVIAL(info) << "process_network_msg, firmware internal error"; + if (!m_show_error_msgdlg) { + MessageDialog msg_dlg(nullptr, _L("Internal error. Please try upgrading the firmware and Studio version. If the issue persists, contact customer support."), "", wxAPPLY | wxOK); + m_show_error_msgdlg = true; + auto modal_result = msg_dlg.ShowModal(); + m_show_error_msgdlg = false; + } + } + else if (msg == "unsigned_studio") { + BOOST_LOG_TRIVIAL(info) << "process_network_msg, unsigned_studio"; + MessageDialog msg_dlg(nullptr, _L("Your software is not signed, and some printing functions have been restricted. Please use the officially signed software version."), "", wxAPPLY | wxOK); + m_show_error_msgdlg = true; + auto modal_result = msg_dlg.ShowModal(); + m_show_error_msgdlg = false; + } + } +} + void GUI_App::no_new_version() { wxCommandEvent* evt = new wxCommandEvent(EVT_SHOW_NO_NEW_VERSION); diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 8d03e3f..f093a06 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -314,11 +314,11 @@ private: bool m_is_dark_mode{ false }; bool m_adding_script_handler { false }; bool m_side_popup_status{false}; - bool m_show_http_errpr_msgdlg{false}; + bool m_show_error_msgdlg{false}; wxString m_info_dialog_content; HttpServer m_http_server; - boost::thread m_check_network_thread; + boost::thread m_check_cert_thread; public: //try again when subscription fails void on_start_subscribe_again(std::string dev_id); @@ -481,6 +481,8 @@ public: void check_update(bool show_tips, int by_user); void check_new_version(bool show_tips = false, int by_user = 0); + void check_cert(); + void process_network_msg(std::string dev_id, std::string msg); void check_beta_version(); void request_new_version(int by_user); void enter_force_upgrade(); diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index a7b71e4..e45e26c 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -547,7 +547,7 @@ wxMenu* MenuFactory::append_submenu_add_generic(wxMenu* menu, ModelVolumeType ty if (type != ModelVolumeType::INVALID) { append_menu_item(sub_menu, wxID_ANY, _L("Load..."), "", - [type](wxCommandEvent&) { obj_list()->load_subobject(type,true); }, "", menu); + [type](wxCommandEvent&) { obj_list()->load_subobject(type); }, "", menu); sub_menu->AppendSeparator(); } diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 16c5efa..8e1c36f 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -2694,12 +2694,11 @@ void ObjectList::split() } take_snapshot("Split to parts"); - - volume->split(filament_cnt); - wxBusyCursor wait; - - auto model_object = (*m_objects)[obj_idx]; + auto model_object = (*m_objects)[obj_idx]; + auto world_tran = model_object->instances[0]->get_transformation().get_matrix() * volume->get_matrix(); + float scale_det = std::fabs(world_tran.matrix().block(0, 0, 3, 3).determinant()); + volume->split(filament_cnt, scale_det); auto parent = m_objects_model->GetObject(item); if (parent) diff --git a/src/slic3r/GUI/GUI_ObjectTable.cpp b/src/slic3r/GUI/GUI_ObjectTable.cpp index 9c86c94..7960148 100644 --- a/src/slic3r/GUI/GUI_ObjectTable.cpp +++ b/src/slic3r/GUI/GUI_ObjectTable.cpp @@ -1889,6 +1889,7 @@ void ObjectGridTable::init_cols(ObjectGrid *object_grid) col = new ObjectGridCol(coEnum, "brim_type", L("Support"), true, false, true, true, wxALIGN_LEFT); col->size = object_grid->GetTextExtent(L("Auto Brim")).x + 8; //add 8 for border col->choices.Add(_L("Auto")); + col->choices.Add(_L("Painted")); col->choices.Add(_L("Outer brim only")); col->choices.Add(_L("Inner brim only")); col->choices.Add(_L("Outer and inner brim")); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp index 7625508..f2a8233 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp @@ -358,7 +358,7 @@ void GLGizmoFlatten::update_planes() planes.pop_back(); } }; - const int plane_count = 10; + const int plane_count = 20; for (size_t i = 0; i < m_planes.size(); i++) { if (m_planes[i].area < experted_minimal_area) { if (i + 1 >= plane_count) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.cpp index 985cafc..31d9741 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.cpp @@ -1,12 +1,16 @@ #include "GLGizmoMeshBoolean.hpp" -#include "slic3r/GUI/GLCanvas3D.hpp" -#include "slic3r/GUI/ImGuiWrapper.hpp" -#include "slic3r/GUI/GUI.hpp" +#include "libslic3r/CSGMesh/CSGMesh.hpp" #include "libslic3r/MeshBoolean.hpp" -#include "slic3r/GUI/GUI_ObjectList.hpp" -#include "slic3r/GUI/Plater.hpp" +#include "libslic3r/CSGMesh/ModelToCSGMesh.hpp" +#include "libslic3r/CSGMesh/PerformCSGMeshBooleans.hpp" #include "slic3r/GUI/Camera.hpp" +#include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/GUI.hpp" +#include "slic3r/GUI/GUI_ObjectList.hpp" +#include "slic3r/GUI/ImGuiWrapper.hpp" #include "slic3r/GUI/NotificationManager.hpp" +#include "slic3r/GUI/Plater.hpp" + #ifndef IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS #endif @@ -366,18 +370,21 @@ void GLGizmoMeshBoolean::on_render_input_window(float x, float y, float bottom_l if (m_operation_mode == MeshBooleanOperation::Union) { if (operate_button(_L("Union") + "##btn", enable_button)) { - TriangleMesh temp_src_mesh = m_src.mv->mesh(); - temp_src_mesh.transform(m_src.trafo); - TriangleMesh temp_tool_mesh = m_tool.mv->mesh(); - temp_tool_mesh.transform(m_tool.trafo); - std::vector temp_mesh_resuls; - Slic3r::MeshBoolean::mcut::make_boolean(temp_src_mesh, temp_tool_mesh, temp_mesh_resuls, "UNION"); - if (temp_mesh_resuls.size() != 0) { - generate_new_volume(true, *temp_mesh_resuls.begin()); - m_warning_texts[index] = ""; - } - else { - m_warning_texts[index] = warning_text_common; + m_warning_texts[index] = check_boolean_possible({ m_src.mv, m_tool.mv }); + if(m_warning_texts[index] == "") { + TriangleMesh temp_src_mesh = m_src.mv->mesh(); + temp_src_mesh.transform(m_src.trafo); + TriangleMesh temp_tool_mesh = m_tool.mv->mesh(); + temp_tool_mesh.transform(m_tool.trafo); + std::vector temp_mesh_resuls; + Slic3r::MeshBoolean::mcut::make_boolean(temp_src_mesh, temp_tool_mesh, temp_mesh_resuls, "UNION"); + if (temp_mesh_resuls.size() != 0) { + generate_new_volume(true, *temp_mesh_resuls.begin()); + m_warning_texts[index] = ""; + } + else { + m_warning_texts[index] = warning_text_common; + } } m_selecting_state = MeshBooleanSelectingState::SelectSource; m_src.reset(); @@ -387,18 +394,21 @@ void GLGizmoMeshBoolean::on_render_input_window(float x, float y, float bottom_l else if (m_operation_mode == MeshBooleanOperation::Difference) { m_imgui->qdt_checkbox(_L("Delete input"), m_diff_delete_input); if (operate_button(_L("Difference") + "##btn", enable_button)) { - TriangleMesh temp_src_mesh = m_src.mv->mesh(); - temp_src_mesh.transform(m_src.trafo); - TriangleMesh temp_tool_mesh = m_tool.mv->mesh(); - temp_tool_mesh.transform(m_tool.trafo); - std::vector temp_mesh_resuls; - Slic3r::MeshBoolean::mcut::make_boolean(temp_src_mesh, temp_tool_mesh, temp_mesh_resuls, "A_NOT_B"); - if (temp_mesh_resuls.size() != 0) { - generate_new_volume(m_diff_delete_input, *temp_mesh_resuls.begin()); - m_warning_texts[index] = ""; - } - else { - m_warning_texts[index] = warning_text_common; + m_warning_texts[index] = check_boolean_possible({ m_src.mv, m_tool.mv }); + if (m_warning_texts[index] == "") { + TriangleMesh temp_src_mesh = m_src.mv->mesh(); + temp_src_mesh.transform(m_src.trafo); + TriangleMesh temp_tool_mesh = m_tool.mv->mesh(); + temp_tool_mesh.transform(m_tool.trafo); + std::vector temp_mesh_resuls; + Slic3r::MeshBoolean::mcut::make_boolean(temp_src_mesh, temp_tool_mesh, temp_mesh_resuls, "A_NOT_B"); + if (temp_mesh_resuls.size() != 0) { + generate_new_volume(m_diff_delete_input, *temp_mesh_resuls.begin()); + m_warning_texts[index] = ""; + } + else { + m_warning_texts[index] = warning_text_common; + } } m_selecting_state = MeshBooleanSelectingState::SelectSource; m_src.reset(); @@ -408,18 +418,21 @@ void GLGizmoMeshBoolean::on_render_input_window(float x, float y, float bottom_l else if (m_operation_mode == MeshBooleanOperation::Intersection){ m_imgui->qdt_checkbox(_L("Delete input"), m_inter_delete_input); if (operate_button(_L("Intersection") + "##btn", enable_button)) { - TriangleMesh temp_src_mesh = m_src.mv->mesh(); - temp_src_mesh.transform(m_src.trafo); - TriangleMesh temp_tool_mesh = m_tool.mv->mesh(); - temp_tool_mesh.transform(m_tool.trafo); - std::vector temp_mesh_resuls; - Slic3r::MeshBoolean::mcut::make_boolean(temp_src_mesh, temp_tool_mesh, temp_mesh_resuls, "INTERSECTION"); - if (temp_mesh_resuls.size() != 0) { - generate_new_volume(m_inter_delete_input, *temp_mesh_resuls.begin()); - m_warning_texts[index] = ""; - } - else { - m_warning_texts[index] = warning_text_intersection; + m_warning_texts[index] = check_boolean_possible({ m_src.mv, m_tool.mv }); + if (m_warning_texts[index] == "") { + TriangleMesh temp_src_mesh = m_src.mv->mesh(); + temp_src_mesh.transform(m_src.trafo); + TriangleMesh temp_tool_mesh = m_tool.mv->mesh(); + temp_tool_mesh.transform(m_tool.trafo); + std::vector temp_mesh_resuls; + Slic3r::MeshBoolean::mcut::make_boolean(temp_src_mesh, temp_tool_mesh, temp_mesh_resuls, "INTERSECTION"); + if (temp_mesh_resuls.size() != 0) { + generate_new_volume(m_inter_delete_input, *temp_mesh_resuls.begin()); + m_warning_texts[index] = ""; + } + else { + m_warning_texts[index] = warning_text_intersection; + } } m_selecting_state = MeshBooleanSelectingState::SelectSource; m_src.reset(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp index eff2b99..6a1e8f2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp @@ -508,6 +508,7 @@ void GLGizmoSVG::on_set_state() { // Closing gizmo. e.g. selecting another one if (GLGizmoBase::m_state == GLGizmoBase::Off) { + ImGui::FocusWindow(nullptr); // exit cursor m_parent.enable_moving(true); // modify by qds reset_volume(); } else if (GLGizmoBase::m_state == GLGizmoBase::On) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoText.cpp b/src/slic3r/GUI/Gizmos/GLGizmoText.cpp index 1784d44..9019e71 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoText.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoText.cpp @@ -395,12 +395,71 @@ void GLGizmoText::on_set_state() if (m_state == EState::On) { m_last_text_mv = nullptr; m_need_fix = false; + m_show_text_normal_reset_tip = false; load_init_text(); + if (m_last_text_mv) { + m_reedit_text = true; + m_need_fix = true; + m_load_text_tran_in_object = m_text_tran_in_object; + if (m_really_use_surface_calc) { + m_show_warning_regenerated = true; + m_need_fix = false; + use_fix_normal_position(); + } else if (m_fix_old_tran_flag && m_font_version == "") { + m_show_warning_old_tran = false; + auto offset = m_text_tran_in_object.get_offset(); + auto rotation = m_text_tran_in_object.get_rotation(); + float eps = 0.01f; + int count = 0; + bool has_rotation = rotation.norm() > eps; + count += has_rotation ? 1 : 0; + auto scaling_factor = m_text_tran_in_object.get_scaling_factor(); + bool has_scale = (scaling_factor - Vec3d(1, 1, 1)).norm() > eps; + count += has_scale ? 1 : 0; + auto mirror = m_text_tran_in_object.get_mirror(); + bool has_mirror = (mirror - Vec3d(1, 1, 1)).norm() > eps; + count += has_mirror ? 1 : 0; + + Geometry::Transformation expert_text_tran_in_world; + generate_text_tran_in_world(m_fix_text_normal_in_world.cast(), m_text_position_in_world, m_rotate_angle, expert_text_tran_in_world); + auto temp_expert_text_tran_in_object = m_object_cs_to_world_tran.inverse() * expert_text_tran_in_world.get_matrix(); + Geometry::Transformation expert_text_tran_in_object(temp_expert_text_tran_in_object); + expert_text_tran_in_object.set_offset(m_text_tran_in_object.get_offset()); + + if (count >= 2) { + m_show_warning_old_tran = true; + } + if (m_is_version1_10_xoy) { + auto rotate_tran = Geometry::assemble_transform(Vec3d::Zero(), {0.5 * M_PI, 0.0, 0.0}); + m_load_text_tran_in_object.set_from_transform(m_load_text_tran_in_object.get_matrix() * rotate_tran); + m_load_text_tran_in_object.set_offset(m_load_text_tran_in_object.get_offset() + Vec3d(0, 1.65, 0)); // for size 16 + return; + } + //go on + if (has_rotation && m_show_warning_old_tran == false) { + m_show_warning_lost_rotate = true; + m_need_fix = false; + use_fix_normal_position(); + } + //not need set set_rotation//has_rotation + if (has_scale) { + expert_text_tran_in_object.set_scaling_factor(scaling_factor); + } + if (has_mirror) { + expert_text_tran_in_object.set_mirror(mirror); + } + m_load_text_tran_in_object.set_from_transform(expert_text_tran_in_object.get_matrix()); + if (m_is_version1_9_xoz) { + m_load_text_tran_in_object.set_offset(m_load_text_tran_in_object.get_offset()); + } + } + } } else if (m_state == EState::Off) { - m_show_warning = false; - m_show_text_normal_error = false; - m_edit_text_again = false; + ImGui::FocusWindow(nullptr);//exit cursor + m_reedit_text = false; + m_fix_old_tran_flag = false; + close_warning_flag_after_close_or_drag(); reset_text_info(); delete_temp_preview_text_volume(); m_parent.use_slope(false); @@ -408,6 +467,47 @@ void GLGizmoText::on_set_state() } } +void GLGizmoText::check_text_type(bool is_surface_text, bool is_keep_horizontal) { + if (is_surface_text && is_keep_horizontal) { + m_text_type = TextType::SURFACE_HORIZONAL; + } else if (is_surface_text) { + m_text_type = TextType::SURFACE; + } else if (is_keep_horizontal) { + m_text_type = TextType::HORIZONAL; + } else { + m_text_type = TextType::HORIZONAL; + } +} + +void GLGizmoText::generate_text_tran_in_world(const Vec3d &text_normal_in_world, const Vec3d &text_position_in_world,float rotate_degree, Geometry::Transformation &cur_tran) +{ + Vec3d temp_normal = text_normal_in_world; + Vec3d cut_plane_in_world = Vec3d::UnitY(); + double epson = 1e-6; + if (!(abs(temp_normal.x()) <= epson && abs(temp_normal.y()) <= epson && abs(temp_normal.z()) > epson)) { // temp_normal != Vec3d::UnitZ() + Vec3d v_plane = temp_normal.cross(Vec3d::UnitZ()); + cut_plane_in_world = v_plane.cross(temp_normal); + } + Vec3d z_dir = text_normal_in_world; + Vec3d y_dir = cut_plane_in_world; + Vec3d x_dir_world = y_dir.cross(z_dir); + if (m_text_type == TextType::SURFACE_HORIZONAL && text_normal_in_world != Vec3d::UnitZ()) { + y_dir = Vec3d::UnitZ(); + x_dir_world = y_dir.cross(z_dir); + z_dir = x_dir_world.cross(y_dir); + } + auto temp_tran = Geometry::generate_transform(x_dir_world, y_dir, z_dir, text_position_in_world); + Geometry::Transformation rotate_trans; + rotate_trans.set_rotation(Vec3d(0, 0, Geometry::deg2rad(rotate_degree))); // m_rotate_angle + cur_tran.set_matrix(temp_tran.get_matrix() * rotate_trans.get_matrix()); +} + +void GLGizmoText::use_fix_normal_position() +{ + m_text_normal_in_world = m_fix_text_normal_in_world; + m_text_position_in_world = m_fix_text_position_in_world; +} + void GLGizmoText::load_init_text() { Plater *plater = wxGetApp().plater(); @@ -420,33 +520,62 @@ void GLGizmoText::load_init_text() m_last_text_mv = model_volume; return; } - m_need_fix = false; if (plater) { plater->take_snapshot("enter Text"); } + auto box = model_volume->get_mesh_shared_ptr()->bounding_box(); + auto valid_z = text_info.m_embeded_depth + text_info.m_thickness; + auto text_height = get_text_height(text_info.m_text); + m_really_use_surface_calc = true; + int index = -1; + for (size_t i = 0; i < 3; i++) { + if (abs(box.size()[i] - valid_z) < 0.1) { + m_really_use_surface_calc = false; + index = i; + break; + } + } + if (abs(box.size()[1] - text_height) > 0.1) { + m_fix_old_tran_flag = true; + } m_last_text_mv = model_volume; load_from_text_info(text_info); - m_edit_text_again = true; m_text_volume_tran = model_volume->get_matrix(); m_text_tran_in_object.set_matrix(m_text_volume_tran); int temp_object_idx; auto mo = m_parent.get_selection().get_selected_single_object(temp_object_idx); const ModelInstance *mi = mo->instances[m_parent.get_selection().get_instance_idx()]; - auto world_tran = mi->get_transformation().get_matrix() * m_text_volume_tran; + m_object_cs_to_world_tran = mi->get_transformation().get_matrix(); + auto world_tran = m_object_cs_to_world_tran * m_text_volume_tran; m_text_tran_in_world.set_matrix(world_tran); m_text_position_in_world = m_text_tran_in_world.get_offset(); - m_text_normal_in_world = -m_text_tran_in_world.get_matrix().linear().col(1).cast(); + m_text_normal_in_world = m_text_tran_in_world.get_matrix().linear().col(2).cast(); { TriangleMesh text_attach_mesh(mo->volumes[m_rr.mesh_id]->mesh()); text_attach_mesh.transform(mo->volumes[m_rr.mesh_id]->get_matrix()); MeshRaycaster temp_ray_caster(text_attach_mesh); - Vec3f local_center = m_text_tran_in_object.get_offset().cast(); + Vec3f local_center = m_text_tran_in_object.get_offset().cast();//(m_text_tran_in_object.get_matrix() * box.center()).cast(); // Vec3f temp_normal; Vec3f closest_pt = temp_ray_caster.get_closest_point(local_center, &temp_normal); - m_fix_text_position_in_world = mi->get_transformation().get_matrix() * closest_pt.cast(); + m_fix_text_position_in_world = m_object_cs_to_world_tran * closest_pt.cast(); m_fix_text_normal_in_world = (mi->get_transformation().get_matrix_no_offset().cast() * temp_normal).normalized(); - if ((m_fix_text_position_in_world - m_text_position_in_world).norm() > 0.1) { - m_need_fix = true; + int face_id; + Vec3f direction = m_text_tran_in_world.get_matrix().linear().col(2).cast(); + if (index == 2 && abs(box.size()[1] - text_height) < 0.1) { + m_is_version1_9_xoz = true; + m_fix_old_tran_flag = true; + } + if (index == 1 && abs(box.size()[2] - text_height) < 0.1) {//for 1.10 version, xoy plane cut,just fix + m_is_version1_10_xoy = true; + direction = m_text_tran_in_world.get_matrix().linear().col(1).cast(); + if (!temp_ray_caster.get_closest_point_and_normal(local_center, direction, &closest_pt, &temp_normal, &face_id)) { + m_show_warning_error_mesh = true; + } + } + else if (!temp_ray_caster.get_closest_point_and_normal(local_center, -direction, &closest_pt, &temp_normal, &face_id)) { + if (!temp_ray_caster.get_closest_point_and_normal(local_center, direction, &closest_pt, &temp_normal, &face_id)) { + m_show_warning_error_mesh = true; + } } } // m_rr.mesh_id @@ -459,7 +588,6 @@ void GLGizmoText::load_init_text() void GLGizmoText::data_changed(bool is_serializing) { load_init_text(); - m_rr.normal = Vec3f::Zero(); } CommonGizmosDataID GLGizmoText::on_get_requirements() const @@ -691,6 +819,8 @@ void GLGizmoText::on_update(const UpdateData &data) if (closest_hit_mesh_id != -1) { m_rr = {mouse_pos, closest_hit_mesh_id, closest_hit, closest_normal};//on drag + m_need_fix = false; + close_warning_flag_after_close_or_drag(); m_need_update_text = true; } } @@ -822,10 +952,16 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit) std::string hit = "local hit x:" + formatFloat(m_rr.hit[0]) + " y:" + formatFloat(m_rr.hit[1]) + " z:" + formatFloat(m_rr.hit[2]); std::string normal = "normal x:" + formatFloat(m_rr.normal[0]) + " y:" + formatFloat(m_rr.normal[1]) + " z:" + formatFloat(m_rr.normal[2]); auto cut_dir = "cut_dir x:" + formatFloat(m_cut_plane_dir_in_world[0]) + " y:" + formatFloat(m_cut_plane_dir_in_world[1]) + " z:" + formatFloat(m_cut_plane_dir_in_world[2]); + auto fix_position_str = "fix position:" + formatFloat(m_fix_text_position_in_world[0]) + " y:" + formatFloat(m_fix_text_position_in_world[1]) + + " z:" + formatFloat(m_fix_text_position_in_world[2]); + auto fix_normal_str = "fix normal:" + formatFloat(m_fix_text_normal_in_world[0]) + " y:" + formatFloat(m_fix_text_normal_in_world[1]) + + " z:" + formatFloat(m_fix_text_normal_in_world[2]); m_imgui->text(world_hit); m_imgui->text(hit); m_imgui->text(normal); m_imgui->text(cut_dir); + m_imgui->text(fix_position_str); + m_imgui->text(fix_normal_str); #endif float space_size = m_imgui->get_style_scaling() * 8; float font_cap = m_imgui->calc_text_size(_L("Font")).x; @@ -985,14 +1121,37 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit) if (text.empty() && m_is_modify) { m_imgui->warning_text(_L("Warning:Input cannot be empty!")); } - if (m_show_warning) { + if (m_show_warning_text_create_fail) { m_imgui->warning_text(_L("Warning:create text fail.")); } if (m_show_text_normal_error) { m_imgui->warning_text(_L("Warning:text normal is error.")); } + if (m_show_text_normal_reset_tip) { + m_imgui->warning_text(_L("Warning:text normal has been reset.")); + } + if (m_show_warning_regenerated) { + m_imgui->warning_text(_L("Warning:Because current text does indeed use surround algorithm,\nif continue to edit, text has to regenerated according to new location.")); + } + if (m_show_warning_old_tran) { + m_imgui->warning_text(_L("Warning:old matrix has at least two parameters: mirroring, scaling, and rotation. \nIf you continue editing, it may not be correct. \nPlease dragging text or cancel using current pose, \nsave and reedit again.")); + } + if (m_show_warning_error_mesh) { + m_imgui->warning_text(_L("Error:Detecting an incorrect mesh id or an unknown error, \nregenerating text may result in incorrect outcomes.\nPlease drag text,save it then reedit it again.")); + } + if (m_show_warning_lost_rotate) { + m_imgui->warning_text(_L("Warning:Due to functional upgrade, rotation information \ncannot be restored. Please drag or modify text,\n save it and reedit it will ok.")); + } ImGui::Separator(); - + if (m_need_fix && m_text_type > TextType::HORIZONAL) { + ImGui::AlignTextToFramePadding(); + ImGui::SameLine(caption_size); + ImGui::PushItemWidth(list_width); + ImGui::AlignTextToFramePadding(); + if (m_imgui->qdt_checkbox(_L("Use opened text pose"), m_use_current_pose)) { + m_need_update_text = true; + } + } ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6.0f, 10.0f)); float get_cur_y = ImGui::GetContentRegionMax().y + ImGui::GetFrameHeight() + y; show_tooltip_information(x, get_cur_y); @@ -1002,17 +1161,20 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit) ImGui::SameLine(caption_size); ImGui::AlignTextToFramePadding(); - if (m_imgui->qdt_checkbox(_L("Surface"), m_is_surface_text)){ + auto is_surface_text = m_text_type == TextType::SURFACE || m_text_type == TextType::SURFACE_HORIZONAL; + if (m_imgui->qdt_checkbox(_L("Surface"), is_surface_text)) { m_need_update_text = true; } - ImGui::SameLine(); ImGui::AlignTextToFramePadding(); - auto keep_horizontal = !m_is_surface_text; - if (m_imgui->qdt_checkbox(_L("Horizontal text"), keep_horizontal)) { + auto is_keep_horizontal = m_text_type == TextType::HORIZONAL || m_text_type == TextType::SURFACE_HORIZONAL; + if (m_imgui->qdt_checkbox(_L("Horizontal text"), is_keep_horizontal)) { m_need_update_text = true; + if (is_surface_text && is_keep_horizontal == false) { + update_text_normal_in_world(); + } } - m_is_surface_text = !keep_horizontal; + check_text_type(is_surface_text, is_keep_horizontal); //ImGui::SameLine(); //ImGui::AlignTextToFramePadding(); @@ -1080,7 +1242,7 @@ void GLGizmoText::reset_text_info() m_embeded_depth = 0.f; m_rotate_angle = 0; m_text_gap = 0.f; - m_is_surface_text = true; + m_text_type = TextType::SURFACE; m_rr = RaycastResult(); m_is_modify = false; @@ -1127,6 +1289,66 @@ void GLGizmoText::update_font_status() } } +float GLGizmoText::get_text_height(const std::string &text) +{ + std::wstring_convert> str_cnv; + std::wstring ws = boost::nowide::widen(text); + std::vector alphas; + for (auto w : ws) { + alphas.push_back(str_cnv.to_bytes(w)); + } + auto texts = alphas ; + float max_height = 0.f; + for (int i = 0; i < texts.size(); ++i) { + std::string alpha; + if (texts[i] == " ") { + alpha = "i"; + } else { + alpha = texts[i]; + } + TextResult text_result; + load_text_shape(alpha.c_str(), m_font_name.c_str(), m_font_size, m_thickness + m_embeded_depth, m_bold, m_italic, text_result); + auto height = text_result.text_mesh.bounding_box().size()[1]; + if (max_height < height ){ + max_height = height; + } + } + return max_height; +} + +void GLGizmoText::close_warning_flag_after_close_or_drag() +{ + m_show_warning_text_create_fail = false; + m_show_warning_regenerated = false; + m_show_warning_error_mesh = false; + m_show_warning_old_tran = false; + m_show_warning_lost_rotate = false; + m_show_text_normal_error = false; + m_show_text_normal_reset_tip = false; + m_show_warning_lost_rotate = false; + m_is_version1_10_xoy = false; + m_is_version1_9_xoz = false; +} + +void GLGizmoText::update_text_normal_in_world() +{ + int temp_object_idx; + auto mo = m_parent.get_selection().get_selected_single_object(temp_object_idx); + if (mo && m_rr.mesh_id >= 0) { + const ModelInstance *mi = mo->instances[m_parent.get_selection().get_instance_idx()]; + TriangleMesh text_attach_mesh(mo->volumes[m_rr.mesh_id]->mesh()); + text_attach_mesh.transform(mo->volumes[m_rr.mesh_id]->get_matrix()); + MeshRaycaster temp_ray_caster(text_attach_mesh); + Vec3f local_center = m_text_tran_in_object.get_offset().cast(); //(m_text_tran_in_object.get_matrix() * box.center()).cast(); // + Vec3f temp_normal; + Vec3f closest_pt = temp_ray_caster.get_closest_point(local_center, &temp_normal); + m_text_normal_in_world = (mi->get_transformation().get_matrix_no_offset().cast() * temp_normal).normalized(); + } + else { + BOOST_LOG_TRIVIAL(info) << boost::format("error: update_text_normal_in_world"); + } +} + bool GLGizmoText::update_text_positions(const std::vector& texts) { std::vector text_lengths; @@ -1171,9 +1393,10 @@ bool GLGizmoText::update_text_positions(const std::vector& texts) // mouse_position_world may is error after user modified if (m_need_fix) { - m_need_fix = false; - m_text_position_in_world = m_fix_text_position_in_world; - m_text_normal_in_world = m_fix_text_normal_in_world; + if (m_text_normal_in_world.norm() < 0.1) { + m_show_text_normal_reset_tip = true; + } + use_fix_normal_position(); } if (m_text_normal_in_world.norm() < 0.1) { m_show_text_normal_error = true; @@ -1206,23 +1429,10 @@ bool GLGizmoText::update_text_positions(const std::vector& texts) ModelVolume* volume = mo->volumes[volume_index]; - Vec3d temp_normal = m_text_normal_in_world.cast(); - Vec3d cut_plane_in_world = Vec3d::UnitY(); - double epson = 1e-6; - if (!(abs(temp_normal.x()) <= epson && abs(temp_normal.y()) <= epson && abs(temp_normal.z()) > epson)) { // temp_normal != Vec3d::UnitZ() - Vec3d v_plane = temp_normal.cross(Vec3d::UnitZ()); - cut_plane_in_world = v_plane.cross(temp_normal); - } + generate_text_tran_in_world(m_text_normal_in_world.cast(), m_text_position_in_world, m_rotate_angle, m_text_tran_in_world); - m_cut_plane_dir_in_world = cut_plane_in_world; - - auto y_dir = -m_text_normal_in_world.cast(); - Vec3d x_dir_world = y_dir.cross(m_cut_plane_dir_in_world); - m_text_tran_in_world = Geometry::generate_transform(x_dir_world, y_dir, m_cut_plane_dir_in_world.cast(), m_text_position_in_world); - Geometry::Transformation rotate_trans; - rotate_trans.set_rotation(Vec3d(0, Geometry::deg2rad(m_rotate_angle), 0)); // m_rotate_angle - m_text_tran_in_world.set_matrix(m_text_tran_in_world.get_matrix() * rotate_trans.get_matrix()); - m_cut_plane_dir_in_world = m_text_tran_in_world.get_matrix().linear().col(2); + m_cut_plane_dir_in_world = m_text_tran_in_world.get_matrix().linear().col(1); + m_text_normal_in_world = m_text_tran_in_world.get_matrix().linear().col(2).cast(); // generate clip cs at click pos auto text_position_in_object = mi->get_transformation().get_matrix().inverse() * m_text_position_in_world.cast(); @@ -1231,8 +1441,8 @@ bool GLGizmoText::update_text_positions(const std::vector& texts) auto text_tran_in_object = mi->get_transformation().get_matrix().inverse() * m_text_tran_in_world.get_matrix(); // Geometry::generate_transform(cs_x_dir, cs_y_dir, cs_z_dir, text_position_in_object); // todo modify by m_text_tran_in_world m_text_tran_in_object.set_matrix(text_tran_in_object); m_text_cs_to_world_tran = mi->get_transformation().get_matrix() * m_text_tran_in_object.get_matrix(); - - if (!m_is_surface_text) { + auto rotate_tran = Geometry::assemble_transform(Vec3d::Zero(), {-0.5 * M_PI, 0.0, 0.0}); + if (m_text_type == TextType::HORIZONAL || (m_need_fix && m_use_current_pose)) { m_position_points.resize(text_num); m_normal_points.resize(text_num); @@ -1283,7 +1493,8 @@ bool GLGizmoText::update_text_positions(const std::vector& texts) } MeshSlicingParams slicing_params; - slicing_params.trafo = m_text_tran_in_object.get_matrix().inverse(); + auto cut_tran = (m_text_tran_in_object.get_matrix() * rotate_tran); + slicing_params.trafo = cut_tran.inverse(); // for debug // its_write_obj(slice_meshs.its, "D:/debug_files/mesh.obj"); // generate polygons @@ -1328,7 +1539,7 @@ bool GLGizmoText::update_text_positions(const std::vector& texts) m_cut_points_in_world.clear(); m_cut_points_in_world.reserve(hit_ploy.points.size()); for (int i = 0; i < hit_ploy.points.size(); ++i) { - m_cut_points_in_world.emplace_back(m_text_cs_to_world_tran * Vec3d(unscale_(hit_ploy.points[i].x()), unscale_(hit_ploy.points[i].y()), 0)); + m_cut_points_in_world.emplace_back(m_text_cs_to_world_tran * rotate_tran * Vec3d(unscale_(hit_ploy.points[i].x()), unscale_(hit_ploy.points[i].y()), 0)); } Polygon_3D new_polygon(m_cut_points_in_world); @@ -1653,6 +1864,14 @@ void GLGizmoText::generate_text_volume(bool is_temp) } if (mesh.empty()) return; + auto center = mesh.bounding_box().center(); + if (abs(mesh.bounding_box().size()[2] - (m_embeded_depth + m_thickness)) < 0.01) { + mesh.translate(Vec3f(-center.x(), -center.y(), 0)); // align horizontal and vertical center + } + else { + mesh.translate(Vec3f(0, -center.y(), 0)); // align vertical center + } + Plater *plater = wxGetApp().plater(); if (!plater) return; @@ -1670,9 +1889,16 @@ void GLGizmoText::generate_text_volume(bool is_temp) if (!is_temp) { plater->take_snapshot("Modify Text"); } + text_info.m_font_version = CUR_FONT_VERSION; ModelVolume * model_volume = model_object->volumes[m_volume_idx]; ModelVolume * new_model_volume = model_object->add_volume(std::move(mesh),false); - new_model_volume->set_transformation(m_text_tran_in_object.get_matrix()); + if (m_need_fix && // m_reedit_text//m_need_fix + (m_text_type == TextType::HORIZONAL || (m_text_type > TextType::HORIZONAL && m_use_current_pose))) { + new_model_volume->set_transformation(m_load_text_tran_in_object.get_matrix()); + } + else { + new_model_volume->set_transformation(m_text_tran_in_object.get_matrix()); + } new_model_volume->set_text_info(text_info); new_model_volume->name = model_volume->name; new_model_volume->set_type(model_volume->type()); @@ -1689,15 +1915,16 @@ void GLGizmoText::generate_text_volume(bool is_temp) cur_volume_id = obj_list->add_text_part(mesh, "text_shape", text_info, m_text_tran_in_object.get_matrix(), is_temp); m_preview_text_volume_id = is_temp ? cur_volume_id : -1; if (cur_volume_id >= 0) { - m_show_warning = false; + m_show_warning_text_create_fail = false; ModelVolume *text_model_volume = model_object->volumes[cur_volume_id]; m_text_volume_tran = text_model_volume->get_matrix(); } else { - m_show_warning = true; + m_show_warning_text_create_fail = true; } } - m_need_update_text = false; + m_need_update_text = false; + m_show_warning_lost_rotate = false; } void GLGizmoText::delete_temp_preview_text_volume() @@ -1722,6 +1949,7 @@ TextInfo GLGizmoText::get_text_info() { TextInfo text_info; text_info.m_font_name = m_font_name; + text_info.m_font_version = m_font_version; text_info.m_font_size = m_font_size; text_info.m_curr_font_idx = m_curr_font_idx; text_info.m_bold = m_bold; @@ -1732,13 +1960,15 @@ TextInfo GLGizmoText::get_text_info() text_info.m_embeded_depth = m_embeded_depth; text_info.m_rotate_angle = m_rotate_angle; text_info.m_text_gap = m_text_gap; - text_info.m_is_surface_text = m_is_surface_text; + text_info.m_is_surface_text = m_text_type == TextType::SURFACE || m_text_type == TextType::SURFACE_HORIZONAL; + text_info.m_keep_horizontal = m_text_type == TextType::HORIZONAL || m_text_type == TextType::SURFACE_HORIZONAL; return text_info; } void GLGizmoText::load_from_text_info(const TextInfo &text_info) { m_font_name = text_info.m_font_name; + m_font_version = text_info.m_font_version; m_font_size = text_info.m_font_size; // from other user's computer may exist case:font library size is different if (text_info.m_curr_font_idx < m_font_names.size()) { @@ -1755,7 +1985,7 @@ void GLGizmoText::load_from_text_info(const TextInfo &text_info) m_embeded_depth = text_info.m_embeded_depth; m_rotate_angle = text_info.m_rotate_angle; m_text_gap = text_info.m_text_gap; - m_is_surface_text = text_info.m_is_surface_text; + check_text_type(text_info.m_is_surface_text, text_info.m_keep_horizontal); } } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoText.hpp b/src/slic3r/GUI/Gizmos/GLGizmoText.hpp index 630613c..f4d17b8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoText.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoText.hpp @@ -17,12 +17,14 @@ namespace GUI { //#define DEBUG_TEXT_VALUE enum class SLAGizmoEventType : unsigned char; +const std::string CUR_FONT_VERSION = "1.0"; class GLGizmoText : public GLGizmoBase { private: std::vector m_avail_font_names; char m_text[1024] = { 0 }; std::string m_font_name; + std::string m_font_version = CUR_FONT_VERSION; float m_font_size = 16.f; const float m_font_size_min = 3.f; const float m_font_size_max = 1000.f; @@ -36,7 +38,14 @@ private: const float m_embeded_depth_max = 1000.f; float m_rotate_angle = 0; float m_text_gap = 0.f; - bool m_is_surface_text = false; + enum TextType { + HORIZONAL, + SURFACE, + SURFACE_HORIZONAL + }; + TextType m_text_type{TextType ::SURFACE}; + bool m_really_use_surface_calc = false; + bool m_use_current_pose = true; mutable RaycastResult m_rr; float m_combo_height = 0.0f; @@ -66,12 +75,19 @@ private: std::mutex m_mutex; std::thread m_thread; - bool m_edit_text_again = false; bool m_is_modify = false; bool m_need_update_text = false; - bool m_show_warning = false; + bool m_reedit_text = false; + bool m_show_warning_text_create_fail = false; bool m_show_text_normal_error = false; - + bool m_show_text_normal_reset_tip = false; + bool m_show_warning_regenerated = false; + bool m_show_warning_old_tran = false; + bool m_show_warning_error_mesh = false; + bool m_show_warning_lost_rotate = false; + bool m_fix_old_tran_flag = false; + bool m_is_version1_10_xoy = false; + bool m_is_version1_9_xoz = false; int m_object_idx = -1; int m_volume_idx = -1; @@ -83,9 +99,10 @@ private: Vec3f m_text_normal_in_world = Vec3f::Zero(); Geometry::Transformation m_text_tran_in_object; Geometry::Transformation m_text_tran_in_world; + Geometry::Transformation m_load_text_tran_in_object; Geometry::Transformation m_model_object_in_world_tran; Transform3d m_text_cs_to_world_tran; - + Transform3d m_object_cs_to_world_tran; Vec3d m_cut_plane_dir_in_world = Vec3d::UnitZ(); std::vector m_position_points; @@ -136,10 +153,16 @@ protected: void show_tooltip_information(float x, float y); private: + void check_text_type(bool is_surface_text,bool is_keep_horizontal); + void generate_text_tran_in_world(const Vec3d &text_normal_in_world, const Vec3d &text_position_in_world, float rotate_degree, Geometry::Transformation &tran); + void use_fix_normal_position(); void load_init_text(); void update_text_pos_normal(); void update_font_status(); void reset_text_info(); + float get_text_height(const std::string &text); + void close_warning_flag_after_close_or_drag(); + void update_text_normal_in_world(); bool update_text_positions(const std::vector& texts); TriangleMesh get_text_mesh(const char* text_str, const Vec3d &position, const Vec3d &normal, const Vec3d &text_up_dir); diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 9547117..66983d7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -958,7 +958,12 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) m_gizmos[m_current]->on_mouse(evt); } // mouse anywhere - if (evt.Moving()) { + if (evt.LeftDClick()) { + if (m_current == Text || m_current == Svg) { + return false; + } + } + else if (evt.Moving()) { m_tooltip = update_hover_state(mouse_pos); if (m_current == MmuSegmentation || m_current == FdmSupports || m_current == Text || m_current == BrimEars || m_current == Svg) // QDS diff --git a/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp b/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp index 16e5722..6af5e0d 100644 --- a/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp +++ b/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp @@ -90,12 +90,19 @@ void GizmoObjectManipulation::update_ui_from_settings() update_buffered_value(); } } +void delete_negative_sign(Vec3d& value) { + for (size_t i = 0; i < value.size(); i++) { + if (abs(value[i]) < 0.001) + value[i] = 0.f; + } +} -void GizmoObjectManipulation::update_settings_value(const Selection& selection) +void GizmoObjectManipulation::update_settings_value(const Selection &selection) { m_new_move_label_string = L("Position"); m_new_rotate_label_string = L("Rotate (relative)"); m_new_rotation = Vec3d::Zero(); + m_new_absolute_rotation = Vec3d::Zero(); m_new_scale_label_string = L("Scale ratios"); ObjectList* obj_list = wxGetApp().obj_list(); @@ -103,7 +110,9 @@ void GizmoObjectManipulation::update_settings_value(const Selection& selection) // all volumes in the selection belongs to the same instance, any of them contains the needed instance data, so we take the first one const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); m_new_position = volume->get_instance_offset(); - + auto rotation = volume->get_instance_transformation().get_rotation_by_quaternion(); + m_new_absolute_rotation = rotation * (180. / M_PI); + delete_negative_sign(m_new_absolute_rotation); if (is_world_coordinates()) {//for move and rotate m_new_size = selection.get_bounding_box_in_current_reference_system().first.size(); m_unscale_size = selection.get_unscaled_instance_bounding_box().size(); @@ -131,6 +140,9 @@ void GizmoObjectManipulation::update_settings_value(const Selection& selection) m_new_title_string = L("Object Operations"); } else if (selection.is_single_volume_or_modifier()) { const GLVolume *volume = selection.get_first_volume(); + auto rotation = volume->get_volume_transformation().get_rotation_by_quaternion(); + m_new_absolute_rotation = rotation * (180. / M_PI); + delete_negative_sign(m_new_absolute_rotation); if (is_world_coordinates()) {//for move and rotate const Geometry::Transformation trafo(volume->world_matrix()); const Vec3d &offset = trafo.get_offset(); @@ -179,7 +191,7 @@ void GizmoObjectManipulation::update_buffered_value() m_buffered_position = this->m_new_position; m_buffered_rotation = this->m_new_rotation; - + m_buffered_absolute_rotation = this->m_new_absolute_rotation; m_buffered_scale = this->m_new_scale; if (this->m_imperial_units) @@ -205,6 +217,7 @@ void GizmoObjectManipulation::update_if_dirty() }; update_label(m_cache.move_label_string, m_new_move_label_string); update_label(m_cache.rotate_label_string, m_new_rotate_label_string); + update_label(m_cache.rotate_label_string, m_new_rotate_label_string); update_label(m_cache.scale_label_string, m_new_scale_label_string); enum ManipulationEditorKey @@ -233,6 +246,7 @@ void GizmoObjectManipulation::update_if_dirty() update(m_cache.scale, m_cache.scale_rounded, m_new_scale); update(m_cache.size, m_cache.size_rounded, m_new_size); update(m_cache.rotation, m_cache.rotation_rounded, m_new_rotation); + update(m_cache.absolute_rotation, m_cache.absolute_rotation_rounded, m_new_absolute_rotation); } update_reset_buttons_visibility(); @@ -273,6 +287,7 @@ void GizmoObjectManipulation::reset_settings_value() { m_new_position = Vec3d::Zero(); m_new_rotation = Vec3d::Zero(); + m_new_absolute_rotation = Vec3d::Zero(); m_new_scale = Vec3d::Ones() * 100.; m_new_size = Vec3d::Zero(); m_new_enabled = false; @@ -344,6 +359,34 @@ void GizmoObjectManipulation::change_rotation_value(int axis, double value) this->UpdateAndShow(true); } +void GizmoObjectManipulation::change_absolute_rotation_value(int axis, double value) { + if (std::abs(m_cache.absolute_rotation_rounded(axis) - value) < EPSILON) + return; + + Vec3d absolute_rotation = m_cache.absolute_rotation; + absolute_rotation(axis) = value; + + Selection &selection = m_glcanvas.get_selection(); + TransformationType transformation_type; + transformation_type.set_relative(); + if (selection.is_single_full_instance()) + transformation_type.set_independent(); + if (is_local_coordinates()) + transformation_type.set_local(); + if (is_instance_coordinates()) + transformation_type.set_instance(); + + selection.setup_cache(); + auto diff_rotation = transformation_type.absolute() ? absolute_rotation : absolute_rotation - m_cache.absolute_rotation; + selection.rotate((M_PI / 180.0) * diff_rotation, transformation_type); + wxGetApp().plater()->take_snapshot("set absolute orientation", UndoRedo::SnapshotType::GizmoAction); + m_glcanvas.do_rotate(""); + + m_cache.absolute_rotation = absolute_rotation; + m_cache.absolute_rotation_rounded(axis) = DBL_MAX; + this->UpdateAndShow(true); +} + void GizmoObjectManipulation::change_scale_value(int axis, double value) { if (value <= 0.0) @@ -440,6 +483,8 @@ void GizmoObjectManipulation::on_change(const std::string &opt_key, int axis, do change_position_value(axis, new_value); else if (opt_key == "rotation") change_rotation_value(axis, new_value); + else if (opt_key == "absolute_rotation") + change_absolute_rotation_value(axis, new_value); else if (opt_key == "scale") change_scale_value(axis, new_value); else if (opt_key == "size") @@ -577,10 +622,11 @@ void GizmoObjectManipulation::set_coordinates_type(ECoordinatesType type) } -static const char* label_values[2][3] = { -{ "##position_x", "##position_y", "##position_z"}, -{ "##rotation_x", "##rotation_y", "##rotation_z"} -}; +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"} + }; static const char* label_scale_values[2][3] = { { "##scale_x", "##scale_y", "##scale_z"}, @@ -799,7 +845,6 @@ void GizmoObjectManipulation::set_init_rotation(const Geometry::Transformation & Vec3d display_position = m_buffered_position; // Rotation - Vec3d rotation = this->m_buffered_rotation; float unit_size = imgui_wrapper->calc_text_size(MAX_SIZE).x + space_size; int index = 1; int index_unit = 1; @@ -949,7 +994,7 @@ void GizmoObjectManipulation::do_render_rotate_window(ImGuiWrapper *imgui_wrappe Vec3d display_position = m_buffered_position; // Rotation Vec3d rotation = this->m_buffered_rotation; - + Vec3d absolute_rotation = this->m_buffered_absolute_rotation; float unit_size = imgui_wrapper->calc_text_size(MAX_SIZE).x + space_size; int index = 1; int index_unit = 1; @@ -967,33 +1012,41 @@ void GizmoObjectManipulation::do_render_rotate_window(ImGuiWrapper *imgui_wrappe ImGui::SameLine(caption_max + (++index_unit) * unit_size + (++index) * space_size); ImGui::PushItemWidth(unit_size); ImGui::TextAlignCenter("Z"); - if (m_show_reset_0_rotation) { - ImGui::SameLine(caption_max + 3 * unit_size + 4 * space_size + end_text_size); - if (reset_zero_button(imgui_wrapper, caption_max, unit_size, space_size, end_text_size)) { reset_rotation_value(false); } - if (ImGui::IsItemHovered()) { - float tooltip_size = imgui_wrapper->calc_text_size(_L("Reset current rotation to real zeros.")).x + 3 * space_size; - imgui_wrapper->tooltip(_L("Reset current rotation to real zeros."), tooltip_size); - } - } + index = 1; index_unit = 1; // ImGui::PushItemWidth(unit_size * 2); + bool is_relative_input = false; ImGui::AlignTextToFramePadding(); imgui_wrapper->text(_L("Rotate (relative)")); ImGui::SameLine(caption_max + index * space_size); ImGui::PushItemWidth(unit_size); - ImGui::QDTInputDouble(label_values[1][0], &rotation[0], 0.0f, 0.0f, "%.2f"); + if (ImGui::QDTInputDouble(label_values[1][0], &rotation[0], 0.0f, 0.0f, "%.2f")) { + is_relative_input = true; + } ImGui::SameLine(caption_max + unit_size + (++index) * space_size); ImGui::PushItemWidth(unit_size); - ImGui::QDTInputDouble(label_values[1][1], &rotation[1], 0.0f, 0.0f, "%.2f"); + if (ImGui::QDTInputDouble(label_values[1][1], &rotation[1], 0.0f, 0.0f, "%.2f")) { + is_relative_input = true; + } ImGui::SameLine(caption_max + (++index_unit) * unit_size + (++index) * space_size); ImGui::PushItemWidth(unit_size); - ImGui::QDTInputDouble(label_values[1][2], &rotation[2], 0.0f, 0.0f, "%.2f"); + if (ImGui::QDTInputDouble(label_values[1][2], &rotation[2], 0.0f, 0.0f, "%.2f")) { + is_relative_input = true; + } ImGui::SameLine(caption_max + (++index_unit) * unit_size + (++index) * space_size); imgui_wrapper->text(_L("°")); m_buffered_rotation = rotation; - update(current_active_id, "rotation", this->m_new_rotation, m_buffered_rotation); + if (is_relative_input) { + m_last_rotate_type = RotateType::Relative; + } + if (m_last_rotate_type == RotateType::Relative) { + bool is_valid = update(current_active_id, "rotation", this->m_new_rotation, m_buffered_rotation) >= 0; + if (is_valid) { + m_last_rotate_type = RotateType::None; + } + } if (m_show_clear_rotation) { ImGui::SameLine(caption_max + 3 * unit_size + 4 * space_size + end_text_size); @@ -1008,7 +1061,6 @@ void GizmoObjectManipulation::do_render_rotate_window(ImGuiWrapper *imgui_wrappe ImGui::SameLine(caption_max + 3 * unit_size + 5 * space_size + end_text_size); ImGui::InvisibleButton("", ImVec2(ImGui::GetFontSize(), ImGui::GetFontSize())); } - // send focus to m_glcanvas bool focued_on_text = false; for (int j = 0; j < 3; j++) { @@ -1019,7 +1071,61 @@ void GizmoObjectManipulation::do_render_rotate_window(ImGuiWrapper *imgui_wrappe break; } } - if (!focued_on_text) m_glcanvas.handle_sidebar_focus_event("", false); + + index = 1; + index_unit = 1; + ImGui::AlignTextToFramePadding(); + imgui_wrapper->text(_L("Rotate (absolute)")); + ImGui::SameLine(caption_max + index * space_size); + ImGui::PushItemWidth(unit_size); + bool is_absolute_input = false; + if (ImGui::QDTInputDouble(label_values[2][0], &absolute_rotation[0], 0.0f, 0.0f, "%.2f")) { + is_absolute_input = true; + } + ImGui::SameLine(caption_max + unit_size + (++index) * space_size); + ImGui::PushItemWidth(unit_size); + if (ImGui::QDTInputDouble(label_values[2][1], &absolute_rotation[1], 0.0f, 0.0f, "%.2f")) { + is_absolute_input = true; + } + ImGui::SameLine(caption_max + (++index_unit) * unit_size + (++index) * space_size); + ImGui::PushItemWidth(unit_size); + if (ImGui::QDTInputDouble(label_values[2][2], &absolute_rotation[2], 0.0f, 0.0f, "%.2f")) { + is_absolute_input = true; + } + ImGui::SameLine(caption_max + (++index_unit) * unit_size + (++index) * space_size); + imgui_wrapper->text(_L("°")); + m_buffered_absolute_rotation = absolute_rotation; + if (is_absolute_input) { + m_last_rotate_type = RotateType::Absolute; + } + if (m_last_rotate_type == RotateType::Absolute) { + bool is_valid = update(current_active_id, "absolute_rotation", this->m_new_absolute_rotation, m_buffered_absolute_rotation) >= 0; + if (is_valid) { + m_last_rotate_type = RotateType::None; + } + } + + if (m_show_reset_0_rotation) { + ImGui::SameLine(caption_max + 3 * unit_size + 4 * space_size + end_text_size); + if (reset_zero_button(imgui_wrapper, caption_max, unit_size, space_size, end_text_size)) { reset_rotation_value(false); } + if (ImGui::IsItemHovered()) { + float tooltip_size = imgui_wrapper->calc_text_size(_L("Reset current rotation to real zeros.")).x + 3 * space_size; + imgui_wrapper->tooltip(_L("Reset current rotation to real zeros."), tooltip_size); + } + } + // send focus to m_glcanvas + bool absolute_focued_on_text = false; + for (int j = 0; j < 3; j++) { + unsigned int id = ImGui::GetID(label_values[2][j]); + if (current_active_id == id) { + m_glcanvas.handle_sidebar_focus_event(label_values[2][j] + 2, true); + absolute_focued_on_text = true; + break; + } + } + if (!focued_on_text && !absolute_focued_on_text) + m_glcanvas.handle_sidebar_focus_event("", false); + float get_cur_y = ImGui::GetContentRegionMax().y + ImGui::GetFrameHeight() + y; float tip_caption_max = 0.f; float total_text_max = 0.f; diff --git a/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.hpp b/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.hpp index 7aa2be1..96e8aea 100644 --- a/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.hpp +++ b/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.hpp @@ -27,6 +27,8 @@ public: Vec3d position_rounded; Vec3d rotation; Vec3d rotation_rounded; + Vec3d absolute_rotation; + Vec3d absolute_rotation_rounded; Vec3d scale; Vec3d scale_rounded; Vec3d size; @@ -72,11 +74,13 @@ public: std::string m_new_unit_string; Vec3d m_new_position; Vec3d m_new_rotation; + Vec3d m_new_absolute_rotation; Vec3d m_new_scale; Vec3d m_new_size; Vec3d m_unscale_size; Vec3d m_buffered_position; Vec3d m_buffered_rotation; + Vec3d m_buffered_absolute_rotation; Vec3d m_buffered_scale; Vec3d m_buffered_size; Vec3d cs_center; @@ -89,6 +93,9 @@ public: bool m_show_clear_rotation { false }; bool m_show_clear_scale { false }; bool m_show_drop_to_bed { false }; + enum class RotateType { None, Relative, Absolute + }; + RotateType m_last_rotate_type{RotateType::None}; // 0:no input 1:relative 2:absolute protected: float last_move_input_window_width = 0.0f; @@ -151,6 +158,7 @@ private: // change values void change_position_value(int axis, double value); void change_rotation_value(int axis, double value); + void change_absolute_rotation_value(int axis, double value); void change_scale_value(int axis, double value); void change_size_value(int axis, double value); void do_scale(int axis, const Vec3d &scale) const; diff --git a/src/slic3r/GUI/Jobs/EmbossJob.cpp b/src/slic3r/GUI/Jobs/EmbossJob.cpp index 75a481c..f093884 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.cpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.cpp @@ -374,7 +374,11 @@ void CreateObjectJob::process(Ctl &ctl) if (m_input.base->shape.projection.use_surface) m_input.base->shape.projection.use_surface = false; // auto was_canceled = ::was_canceled(ctl, *m_input.base); - m_result = create_mesh(*m_input.base); + if (m_input.base->merge_shape || !m_input.base->text_lines.empty()) { // || m_input.base->shape.shapes_with_ids.size() > 20 + m_result = create_mesh(*m_input.base); + } else { + m_results = create_meshs(*m_input.base); + } // Create new object // calculate X,Y offset position for lay on platter in place of @@ -407,7 +411,7 @@ void CreateObjectJob::finalize(bool canceled, std::exception_ptr &eptr) { if (!_finalize(canceled, eptr, *m_input.base)) return; // only for sure - if (m_result.empty()) { + if (m_result.empty() && m_results.empty()) { create_message("Can't create empty object."); return; } @@ -425,13 +429,28 @@ void CreateObjectJob::finalize(bool canceled, std::exception_ptr &eptr) new_object->name = m_input.base->volume_name; new_object->add_instance(); // each object should have at list one instance - ModelVolume *new_volume = new_object->add_volume(std::move(m_result)); - // set a default extruder value, since user can't add it manually - new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); - - // write emboss data into volume - m_input.base->write(*new_volume); + if (!m_result.empty()) { + ModelVolume *new_volume = new_object->add_volume(std::move(m_result)); + // set a default extruder value, since user can't add it manually + new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); + // write emboss data into volume + m_input.base->write(*new_volume); + } else if (!m_results.empty()) { + int index = 0; + for (auto shape : m_input.base->shape.shapes_with_ids) { + if (shape.expoly.empty()) + continue; + ModelVolume *new_volume = new_object->add_volume(std::move(m_results[index])); + // set a default extruder value, since user can't add it manually + new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); + //donot write emboss data into volume + new_volume->name = new_object->name + "_" + std::to_string(index); + index++; + } + } else { + create_message("CreateObjectJob:unknown error."); + } // set transformation Slic3r::Geometry::Transformation tr(m_transformation); new_object->instances.front()->set_transformation(tr); @@ -632,6 +651,26 @@ TriangleMesh create_mesh(DataBase &input) return result; } +std::vector create_meshs(DataBase &input) +{ + std::vector meshs; + + // NOTE: SHAPE_SCALE is applied in ProjectZ + double scale = input.shape.scale; + double depth = input.shape.projection.depth / scale; + auto projectZ = std::make_unique(depth); + float offset = input.is_outside ? -SAFE_SURFACE_OFFSET : (SAFE_SURFACE_OFFSET - input.shape.projection.depth); + if (input.from_surface.has_value()) offset += *input.from_surface; + Transform3d tr = Eigen::Translation(0., 0., static_cast(offset)) * Eigen::Scaling(scale); + ProjectTransform project(std::move(projectZ), tr); + + for (auto shape : input.shape.shapes_with_ids) { + if (shape.expoly.empty()) continue; + meshs.emplace_back(TriangleMesh(polygons2model(shape.expoly, project))); + } + return meshs; +} + void create_volume( TriangleMesh &&mesh, const ObjectID &object_id, const ModelVolumeType type, const std::optional &trmat, const DataBase &data, unsigned char gizmo_type) { @@ -967,9 +1006,13 @@ bool start_update_volume(DataUpdate &&data, const ModelVolume &volume, const Sel return execute_job(std::move(job)); #endif // EXECUTE_UPDATE_ON_MAIN_THREAD } +bool is_merge_shape_before_create_object() { + return GUI::wxGetApp().app_config->get_bool("import_single_svg_and_split") ? false : true; +} bool start_create_object_job(const CreateVolumeParams &input, DataBasePtr emboss_data, const Vec2d &coor) { + emboss_data->merge_shape = input.merge_shape; const Pointfs & bed_shape = input.build_volume.printable_area(); DataCreateObject m_input{std::move(emboss_data), coor, input.camera, bed_shape, input.gizmo_type, input.angle}; @@ -1025,17 +1068,20 @@ bool start_create_volume_without_position(CreateVolumeParams &input, DataBasePtr const ModelObjectPtrs &objects = selection.get_model()->objects; // No selected object so create new object - if (selection.is_empty() || object_idx < 0 || static_cast(object_idx) >= objects.size()) + if (selection.is_empty() || object_idx < 0 || static_cast(object_idx) >= objects.size()){ // create Object on center of screen // when ray throw center of screen not hit bed it create object on center of bed + input.merge_shape = is_merge_shape_before_create_object(); return start_create_object_job(input, std::move(data), screen_center); - + } // create volume inside of selected object Vec2d coor; const Camera &camera = wxGetApp().plater()->get_camera(); input.gl_volume = find_closest(selection, screen_center, camera, objects, &coor); - if (input.gl_volume == nullptr) + if (input.gl_volume == nullptr) { + input.merge_shape = is_merge_shape_before_create_object(); return start_create_object_job(input, std::move(data), screen_center); + } else { return start_create_volume_on_surface_job(input, std::move(data), coor); } @@ -1097,6 +1143,7 @@ bool start_create_volume_on_surface_job(CreateVolumeParams &input, DataBasePtr d // object. After right click, object is selected and object_idx is set // also hit must exist. But there is options to add text by object list if (!hit.has_value()) { // modify by qds + input.merge_shape = is_merge_shape_before_create_object(); return start_create_object_job(input, std::move(data), mouse_pos); // return on_bad_state(std::move(data), object); } @@ -1114,9 +1161,11 @@ bool start_create_volume(CreateVolumeParams &input, DataBasePtr data, const Vec2 if (data == nullptr) return false; if (!check(input)) return false; - if (input.gl_volume == nullptr || !input.gl_volume->selected) + if (input.gl_volume == nullptr || !input.gl_volume->selected) { + input.merge_shape = is_merge_shape_before_create_object(); // object is not under mouse position soo create object on plater return start_create_object_job(input, std::move(data), mouse_pos); + } else { // modify by qds return start_create_volume_on_surface_job(input, std::move(data), mouse_pos); } diff --git a/src/slic3r/GUI/Jobs/EmbossJob.hpp b/src/slic3r/GUI/Jobs/EmbossJob.hpp index bb346d3..0732726 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.hpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.hpp @@ -69,6 +69,7 @@ public: std::shared_ptr> cancel; // shape to emboss EmbossShape shape; + bool merge_shape{true}; }; struct DataCreateVolumeUtil : public DataBase // modfiy bu qds //struct DataCreateVolume : public DataBase @@ -124,6 +125,7 @@ struct DataUpdate std::optional distance = {}; // Wanted additionl rotation around Z of new created volume std::optional angle = {}; + bool merge_shape{true}; }; struct DataCreateObject { @@ -251,6 +253,7 @@ class CreateObjectJob : public JobNew { DataCreateObject m_input; TriangleMesh m_result; + std::vector m_results; Transform3d m_transformation; public: @@ -318,6 +321,7 @@ static ExPolygons create_shape(DataBase &input); static TriangleMesh create_mesh_per_glyph(DataBase &input); static TriangleMesh try_create_mesh(DataBase &input); static TriangleMesh create_mesh(DataBase &input); +static std::vector create_meshs(DataBase &input); static indexed_triangle_set cut_surface_to_its(const ExPolygons &shapes, const Transform3d &tr, const SurfaceVolumeData::ModelSources &sources, DataBase &input); static TriangleMesh cut_per_glyph_surface(DataBase &input1, const SurfaceVolumeData &input2); static TriangleMesh cut_surface(DataBase &input1, const SurfaceVolumeData &input2); diff --git a/src/slic3r/GUI/Jobs/PrintJob.cpp b/src/slic3r/GUI/Jobs/PrintJob.cpp index ed26142..65305b8 100644 --- a/src/slic3r/GUI/Jobs/PrintJob.cpp +++ b/src/slic3r/GUI/Jobs/PrintJob.cpp @@ -21,6 +21,7 @@ static wxString file_over_size_str = _L("The print file exceeds the max static wxString print_canceled_str = _L("Task canceled."); static wxString send_print_failed_str = _L("Failed to send the print job. Please try again."); static wxString upload_ftp_failed_str = _L("Failed to upload file to ftp. Please try again."); +static wxString print_signed_str = _L("Your software is not signed, and some printing functions have been restricted. Please use the officially signed software version."); static wxString desc_network_error = _L("Check the current status of the qidi server by clicking on the link above."); static wxString desc_file_too_large = _L("The size of the print file is too large. Please adjust the file size and try again."); @@ -593,8 +594,10 @@ void PrintJob::process() if (result < 0) { curr_percent = -1; - - if (result == QIDI_NETWORK_ERR_PRINT_WR_FILE_NOT_EXIST || result == QIDI_NETWORK_ERR_PRINT_SP_FILE_NOT_EXIST) { + if (result == QIDI_NETOWRK_ERR_PRINT_SP_ENC_FLAG_NOT_READY) { + msg_text = _L("Retrieving printer information, please try again later."); + } + else if (result == QIDI_NETWORK_ERR_PRINT_WR_FILE_NOT_EXIST || result == QIDI_NETWORK_ERR_PRINT_SP_FILE_NOT_EXIST) { msg_text = file_is_not_exists_str; } else if (result == QIDI_NETWORK_ERR_PRINT_SP_FILE_OVER_SIZE || result == QIDI_NETWORK_ERR_PRINT_WR_FILE_OVER_SIZE) { msg_text = file_over_size_str; @@ -607,6 +610,8 @@ void PrintJob::process() } else if (result == QIDI_NETWORK_ERR_CANCELED) { msg_text = print_canceled_str; this->update_status(0, msg_text); + } else if (result == QIDI_NETWORK_SIGNED_ERROR) { + msg_text = print_signed_str; } else { msg_text = send_print_failed_str; } diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 746e4ad..7bdf1ad 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1720,6 +1720,8 @@ wxBoxSizer* MainFrame::create_side_tools() wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_SEND_TO_PRINTER)); else if (m_print_select == eSendToPrinterAll) wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_SEND_TO_PRINTER_ALL)); + else if (m_print_select == eSendMultiApp) + wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_SEND_MULTI_APP)); // y16 else if (m_print_select == ePrintMultiMachine) wxPostEvent(m_plater, SimpleEvent(EVT_GLTOOLBAR_PRINT_MULTI_MACHINE)); @@ -1885,6 +1887,22 @@ wxBoxSizer* MainFrame::create_side_tools() p->append_button(export_sliced_file_btn); // p->append_button(export_all_sliced_file_btn); + //y + // if (check_qdt_farm_client_installed()) { + // SideButton *send_to_multi_app_btn = new SideButton(p, _L("Send to Bambu Farm Manager Client"), ""); + // send_to_multi_app_btn->SetCornerRadius(0); + // p->append_button(send_to_multi_app_btn); + + // send_to_multi_app_btn->Bind(wxEVT_BUTTON, [this, p](wxCommandEvent &) { + // m_print_btn->SetLabel(_L("Send to BFMC")); + // m_print_select = eSendMultiApp; + // m_print_enable = get_enable_print_status(); + // m_print_btn->Enable(m_print_enable); + // this->Layout(); + // p->Dismiss(); + // }); + // } + if (enable_multi_machine) { SideButton* print_multi_machine_btn = new SideButton(p, _L("Send to Multi-device"), ""); print_multi_machine_btn->SetCornerRadius(0); @@ -1988,6 +2006,11 @@ bool MainFrame::get_enable_print_status() if(m_plater->only_gcode_mode()) enable = false; enable = enable && !is_all_plates; + }else if (m_print_select == eSendMultiApp) { + if (!current_plate->is_slice_result_ready_for_print()) { + enable = false; + } + enable = enable && !is_all_plates; } else if (m_print_select == eExportGcode) { @@ -2051,6 +2074,11 @@ bool MainFrame::get_enable_print_status() enable = false; } enable = enable && !is_all_plates; + }else if (m_print_select == eSendMultiApp) { + if (!current_plate->is_slice_result_ready_for_print()) { + enable = false; + } + enable = enable && !is_all_plates; } BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": m_print_select %1%, enable= %2% ")%m_print_select %enable; @@ -2518,7 +2546,10 @@ void MainFrame::init_menubar_as_editor() [this](){return can_add_models(); }, this); #else append_menu_item(import_menu, wxID_ANY, _L("Import 3MF/STL/STEP/SVG/OBJ/AMF") + dots + "\t" + ctrl + "I", _L("Load a model"), - [this](wxCommandEvent&) { if (m_plater) { m_plater->add_model(); } }, "", nullptr, + [this](wxCommandEvent &) { + if (m_plater) { m_plater->add_file(); } + }, + "", nullptr, [this](){return can_add_models(); }, this); #endif append_menu_item(import_menu, wxID_ANY, _L("Import Configs") + dots /*+ "\tCtrl+I"*/, _L("Load configs"), @@ -3996,6 +4027,27 @@ void MainFrame::show_sync_dialog() wxQueueEvent(this, evt); } +bool MainFrame::check_qdt_farm_client_installed() +{ +#ifdef WIN32 + HKEY hKey; + LONG result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Bambulab\\Bambu Farm Manager Client"), 0, KEY_READ, &hKey); + LONG result_backup = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("HKEY_CLASSES_ROOT\\bambu-farm-client\\shell\\open\\command"), 0, KEY_READ, &hKey); + + if (result == ERROR_SUCCESS || result_backup == ERROR_SUCCESS) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "Bambu Farm Manager Client found."; + RegCloseKey(hKey); + return true; + } else { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "Bambu Farm Manager Client Not found."; + return false; + } + +#else + return false; +#endif +} + void MainFrame::update_side_preset_ui() { // select last preset diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 01347b6..101a8d5 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -253,7 +253,8 @@ public: eUploadGcode = 7, eExportAllSlicedFile = 8, ePrintMultiMachine = 9, - eSendMultiGcode = 10 // y11 + eSendMultiGcode = 10, // y11 + eSendMultiApp = 11 }; void update_layout(); @@ -286,6 +287,7 @@ public: // Called from wxEVT_ACTIVATE, as wxEVT_CREATE was not reliable (bug in wxWidgets?). void register_win32_callbacks(); void init_menubar_as_editor(); + bool check_qdt_farm_client_installed(); void init_menubar_as_gcodeviewer(); void update_menubar(); // Open item in menu by menu and item name (in actual language) diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 7268be2..e098345 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -605,6 +605,19 @@ Vec3f MeshRaycaster::get_closest_point(const Vec3f& point, Vec3f* normal) const return closest_point.cast(); } +bool MeshRaycaster::get_closest_point_and_normal(const Vec3f &point, const Vec3f &direction, Vec3f *closest_point, Vec3f *normal, int *face_id) const +{ + auto hits = m_emesh.query_ray_hits(point.cast(), direction.cast().normalized()); + if (hits.empty()) + return false; // no intersection found + size_t hit_id = 0; + auto & hit = hits[hit_id]; + *closest_point = hit.position().cast(); + *normal = hit.normal().cast(); + if (face_id) { *face_id = hit.face(); } + return true; +} + int MeshRaycaster::get_closest_facet(const Vec3f &point) const { int facet_idx = 0; diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index 80b5e17..916380c 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -201,7 +201,7 @@ public: // normal* can be used to also get normal of the respective triangle. Vec3f get_closest_point(const Vec3f& point, Vec3f* normal = nullptr) const; - + bool get_closest_point_and_normal(const Vec3f &point, const Vec3f &direction, Vec3f *closest_point, Vec3f *normal, int *face_id = nullptr) const; // Given a point in mesh coords, the method returns the closest facet from mesh. int get_closest_facet(const Vec3f &point) const; @@ -221,7 +221,7 @@ public: /*PickRaycaster(TriangleMesh *mesh, const Transform3d &tran) : PickRaycaster(mesh) { set_transform(tran); }*/ - PickRaycaster(TriangleMesh *mesh, int _id) + PickRaycaster(TriangleMesh *mesh, int _id) { mesh_raycaster = std::make_shared(*mesh); m_id = _id; diff --git a/src/slic3r/GUI/Monitor.cpp b/src/slic3r/GUI/Monitor.cpp index 9d438f8..609bfa9 100644 --- a/src/slic3r/GUI/Monitor.cpp +++ b/src/slic3r/GUI/Monitor.cpp @@ -309,11 +309,13 @@ void MonitorPanel::select_machine(std::string machine_sn) set_default(); update_all(); - m_status_info_panel->last_cali_version.reset(); - MachineObject *obj_ = dev->get_selected_machine(); - if (obj_) + if (obj_) { + obj_->last_cali_version = -1; + obj_->reset_pa_cali_history_result(); + obj_->reset_pa_cali_result(); GUI::wxGetApp().sidebar().load_ams_list(obj_->dev_id, obj_); + } Layout(); } @@ -373,6 +375,8 @@ void MonitorPanel::update_all() ; } } + if (obj) + m_agent->install_device_cert(obj->dev_id, obj->is_lan_mode_printer()); if (obj) { wxGetApp().reset_to_active(); @@ -477,8 +481,6 @@ bool MonitorPanel::Show(bool show) if (obj && !obj->dev_id.empty()) { select_machine(obj->dev_id); - } else { - select_machine(""); } return wxPanel::Show(show); diff --git a/src/slic3r/GUI/Mouse3DController.cpp b/src/slic3r/GUI/Mouse3DController.cpp index b65f1b5..21d8dff 100644 --- a/src/slic3r/GUI/Mouse3DController.cpp +++ b/src/slic3r/GUI/Mouse3DController.cpp @@ -27,7 +27,7 @@ static const std::vector _3DCONNEXION_VENDORS = 0x256F // 3DCONNECTION = 9583 // 3Dconnexion }; -// See: https://github.com/FreeSpacenav/spacenavd/blob/a9eccf34e7cac969ee399f625aef827f4f4aaec6/src/dev.c#L202 +// See: https://github.com/FreeSpacenav/spacenavd/blob/39856625a6de1e8c4b57c5938e1bf29d13cf1a9f/src/dev.c#L63 static const std::vector _3DCONNEXION_DEVICES = { 0xc603, /* 50691 spacemouse plus XT */ @@ -41,6 +41,7 @@ static const std::vector _3DCONNEXION_DEVICES = 0xc628, /* 50728 space navigator for notebooks*/ 0xc629, /* 50729 space pilot pro*/ 0xc62b, /* 50731 space mouse pro*/ + 0xc640, /* 50752 nulooq */ 0xc62e, /* 50734 spacemouse wireless (USB cable) *TESTED* */ 0xc62f, /* 50735 spacemouse wireless receiver */ 0xc631, /* 50737 spacemouse pro wireless *TESTED* */ @@ -48,7 +49,7 @@ static const std::vector _3DCONNEXION_DEVICES = 0xc633, /* 50739 spacemouse enterprise */ 0xc635, /* 50741 spacemouse compact *TESTED* */ 0xc636, /* 50742 spacemouse module */ - 0xc640, /* 50752 nulooq */ + 0xc63a, /* 60060 spacemouse wireless (Bluetooth) */ 0xc652, /* 50770 3Dconnexion universal receiver *TESTED* */ }; diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 777b842..66b26b7 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -178,6 +178,18 @@ void NotificationManager::PopNotification::on_change_color_mode(bool is_dark) m_is_dark = is_dark; } +void NotificationManager::PopNotification::set_delete_callback(DeleteCallback callback) +{ + m_on_delete_callback =callback; +} + +bool NotificationManager::PopNotification::is_valid_delete_callback() +{ + if(m_on_delete_callback) + return true; + return false; +} + void NotificationManager::PopNotification::use_qdt_theme() { ensure_ui_inited(); @@ -319,6 +331,12 @@ void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float init ImGui::PopStyleColor(3); } +void NotificationManager::PopNotification::close() +{ + m_state = EState::ClosePending; + wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); +} + void NotificationManager::PopNotification::qdt_render_block_notification(GLCanvas3D &canvas, float initial_y, bool move_from_overlay, float overlay_width, float right_margin) { if (m_state == EState::Unknown) @@ -970,6 +988,12 @@ bool NotificationManager::PopNotification::compare_text(const std::string& text) return false; } +void NotificationManager::PopNotification::hide(bool h) +{ + if (is_finished()) return; + m_state = h ? EState::Hidden : EState::Unknown; +} + bool NotificationManager::PopNotification::update_state(bool paused, const int64_t delta) { @@ -1831,6 +1855,37 @@ void NotificationManager::close_notification_of_type(const NotificationType type } } } + +void NotificationManager::close_and_delete_self(PopNotification * self) +{ + for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end();) { + std::unique_ptr ¬ification = *it; + if (notification.get() == self) { + m_pop_notifications.erase(it); + break; + }else + ++it; + } +} + +void NotificationManager::remove_notification_of_type(const NotificationType type) { + for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end();) { + std::unique_ptr ¬ification = *it; + if (notification->get_type() == type) { + it = m_pop_notifications.erase(it); + break; + } else + ++it; + } +} + +void NotificationManager::clear_all() +{ + for (size_t i = 0; i < size_t(NotificationType::NotificationTypeCount); i++) { + remove_notification_of_type((NotificationType)i); + } +} + void NotificationManager::remove_slicing_warnings_of_released_objects(const std::vector& living_oids) { for (std::unique_ptr ¬ification : m_pop_notifications) @@ -2241,6 +2296,12 @@ bool NotificationManager::push_notification_data(std::unique_ptris_valid_delete_callback()){ + auto delete_self = [this](NotificationManager::PopNotification* it) { + m_to_delete_after_finish_render = it; + }; + notification->set_delete_callback(delete_self); + } bool retval = false; if (this->activate_existing(notification.get())) { @@ -2315,6 +2376,10 @@ void NotificationManager::render_notifications(GLCanvas3D &canvas, float overlay ;// assert(i <= 1); } } + if (m_to_delete_after_finish_render) { + close_and_delete_self(m_to_delete_after_finish_render); + m_to_delete_after_finish_render = nullptr; + } m_last_render = GLCanvas3D::timestamp_now(); } @@ -2813,5 +2878,13 @@ void NotificationManager::set_upload_job_notification_waittime(int id, const std } } -}//namespace GUI -}//namespace Slic3r +void NotificationManager::PlaterWarningNotification::close() +{ + if (is_finished()) + return; + m_state = EState::Hidden; + wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); + if(m_on_delete_callback) + m_on_delete_callback(this); +} +}}//namespace Slic3r diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index f547801..d6faa9a 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -43,7 +43,7 @@ enum class InfoItemType; enum class NotificationType { - CustomNotification, + CustomNotification = 0, // SlicingNotPossible, // Notification on end of export to a removable media, with hyperling to eject the external media. // Obsolete by ExportFinished @@ -145,7 +145,10 @@ enum class NotificationType QDTPreviewOnlyMode, QDTPrinterConfigUpdateAvailable, QDTUserPresetExceedLimit, - QDTBedFilamentIncompatible + QDTSliceLimitError, + QDTBedFilamentIncompatible, + NotificationTypeCount + }; class NotificationManager @@ -285,6 +288,8 @@ public: void render_notifications(GLCanvas3D &canvas, float overlay_width, float bottom_margin, float right_margin); // finds and closes all notifications of given type void close_notification_of_type(const NotificationType type); + void remove_notification_of_type(const NotificationType type); + void clear_all(); // Hides warnings in G-code preview. Should be called from plater only when 3d view/ preview is changed void set_in_preview(bool preview); // Calls set_in_preview to apply appearing or disappearing of some notificatons; @@ -402,7 +407,7 @@ private: virtual void render(GLCanvas3D& canvas, float initial_y, bool move_from_overlay, float overlay_width, float right_margin); virtual void qdt_render_block_notification(GLCanvas3D &canvas, float initial_y, bool move_from_overlay, float overlay_width, float right_margin); // close will dissapear notification on next render - virtual void close() { m_state = EState::ClosePending; wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);} + virtual void close(); // data from newer notification of same type void update(const NotificationData& n); void append(const std::string& append_str); @@ -420,7 +425,7 @@ private: const bool is_gray() const { return m_is_gray; } void set_gray(bool g) { m_is_gray = g; } virtual bool compare_text(const std::string& text) const; - void hide(bool h) { if (is_finished()) return; m_state = h ? EState::Hidden : EState::Unknown; } + void hide(bool h); // sets m_next_render with time of next mandatory rendering. Delta is time since last render. virtual bool update_state(bool paused, const int64_t delta); int64_t next_render() const { return is_finished() ? 0 : m_next_render; } @@ -432,7 +437,9 @@ private: void set_Multiline(bool Multi) { m_multiline = Multi; } virtual void on_change_color_mode(bool is_dark); void set_scale(float scale) { m_scale = scale; } - + typedef std::function DeleteCallback; + void set_delete_callback(DeleteCallback); + bool is_valid_delete_callback(); protected: // Call after every size change virtual void init(); @@ -536,6 +543,7 @@ private: std::string error_start = ""; std::string error_end = ""; + DeleteCallback m_on_delete_callback; // inner variables to position notification window, texts and buttons correctly // all space without text @@ -568,7 +576,7 @@ private: float m_scale = 1.0f; }; - + void close_and_delete_self(PopNotification*); class ObjectIDNotification : public PopNotification { @@ -584,7 +592,7 @@ private: { public: PlaterWarningNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler) : PopNotification(n, id_provider, evt_handler) {} - void close() override { if(is_finished()) return; m_state = EState::Hidden; wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); } + void close() override; void real_close() { m_state = EState::ClosePending; wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); } void show() { m_state = EState::Unknown; } }; @@ -846,6 +854,7 @@ private: // Cache of IDs to identify and reuse ImGUI windows. NotificationIDProvider m_id_provider; std::deque> m_pop_notifications; + PopNotification* m_to_delete_after_finish_render{nullptr}; // delayed waiting notifications, first is remaining time std::vector m_waiting_notifications; //timestamps used for slicing finished - notification could be gone so it needs to be stored here diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 3ec58a0..9432c5f 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1964,19 +1964,24 @@ void Sidebar::sync_ams_list() //w42 //w42 -std::map Sidebar::build_filament_box_list(std::vector id, std::vector color) +std::map Sidebar::build_filament_box_list(std::vector id, std::vector color, std::vector slot_state) { std::map filament_ams_list; char n = 'A'; char t = 0; - for (int i = 0; i < 4; i++) { - + for (int i = 0; i < 16; i++) { + if (slot_state[i] == 0) + continue; DynamicPrintConfig tray_config; tray_config.set_key_value("filament_id", new ConfigOptionStrings{ id[i] }); tray_config.set_key_value("tag_uid", new ConfigOptionStrings{ "" }); //clear tray_config.set_key_value("filament_type", new ConfigOptionStrings{ "" }); //clear - tray_config.set_key_value("tray_name", new ConfigOptionStrings{ "1A" }); //1A 1B 1C + int group = i / 4 + 1; + char suffix = 'A' + (i % 4); + //std::string tray_name = "1" + std::string(1, 'A' + i); + std::string tray_name = std::to_string(group) + suffix; + tray_config.set_key_value("tray_name", new ConfigOptionStrings{ tray_name }); //1A 1B 1C tray_config.set_key_value("filament_colour", new ConfigOptionStrings{ into_u8(wxColour(color[i]).GetAsString(wxC2S_HTML_SYNTAX)) });//filament_color tray_config.set_key_value("filament_exist", new ConfigOptionBools{ true }); //default @@ -2002,6 +2007,7 @@ void Sidebar::sync_box_list() m_select_machine_dlg->set_print_type(PrintFromType::FROM_NORMAL); m_select_machine_dlg->prepare(0); m_select_machine_dlg->remove_area(); + @@ -2022,7 +2028,7 @@ void Sidebar::updata_filament_list() { void Sidebar::load_box_list(std::vector id, std::vector color) { - std::map filament_ams_list = build_filament_box_list(box_filament_id, box_filment_colors); + std::map filament_ams_list = build_filament_box_list(box_filament_id, box_filment_colors, box_slot_state); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": %1% items") % filament_ams_list.size(); wxGetApp().preset_bundle->filament_ams_list = filament_ams_list; @@ -2906,6 +2912,7 @@ struct Plater::priv bool PopupObjectTable(int object_id, int volume_id, const wxPoint& position); void on_action_send_to_printer(bool isall = false); void on_action_send_to_multi_machine(SimpleEvent&); + void on_action_send_to_multi_app(SimpleEvent&); int update_print_required_data(Slic3r::DynamicPrintConfig config, Slic3r::Model model, Slic3r::PlateDataPtrs plate_data_list, std::string file_name, std::string file_path); private: bool layers_height_allowed() const; @@ -3279,6 +3286,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) q->Bind(EVT_GLTOOLBAR_SEND_TO_PRINTER, &priv::on_action_export_to_sdcard, this); q->Bind(EVT_GLTOOLBAR_SEND_TO_PRINTER_ALL, &priv::on_action_export_to_sdcard_all, this); q->Bind(EVT_GLTOOLBAR_PRINT_MULTI_MACHINE, &priv::on_action_send_to_multi_machine, this); + q->Bind(EVT_GLTOOLBAR_SEND_MULTI_APP, &priv::on_action_send_to_multi_app, this); q->Bind(EVT_GLCANVAS_PLATE_SELECT, &priv::on_plate_selected, this); q->Bind(EVT_DOWNLOAD_PROJECT, &priv::on_action_download_project, this); q->Bind(EVT_IMPORT_MODEL_ID, &priv::on_action_request_model_id, this); @@ -3668,8 +3676,8 @@ void read_binary_stl(const std::string& filename, std::string& model_id, std::st size_t pos = ext_content.find('&'); if (pos != std::string::npos) { - ml_content = ext_content.substr(0, pos); - mw_content = ext_content.substr(pos + 1); + mw_content = ext_content.substr(0, pos); + ml_content = ext_content.substr(pos + 1); } if (ml_content.empty() && ext_content.find("ML") != std::string::npos) { @@ -4249,6 +4257,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ if (linear <= 0) linear = 0.003; double angle = string_to_double_decimal_point(wxGetApp().app_config->get("angle_defletion")); if (angle <= 0) angle = 0.5; + bool split_compound = wxGetApp().app_config->get_bool("is_split_compound"); model = Slic3r::Model:: read_from_step(path.string(), strategy, [this, &dlg, real_filename, &progress_percent, &file_percent, step_percent, INPUT_FILES_RATIO, total_files, i](int load_stage, int current, int total, bool &cancel) { @@ -4265,22 +4274,24 @@ std::vector Plater::priv::load_files(const std::vector& input_ Slic3r::GUI::show_info(nullptr, _L("Name of components inside step file is not UTF8 format!") + "\n\n" + _L("The name may show garbage characters!"), _L("Attention!")); }, - [this, &path, &is_user_cancel, &linear, &angle](Slic3r::Step& file, double& linear_value, double& angle_value)-> int { + [this, &path, &is_user_cancel, &linear, &angle, &split_compound](Slic3r::Step& file, double& linear_value, double& angle_value, bool& is_split)-> int { if (wxGetApp().app_config->get_bool("enable_step_mesh_setting")) { StepMeshDialog mesh_dlg(nullptr, file, linear, angle); if (mesh_dlg.ShowModal() == wxID_OK) { linear_value = mesh_dlg.get_linear_defletion(); - angle_value = mesh_dlg.get_angle_defletion(); + angle_value = mesh_dlg.get_angle_defletion(); + is_split = mesh_dlg.get_split_compound_value(); return 1; } }else { linear_value = linear; angle_value = angle; + is_split = split_compound; return 1; } is_user_cancel = true; return -1; - }, linear, angle); + }, linear, angle, split_compound); }else { model = Slic3r::Model:: read_from_file( path.string(), nullptr, nullptr, strategy, &plate_data, &project_presets, &is_xxx, &file_version, nullptr, @@ -6075,7 +6086,8 @@ void Plater::priv::reload_from_disk() boost::iends_with(path, ".step")) { double linear = string_to_double_decimal_point(wxGetApp().app_config->get("linear_defletion")); double angle = string_to_double_decimal_point(wxGetApp().app_config->get("angle_defletion")); - new_model = Model::read_from_step(path, LoadStrategy::AddDefaultInstances | LoadStrategy::LoadModel, nullptr, nullptr, nullptr, linear, angle); + bool is_split = wxGetApp().app_config->get_bool("is_split_compound"); + new_model = Model::read_from_step(path, LoadStrategy::AddDefaultInstances | LoadStrategy::LoadModel, nullptr, nullptr, nullptr, linear, angle, is_split); }else { new_model = Model::read_from_file(path, nullptr, nullptr, LoadStrategy::AddDefaultInstances | LoadStrategy::LoadModel, &plate_data, &project_presets, nullptr, nullptr, nullptr, nullptr, nullptr, 0, obj_color_fun); } @@ -7582,6 +7594,54 @@ void Plater::priv::on_action_send_to_multi_machine(SimpleEvent&) } } +void Plater::priv::on_action_send_to_multi_app(SimpleEvent &) +{ +#ifdef WIN32 + HKEY hKey; + + LONG result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Bambulab\\Bambu Farm Manager Client"), 0, KEY_READ, &hKey); + LONG result_backup = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("HKEY_CLASSES_ROOT\\bambu-farm-client\\shell\\open\\command"), 0, KEY_READ, &hKey); + + if (result == ERROR_SUCCESS || result_backup == ERROR_SUCCESS) { + RegCloseKey(hKey); + + auto gcodeResult = q->send_gcode(partplate_list.get_curr_plate_index(), [this](int export_stage, int current, int total, bool &cancel) {}); + + if (gcodeResult != 0) { + BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << ":send_gcode failed\n"; + return; + } + + PrintPrepareData data; + q->get_print_job_data(&data); + + if (data._3mf_path.empty()) { + BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << ":3mf path is empty\n"; + return; + } + + wxString filename = q->get_export_gcode_filename("", true, partplate_list.get_curr_plate_index() == PLATE_ALL_IDX ? true : false); + wxString filepath = wxString::FromUTF8(data._3mf_path.string()); + filepath.Replace("\\", "/"); + std::string filePath = "?version=v1.6.0&path=" + filepath.ToStdString() + "&name=" + filename.utf8_string(); + wxString url = "bambu-farm-client://upload-file" + Http::url_encode(filePath); + if (!wxLaunchDefaultBrowser(url)) { + GUI::MessageDialog msgdialog(nullptr, _L("Failed to start Bambu Farm Manager Client."), "", wxAPPLY | wxOK); + msgdialog.ShowModal(); + } + + } else { + GUI::MessageDialog msgdialog(nullptr, _L("No Bambu Farm Manager Client found."), "", wxAPPLY | wxOK); + msgdialog.ShowModal(); + } +#endif // WIN32 + +#ifdef __APPLE__ + // todo +#endif //__APPLE__ + +} + void Plater::priv::on_action_print_plate_from_sdcard(SimpleEvent&) { if (q != nullptr) { @@ -9360,14 +9420,7 @@ int Plater::new_project(bool skip_confirm, bool silent, const wxString &project_ m_only_gcode = false; m_exported_file = false; m_loading_project = false; - get_notification_manager()->qdt_close_plateinfo_notification(); - get_notification_manager()->qdt_close_preview_only_notification(); - get_notification_manager()->qdt_close_3mf_warn_notification(); - get_notification_manager()->close_notification_of_type(NotificationType::PlaterError); - get_notification_manager()->close_notification_of_type(NotificationType::PlaterWarning); - get_notification_manager()->close_notification_of_type(NotificationType::SlicingError); - get_notification_manager()->close_notification_of_type(NotificationType::SlicingSeriousWarning); - get_notification_manager()->close_notification_of_type(NotificationType::SlicingWarning); + get_notification_manager()->clear_all(); if (!silent) wxGetApp().mainframe->select_tab(MainFrame::tp3DEditor); @@ -9668,7 +9721,7 @@ void Plater::import_model_id(wxString download_info) //check file suffix - if (!extension.Contains(".3mf")) { + if (!extension.Contains(".3mf") && !extension.Contains(".3MF")) { msg = _L("Download failed, unknown file format."); return; } @@ -9853,6 +9906,7 @@ bool Plater::up_to_date(bool saved, bool backup) void Plater::add_model(bool imperial_units, std::string fname) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << __LINE__ << " entry"; wxArrayString input_files; std::vector paths; @@ -11484,10 +11538,12 @@ bool Plater::load_svg(const wxArrayString &filenames, bool from_toolbar_or_file_ { // When only one .svg file is dropped on scene if (filenames.size() == 1) { - const wxString &filename = filenames.Last(); - const wxString file_extension = filename.substr(filename.length() - 4); - if (file_extension.CmpNoCase(".svg") == 0) { + const wxString &filename = filenames[0]; + if (boost::iends_with(filenames[0].ToStdString(), ".svg")) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "," << __FILE__ << filename; return emboss_svg(filename, from_toolbar_or_file_menu); + } else { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "," << __FILE__ << ",fail:" << filename; } } else { @@ -11515,15 +11571,23 @@ bool Plater::load_svg(const wxArrayString &filenames, bool from_toolbar_or_file_ } bool Plater::load_same_type_files(const wxArrayString &filenames) { + auto trans_extension = [] (boost::filesystem::path ext) { + std::string ext_str = ext.extension().string(); + boost::algorithm::to_lower(ext_str); + if (ext_str == ".stp" || ext_str == ".step") { + ext.replace_extension(".step"); + } + return ext; + }; if (filenames.size() <= 1) { return true; } else { const wxString &filename = filenames.front(); - boost::filesystem::path path(filename.ToStdWstring()); - auto extension =path.extension(); + boost::filesystem::path path(filename.utf8_string()); + auto extension = trans_extension(path); for (size_t i = 1; i < filenames.size(); i++) { - boost::filesystem::path temp(filenames[i].ToStdWstring()); - auto temp_extension = temp.extension(); - if (extension != temp_extension) { + boost::filesystem::path temp(filenames[i].utf8_string()); + auto temp_extension = trans_extension(temp); + if (extension.extension() != temp_extension.extension()) { return false; } } @@ -11790,6 +11854,7 @@ void Plater::add_file() if (load_svg(input_files,true)) { return; } + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "," << __FILE__ << ","<< "LoadFilesType::SingleOther"; if (!load_files(paths, LoadStrategy::LoadModel, false).empty()) { if (get_project_name() == _L("Untitled") && paths.size() > 0) { p->set_project_filename(wxString::FromUTF8(paths[0].string())); @@ -12663,6 +12728,29 @@ void Plater::export_core_3mf() export_3mf(path_u8, SaveStrategy::Silence); } +// OK if fail_msg is empty +std::string check_boolean_possible(const std::vector& volumes) { + std::string fail_msg; + std::vector csgmesh; + csgmesh.reserve(2 * volumes.size()); + bool has_splitable_volume = csg::model_to_csgmesh(volumes, Transform3d::Identity(), std::back_inserter(csgmesh), + csg::mpartsPositive | csg::mpartsNegative); + + if (auto fail_reason_name = csg::check_csgmesh_booleans(Range{ std::begin(csgmesh), std::end(csgmesh) }); std::get<0>(fail_reason_name) != csg::BooleanFailReason::OK) { + fail_msg = _u8L("Unable to perform boolean operation on model meshes. " + "You may fix the meshes and try again."); + std::string name = std::get<1>(fail_reason_name); + std::map fail_reasons = { + {csg::BooleanFailReason::OK, "OK"}, + {csg::BooleanFailReason::MeshEmpty, Slic3r::format(_u8L("Reason: part \"%1%\" is empty."), name)}, + {csg::BooleanFailReason::NotBoundAVolume, Slic3r::format(_u8L("Reason: part \"%1%\" does not bound a volume."), name)}, + {csg::BooleanFailReason::SelfIntersect, Slic3r::format(_u8L("Reason: part \"%1%\" has self intersection."), name)}, + {csg::BooleanFailReason::NoIntersection, Slic3r::format(_u8L("Reason: \"%1%\" and another part have no intersection."), name)} }; + fail_msg += " " + fail_reasons[std::get<0>(fail_reason_name)]; + } + return fail_msg; +} + // Following lambda generates a combined mesh for export with normals pointing outwards. TriangleMesh Plater::combine_mesh_fff(const ModelObject& mo, int instance_id, std::function notify_func) { @@ -12670,22 +12758,11 @@ TriangleMesh Plater::combine_mesh_fff(const ModelObject& mo, int instance_id, st std::vector csgmesh; csgmesh.reserve(2 * mo.volumes.size()); - bool has_splitable_volume = csg::model_to_csgmesh(mo, Transform3d::Identity(), std::back_inserter(csgmesh), + bool has_splitable_volume = csg::model_to_csgmesh(mo.const_volumes(), Transform3d::Identity(), std::back_inserter(csgmesh), csg::mpartsPositive | csg::mpartsNegative); - std::string fail_msg = _u8L("Unable to perform boolean operation on model meshes. " - "Only positive parts will be kept. You may fix the meshes and try agian."); - if (auto fail_reason_name = csg::check_csgmesh_booleans(Range{ std::begin(csgmesh), std::end(csgmesh) }); std::get<0>(fail_reason_name) != csg::BooleanFailReason::OK) { - std::string name = std::get<1>(fail_reason_name); - std::map fail_reasons = { - {csg::BooleanFailReason::OK, "OK"}, - {csg::BooleanFailReason::MeshEmpty, Slic3r::format( _u8L("Reason: part \"%1%\" is empty."), name)}, - {csg::BooleanFailReason::NotBoundAVolume, Slic3r::format(_u8L("Reason: part \"%1%\" does not bound a volume."), name)}, - {csg::BooleanFailReason::SelfIntersect, Slic3r::format(_u8L("Reason: part \"%1%\" has self intersection."), name)}, - {csg::BooleanFailReason::NoIntersection, Slic3r::format(_u8L("Reason: \"%1%\" and another part have no intersection."), name)} }; - fail_msg += " " + fail_reasons[std::get<0>(fail_reason_name)]; - } - else { + std::string fail_msg = check_boolean_possible(mo.const_volumes()); + if (fail_msg.empty()) { try { MeshBoolean::mcut::McutMeshPtr meshPtr = csg::perform_csgmesh_booleans_mcut(Range{ std::begin(csgmesh), std::end(csgmesh) }); mesh = MeshBoolean::mcut::mcut_to_triangle_mesh(*meshPtr); diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index b7e30f2..ad0cae9 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -154,9 +154,10 @@ public: //w42 std::vector box_filament_id; std::vector box_filment_colors; + std::vector box_slot_state; void sync_box_list(); void load_box_list(std::vector id, std::vector color); - std::map build_filament_box_list(std::vector id, std::vector color); + std::map build_filament_box_list(std::vector id, std::vector color, std::vector slot_state); void updata_filament_list(); ObjectList* obj_list(); @@ -841,6 +842,7 @@ private: }; std::vector get_min_flush_volumes(const DynamicPrintConfig& full_config); +std::string check_boolean_possible(const std::vector& volumes); } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 91ac7c0..4f8f5e3 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -1181,11 +1181,14 @@ wxWindow* PreferencesDialog::create_general_page() auto item_show_shells_in_preview_settings = create_item_checkbox(_L("Always show shells in preview"), page, _L("Always show shells or not in preview view tab.If change value,you should reslice."), 50, "show_shells_in_preview"); + auto item_import_single_svg_and_split = create_item_checkbox(_L("Import a single SVG and split it"), page, + _L("Import a single SVG and then split it to several parts."), 50, + "import_single_svg_and_split"); auto enable_lod_settings = create_item_checkbox(_L("Improve rendering performance by lod"), page, _L("Improved rendering performance under the scene of multiple plates and many models."), 50, "enable_lod"); - auto enable_opengl_multi_instance_rendering = create_item_checkbox(_L("enable multi instance rendering by opengl"), page, - _L("If enabled, it can improve certain rendering performance. But for some graphics cards, it may not be applicable, please turn it off."), 50, "enable_opengl_multi_instance"); + auto enable_opengl_multi_instance_rendering = create_item_checkbox(_L("Enable multi instance rendering by opengl"), page, + _L("If enabled, it can improve certain rendering performance. But for some graphics cards, it may not be applicable, please turn it off."), 50, "enable_opengl_multi_instance"); float range_min = 1.0, range_max = 2.5; auto item_grabber_size_settings = create_item_range_input(_L("Grabber scale"), page, _L("Set grabber size for move,rotate,scale tool.") + _L("Value range") + ":[" + std::to_string(range_min) + "," + @@ -1274,6 +1277,7 @@ wxWindow* PreferencesDialog::create_general_page() sizer_page->Add(_3d_settings, 0, wxTOP | wxEXPAND, FromDIP(20)); sizer_page->Add(item_mouse_zoom_settings, 0, wxTOP, FromDIP(3)); sizer_page->Add(item_show_shells_in_preview_settings, 0, wxTOP, FromDIP(3)); + sizer_page->Add(item_import_single_svg_and_split, 0, wxTOP, FromDIP(3)); sizer_page->Add(enable_opengl_multi_instance_rendering, 0, wxTOP, FromDIP(3)); sizer_page->Add(enable_lod_settings, 0, wxTOP, FromDIP(3)); sizer_page->Add(item_grabber_size_settings, 0, wxTOP, FromDIP(3)); diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index c0099fa..c36d178 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -1081,7 +1081,7 @@ void PlaterPresetComboBox::update() } if (m_type == Preset::TYPE_FILAMENT) - add_ams_filaments(into_u8(selected_user_preset.empty() ? selected_system_preset : selected_user_preset), true); + add_box_filaments(into_u8(selected_user_preset.empty() ? selected_system_preset : selected_user_preset), true); //add_ams_filaments(into_u8(selected_user_preset.empty() ? selected_system_preset : selected_user_preset), true); //y //std::vector filament_orders = {"QIDI PLA Basic", "QIDI PLA Matte", "QIDI PETG HF", "QIDI ABS", "QIDI PLA Silk", "QIDI PLA-CF", diff --git a/src/slic3r/GUI/Project.cpp b/src/slic3r/GUI/Project.cpp index 4f4bde1..5e96ac3 100644 --- a/src/slic3r/GUI/Project.cpp +++ b/src/slic3r/GUI/Project.cpp @@ -222,11 +222,13 @@ void ProjectPanel::on_reload(wxCommandEvent& evt) wxString strJS = wxString::Format("HandleStudio(%s)", m_Res.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()); - }); + wxGetApp().CallAfter([this, strJS] { RunScript(strJS.ToStdString()); }); } +#endif }); } diff --git a/src/slic3r/GUI/SelectMachine.cpp b/src/slic3r/GUI/SelectMachine.cpp index bcb247a..8d98d24 100644 --- a/src/slic3r/GUI/SelectMachine.cpp +++ b/src/slic3r/GUI/SelectMachine.cpp @@ -2917,6 +2917,7 @@ void SelectMachineDialog::on_sync_btn(wxCommandEvent& event) void SelectMachineDialog::get_machine_filament_info() { wxGetApp().plater()->sidebar().box_filament_id = std::move(machine_filament_info.filament_id); wxGetApp().plater()->sidebar().box_filment_colors = std::move(machine_filament_info.filment_colors); + wxGetApp().plater()->sidebar().box_slot_state = std::move(machine_filament_info.slot_state); //return { machine_filament_info.filament_id, machine_filament_info.filment_colors }; } @@ -3751,6 +3752,7 @@ void SelectMachineDialog::update_show_status() } if (!dev) return; dev->check_pushing(); + PartPlate* plate = m_plater->get_partplate_list().get_curr_plate(); // blank plate has no valid gcode file @@ -3773,6 +3775,7 @@ void SelectMachineDialog::update_show_status() } return; } + agent->install_device_cert(obj_->dev_id, obj_->is_lan_mode_printer()); /* check cloud machine connections */ if (!obj_->is_lan_mode_printer()) { diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 3f72d96..2beba8d 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1074,13 +1074,16 @@ std::pair Selection::get_bounding_box_in_reference_s // e.g. for right aligned embossed text if (m_list.size() == 1 && type == ECoordinatesType::Local) { const GLVolume & vol = *get_volume(*m_list.begin()); - const Transform3d vol_world_trafo = vol.world_matrix(); - Vec3d world_zero = vol_world_trafo * Vec3d::Zero(); - for (size_t i = 0; i < 3; i++) { - // move center to local volume zero - center[i] = world_zero.dot(axes[i]); - // extend half size to bigger distance from center - half_box_size[i] = std::max(abs(center[i] - min[i]), abs(center[i] - max[i])); + bool condition = !vol.is_text_shape; + if (condition){ + const Transform3d vol_world_trafo = vol.world_matrix(); + Vec3d world_zero = vol_world_trafo * Vec3d::Zero(); + for (size_t i = 0; i < 3; i++) { + // move center to local volume zero + center[i] = world_zero.dot(axes[i]); + // extend half size to bigger distance from center + half_box_size[i] = std::max(abs(center[i] - min[i]), abs(center[i] - max[i])); + } } } @@ -2049,6 +2052,8 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field, bool unif render_sidebar_position_hints(sidebar_field); else if (boost::starts_with(sidebar_field, "rotation")) render_sidebar_rotation_hints(sidebar_field); + else if (boost::starts_with(sidebar_field, "absolute_rotation")) + render_sidebar_rotation_hints(sidebar_field); else if (boost::starts_with(sidebar_field, "scale") || boost::starts_with(sidebar_field, "size")) //QDS: GUI refactor: add uniform_scale from gizmo render_sidebar_scale_hints(sidebar_field, uniform_scale); diff --git a/src/slic3r/GUI/StatusPanel.cpp b/src/slic3r/GUI/StatusPanel.cpp index bb4bb65..6864579 100644 --- a/src/slic3r/GUI/StatusPanel.cpp +++ b/src/slic3r/GUI/StatusPanel.cpp @@ -397,9 +397,8 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) bSizer_finish_time->Add(0, 0, 0, wxLEFT, FromDIP(20)); bSizer_finish_time->Add(m_staticText_finish_time, 0, wxALIGN_CENTER | wxALL, 0); bSizer_finish_time->Add(m_staticText_finish_day, 0,wxLEFT | wxRIGHT , FromDIP(10)); - // bSizer_finish_time->Add(0, 0, 0, wxLEFT, FromDIP(20)); - bSizer_finish_time->Add(panel_button_block, 0, wxALIGN_CENTER | wxALL, 0); - penel_finish_time->SetMaxSize(wxSize(FromDIP(600), -1)); + bSizer_finish_time->Add(0, 0, 0, wxLEFT, FromDIP(116)); + penel_finish_time->SetMaxSize(wxSize(FromDIP(720), -1)); penel_finish_time->SetSizer(bSizer_finish_time); penel_finish_time->Layout(); @@ -2576,7 +2575,7 @@ void StatusPanel::update_ams(MachineObject *obj) } if (m_filament_setting_dlg) { m_filament_setting_dlg->obj = obj; } - if (obj && (!last_cali_version.has_value() || last_cali_version != obj->cali_version)) { + if (obj && (obj->last_cali_version != obj->cali_version)) { last_cali_version = obj->cali_version; CalibUtils::emit_get_PA_calib_info(obj->nozzle_diameter, ""); } diff --git a/src/slic3r/GUI/StepMeshDialog.cpp b/src/slic3r/GUI/StepMeshDialog.cpp index 3d372af..4e615f7 100644 --- a/src/slic3r/GUI/StepMeshDialog.cpp +++ b/src/slic3r/GUI/StepMeshDialog.cpp @@ -235,6 +235,12 @@ StepMeshDialog::StepMeshDialog(wxWindow* parent, Slic3r::Step& file, double line bSizer->Add(angle_sizer, 1, wxEXPAND | wxLEFT | wxRIGHT, LEFT_RIGHT_PADING); + wxBoxSizer* check_sizer = new wxBoxSizer(wxHORIZONTAL); + m_split_compound_checkbox = new wxCheckBox(this, wxID_ANY, _L("Split compound and compsolid into multiple objects"), wxDefaultPosition, wxDefaultSize, 0); + m_split_compound_checkbox->SetValue(wxGetApp().app_config->get_bool("is_split_compound")); + check_sizer->Add(m_split_compound_checkbox, 0, wxALIGN_LEFT); + bSizer->Add(check_sizer, 1, wxEXPAND | wxLEFT | wxRIGHT, LEFT_RIGHT_PADING); + wxBoxSizer* mesh_face_number_sizer = new wxBoxSizer(wxHORIZONTAL); wxStaticText *mesh_face_number_title = new wxStaticText(this, wxID_ANY, _L("Number of triangular facets") + ": "); mesh_face_number_text = new wxStaticText(this, wxID_ANY, _L("0")); @@ -267,6 +273,7 @@ StepMeshDialog::StepMeshDialog(wxWindow* parent, Slic3r::Step& file, double line if (m_checkbox->IsChecked()) { wxGetApp().app_config->set_bool("enable_step_mesh_setting", false); } + wxGetApp().app_config->set_bool("is_split_compound", m_split_compound_checkbox->GetValue()); wxGetApp().app_config->set("linear_defletion", float_to_string_decimal_point(get_linear_defletion(), 3)); wxGetApp().app_config->set("angle_defletion", float_to_string_decimal_point(get_angle_defletion(), 2)); diff --git a/src/slic3r/GUI/StepMeshDialog.hpp b/src/slic3r/GUI/StepMeshDialog.hpp index 2090bcd..1ed0a73 100644 --- a/src/slic3r/GUI/StepMeshDialog.hpp +++ b/src/slic3r/GUI/StepMeshDialog.hpp @@ -30,11 +30,15 @@ public: return m_last_angle; } } + inline bool get_split_compound_value() { + return m_split_compound_checkbox->GetValue(); + } private: Slic3r::Step& m_file; Button* m_button_ok = nullptr; Button* m_button_cancel = nullptr; wxCheckBox* m_checkbox = nullptr; + wxCheckBox* m_split_compound_checkbox = nullptr; wxString m_linear_last; wxString m_angle_last; wxStaticText* mesh_face_number_text; diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index be1cd5c..095670e 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -1539,7 +1539,6 @@ void UnsavedChangesDialog::update_list() text_left->SetFont(::Label::Head_13); text_left->Wrap(-1); text_left->SetForegroundColour(GREY700); - text_left->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_INFOTEXT)); sizer_left_v->Add(text_left, 0, wxLEFT, 37); @@ -1568,7 +1567,6 @@ void UnsavedChangesDialog::update_list() text_left->SetFont(::Label::Body_13); text_left->Wrap(-1); text_left->SetForegroundColour(GREY700); - text_left->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_INFOTEXT)); sizer_left_v->Add(text_left, 0, wxLEFT, 51 ); diff --git a/src/slic3r/GUI/WebGuideDialog.cpp b/src/slic3r/GUI/WebGuideDialog.cpp index 70ed375..89d1ffa 100644 --- a/src/slic3r/GUI/WebGuideDialog.cpp +++ b/src/slic3r/GUI/WebGuideDialog.cpp @@ -174,8 +174,6 @@ GuideFrame::GuideFrame(GUI_App *pGUI, long style) // Bind(wxEVT_IDLE, &GuideFrame::OnIdle, this); // Bind(wxEVT_CLOSE_WINDOW, &GuideFrame::OnClose, this); - LoadProfile(); - // UI SetStartPage(QDT_REGION); @@ -185,6 +183,12 @@ GuideFrame::GuideFrame(GUI_App *pGUI, long style) GuideFrame::~GuideFrame() { + m_destroy = true; + if (m_load_task && m_load_task->joinable()) { + m_load_task->join(); + delete m_load_task; + m_load_task = nullptr; + } if (m_browser) { delete m_browser; m_browser = nullptr; @@ -206,43 +210,43 @@ wxString GuideFrame::SetStartPage(GuidePage startpage, bool load) m_page = startpage; BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(" enter, load=%1%, start_page=%2%")%load%int(startpage); //wxLogMessage("GUIDE: webpage_1 %s", (boost::filesystem::path(resources_dir()) / "web\\guide\\1\\index.html").make_preferred().string().c_str() ); - wxString TargetUrl = from_u8( (boost::filesystem::path(resources_dir()) / "web/guide/1/index.html").make_preferred().string() ); + wxString TargetUrl = from_u8( (boost::filesystem::path(resources_dir()) / "web/guide/0/index.html?target=1").make_preferred().string() ); //wxLogMessage("GUIDE: webpage_2 %s", TargetUrl.mb_str()); if (startpage == QDT_WELCOME){ SetTitle(_L("Setup Wizard")); - TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/1/index.html").make_preferred().string()); + TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/0/index.html?target=1").make_preferred().string()); } else if (startpage == QDT_REGION) { SetTitle(_L("Setup Wizard")); - TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/11/index.html").make_preferred().string()); + TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/0/index.html?target=11").make_preferred().string()); } else if (startpage == QDT_MODELS) { SetTitle(_L("Setup Wizard")); - TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/21/index.html").make_preferred().string()); + TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/0/index.html?target=21").make_preferred().string()); } else if (startpage == QDT_FILAMENTS) { SetTitle(_L("Setup Wizard")); int nSize = m_ProfileJson["model"].size(); if (nSize>0) - TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/22/index.html").make_preferred().string()); + TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/0/index.html?target=22").make_preferred().string()); else - TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/21/index.html").make_preferred().string()); + TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/0/index.html?target=21").make_preferred().string()); } else if (startpage == QDT_FILAMENT_ONLY) { SetTitle(""); - TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/23/index.html").make_preferred().string()); + TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/0/index.html?target=23").make_preferred().string()); } else if (startpage == QDT_MODELS_ONLY) { SetTitle(""); - TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/24/index.html").make_preferred().string()); + TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/0/index.html?target=24").make_preferred().string()); } else { SetTitle(_L("Setup Wizard")); - TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/21/index.html").make_preferred().string()); + TargetUrl = from_u8((boost::filesystem::path(resources_dir()) / "web/guide/0/index.html?target=21").make_preferred().string()); } wxString strlang = wxGetApp().current_language_code_safe(); BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(", strlang=%1%") % into_u8(strlang); if (strlang != "") - TargetUrl = wxString::Format("%s?lang=%s", w2s(TargetUrl), strlang); + TargetUrl = wxString::Format("%s&lang=%s", w2s(TargetUrl), strlang); TargetUrl = "file://" + TargetUrl; if (load) @@ -293,6 +297,14 @@ void GuideFrame::OnNavigationRequest(wxWebViewEvent &evt) void GuideFrame::OnNavigationComplete(wxWebViewEvent &evt) { //wxLogMessage("%s", "Navigation complete; url='" + evt.GetURL() + "'"); + if (!bFirstComplete) { + m_load_task = new boost::thread(boost::bind(&GuideFrame::LoadProfileData, this)); + // boost::thread LoadProfileThread(boost::bind(&GuideFrame::LoadProfileData, this)); + //LoadProfileThread.detach(); + + bFirstComplete = true; + } + m_browser->Show(); Layout(); @@ -500,7 +512,7 @@ void GuideFrame::OnScriptMessage(wxWebViewEvent &evt) BOOST_LOG_TRIVIAL(trace) << "GuideFrame::OnScriptMessage;Error:" << e.what(); } - wxString strAll = m_ProfileJson.dump(-1,' ',false, json::error_handler_t::ignore); + //wxString strAll = m_ProfileJson.dump(-1,' ',false, json::error_handler_t::ignore); } void GuideFrame::RunScript(const wxString &javascript) @@ -1014,9 +1026,9 @@ int GuideFrame::GetFilamentInfo( std::string VendorDirectory, json & pFilaList, if (jLocal.contains("inherits")) { std::string FName = jLocal["inherits"]; - if (!pFilaList.contains(FName)) { + if (!pFilaList.contains(FName)) { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "pFilaList - Not Contains inherits filaments: " << FName; - return -1; + return -1; } std::string FPath = pFilaList[FName]["sub_path"]; @@ -1061,41 +1073,19 @@ int GuideFrame::GetFilamentInfo( std::string VendorDirectory, json & pFilaList, return 0; } - -int GuideFrame::LoadProfile() +int GuideFrame::LoadProfileData() { try { - //wxString ExePath = boost::dll::program_location().parent_path().string(); - //wxString TargetFolder = ExePath + "\\resources\\profiles\\"; - //wxString TargetFolderSearch = ExePath + "\\resources\\profiles\\*.json"; - - //intptr_t handle; - //_finddata_t findData; - - //handle = _findfirst(TargetFolderSearch.mb_str(), &findData); // 查找目录中的第一个文件 - //if (handle == -1) { return -1; } - - //do { - // if (findData.attrib & _A_SUBDIR && strcmp(findData.name, ".") == 0 && strcmp(findData.name, "..") == 0) // 是否是子目录并且不为"."或".." - // { - // // cout << findData.name << "\t\n"; - // } else { - // wxString strVendor = wxString(findData.name).BeforeLast('.'); - // LoadProfileFamily(strVendor, TargetFolder + findData.name); - // } - - //} while (_findnext(handle, &findData) == 0); // 查找目录中的下一个文件 - // QDS: change directories by design - //BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", will load config from %1%.") % qdt_bundle_path; - m_ProfileJson = json::parse("{}"); - //m_ProfileJson["configpath"] = Slic3r::data_dir(); + // BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", will load config from %1%.") % qdt_bundle_path; + m_ProfileJson = json::parse("{}"); + // m_ProfileJson["configpath"] = Slic3r::data_dir(); m_ProfileJson["model"] = json::array(); m_ProfileJson["machine"] = json::object(); m_ProfileJson["filament"] = json::object(); m_ProfileJson["process"] = json::array(); - vendor_dir = (boost::filesystem::path(Slic3r::data_dir()) / PRESET_SYSTEM_DIR ).make_preferred(); + vendor_dir = (boost::filesystem::path(Slic3r::data_dir()) / PRESET_SYSTEM_DIR).make_preferred(); rsrc_vendor_dir = (boost::filesystem::path(resources_dir()) / "profiles").make_preferred(); // QDS: add QDT as default @@ -1107,25 +1097,7 @@ int GuideFrame::LoadProfile() qdt_bundle_rsrc = true; } - // intptr_t handle; - //_finddata_t findData; - - //handle = _findfirst((qdt_bundle_path / "*.json").make_preferred().string().c_str(), &findData); // 查找目录中的第一个文件 - // if (handle == -1) { return -1; } - - // do { - // if (findData.attrib & _A_SUBDIR && strcmp(findData.name, ".") == 0 && strcmp(findData.name, "..") == 0) // 是否是子目录并且不为"."或".." - // { - // // cout << findData.name << "\t\n"; - // } else { - // wxString strVendor = wxString(findData.name).BeforeLast('.'); - // LoadProfileFamily(w2s(strVendor), vendor_dir.make_preferred().string() + "\\"+ findData.name); - // } - - //} while (_findnext(handle, &findData) == 0); // 查找目录中的下一个文件 - - - //load QDT bundle from user data path + // load QDT bundle from user data path string targetPath = qdt_bundle_path.make_preferred().string(); boost::filesystem::path myPath(targetPath); boost::filesystem::directory_iterator endIter; @@ -1137,14 +1109,15 @@ int GuideFrame::LoadProfile() //cout << "is a file" << endl; //cout << iter->path().string() << endl; - wxString strVendor = from_u8(iter->path().string()).BeforeLast('.'); - strVendor = strVendor.AfterLast( '\\'); - strVendor = strVendor.AfterLast('/'); + wxString strVendor = from_u8(iter->path().string()).BeforeLast('.'); + strVendor = strVendor.AfterLast('\\'); + strVendor = strVendor.AfterLast('/'); wxString strExtension = from_u8(iter->path().string()).AfterLast('.').Lower(); - if (w2s(strVendor) == PresetBundle::QDT_BUNDLE && strExtension.CmpNoCase("json") == 0) - LoadProfileFamily(w2s(strVendor), iter->path().string()); + if (w2s(strVendor) == PresetBundle::QDT_BUNDLE && strExtension.CmpNoCase("json") == 0) LoadProfileFamily(w2s(strVendor), iter->path().string()); } + if (m_destroy) + return 0; } //string others_targetPath = rsrc_vendor_dir.string(); @@ -1154,21 +1127,46 @@ int GuideFrame::LoadProfile() //cout << "is dir" << endl; //cout << iter->path().string() << endl; } else { - //cout << "is a file" << endl; - //cout << iter->path().string() << endl; - wxString strVendor = from_u8(iter->path().string()).BeforeLast('.'); - strVendor = strVendor.AfterLast( '\\'); - strVendor = strVendor.AfterLast('/'); + // cout << "is a file" << endl; + // cout << iter->path().string() << endl; + wxString strVendor = from_u8(iter->path().string()).BeforeLast('.'); + strVendor = strVendor.AfterLast('\\'); + strVendor = strVendor.AfterLast('/'); wxString strExtension = from_u8(iter->path().string()).AfterLast('.').Lower(); - if (w2s(strVendor) != PresetBundle::QDT_BUNDLE && strExtension.CmpNoCase("json")==0) - LoadProfileFamily(w2s(strVendor), iter->path().string()); + if (w2s(strVendor) != PresetBundle::QDT_BUNDLE && strExtension.CmpNoCase("json") == 0) LoadProfileFamily(w2s(strVendor), iter->path().string()); } + if (m_destroy) + return 0; } + //sync to web + std::string strAll = m_ProfileJson.dump(-1, ' ', false, json::error_handler_t::ignore); - //LoadProfileFamily(PresetBundle::QDT_BUNDLE, qdt_bundle_path.string()); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", finished, json contents: " << std::endl << strAll; + json m_Res = json::object(); + m_Res["command"] = "userguide_profile_load_finish"; + m_Res["sequence_id"] = "10001"; + wxString strJS = wxString::Format("HandleStudio(%s)", m_Res.dump(-1, ' ', true)); + if (!m_destroy) + wxGetApp().CallAfter([this, strJS] { RunScript(strJS); }); + //sync to appconfig + if (!m_destroy) + wxGetApp().CallAfter([this] { SaveProfileData(); }); + + } catch (std::exception& e) { + // wxLogMessage("GUIDE: load_profile_error %s ", e.what()); + // wxMessageBox(e.what(), "", MB_OK); + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ", error: " << e.what() << std::endl; + } + + return 0; +} + +int GuideFrame::SaveProfileData() +{ + try { const auto enabled_filaments = wxGetApp().app_config->has_section(AppConfig::SECTION_FILAMENTS) ? wxGetApp().app_config->get_section(AppConfig::SECTION_FILAMENTS) : std::map(); m_appconfig_new.set_vendors(*wxGetApp().app_config); m_appconfig_new.set_section(AppConfig::SECTION_FILAMENTS, enabled_filaments); @@ -1240,10 +1238,6 @@ int GuideFrame::LoadProfile() BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ", error: "<< e.what() < #include #include +#include #include #include @@ -145,10 +146,21 @@ WebViewPanel::WebViewPanel(wxWindow *parent) SetPrintHistoryTaskID(0); m_printhistoryfirst = false; + // MakerLab webview + m_browserML = WebView::CreateWebView(this, "about:blank"); + if (m_browserML == nullptr) { + wxLogError("Could not init m_browserML"); + return; + } + m_browserML->Hide(); + SetMakerlabUrl(""); + m_MakerLabFirst = false; + 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); m_home_web->Add(m_browserPH, 1, wxEXPAND | wxALL, 0); + m_home_web->Add(m_browserML, 1, wxEXPAND | wxALL, 0); topsizer->Add(m_home_web,1, wxEXPAND | wxALL, 0); @@ -312,6 +324,10 @@ void WebViewPanel::ResetWholePage() //PrintHistory SetPrintHistoryTaskID(0); m_printhistoryfirst = false; + + //MakerLab + m_MakerLabFirst = false; + SetMakerlabUrl(""); } wxString WebViewPanel::MakeDisconnectUrl(std::string MenuName) @@ -521,7 +537,15 @@ void WebViewPanel::OnFreshLoginStatus(wxTimerEvent &event) m_loginstatus = 1; if (m_onlinefirst) + { UpdateMakerworldLoginStatus(); + } + + if (m_MakerLabFirst) + { + SetMakerlabUrl(""); + UpdateMakerlabStatus(); + } } if (m_TaskInfo == "" && m_browser && phShow != "false") @@ -535,6 +559,11 @@ void WebViewPanel::OnFreshLoginStatus(wxTimerEvent &event) if (m_onlinefirst) SetMakerworldPageLoginStatus(false); + + if (m_MakerLabFirst) { + SetMakerlabUrl(""); + UpdateMakerlabStatus(); + } } if (m_TaskInfo != "" && m_browser) ShowUserPrintTask(false); @@ -779,6 +808,179 @@ void WebViewPanel::get_makerlab_list(std::function callback) .perform(); } +void WebViewPanel::SetMakerlabUrl(std::string url) { + auto host = wxGetApp().get_model_http_url(wxGetApp().app_config->get_country_code()); + std::string LabUrl; + if (url == "") + LabUrl = (boost::format("%1%makerlab?from=bambustudio") % host).str(); + else + LabUrl = (boost::format("%1%%2%?from=bambustudio") % host % url).str(); + + m_MakerLab_LastUrl = LabUrl; +} + +void WebViewPanel::OpenOneMakerlab(std::string url) +{ + NetworkAgent *agent = GUI::wxGetApp().getAgent(); + if (!agent) return; + + //if (!agent->is_user_login()) { + // wxGetApp().CallAfter([this] { wxGetApp().handle_web_request("{\"sequence_id\":1,\"command\":\"homepage_login_or_register\"}"); }); + //} + + SetMakerlabUrl(url); + SwitchLeftMenu("makerlab"); + +} + +std::string GenerateRandomString(int length) +{ + std::string randomString; + srand(static_cast(time(nullptr))); + for (int i = 0; i < length; ++i) { + int randomAscii = rand() % 26 + 65; + randomString += static_cast(randomAscii); + } + return randomString; +} + +bool WebViewPanel::SaveBase64ToLocal(std::string Base64Buf, std::string FileName, std::string FileTail, wxString &download_path, wxString &download_file) +{ + int nSize = wxBase64DecodedSize(Base64Buf.length()); + char *DstBuf = new char[nSize + 1]; + if (!DstBuf) + { + BOOST_LOG_TRIVIAL(trace) << __FUNCTION__ << ": New Failed. Memory Not Enough"; + return false; + } + memset(DstBuf, 0, nSize + 1); + + int nWrite = wxBase64Decode(DstBuf, nSize + 1, Base64Buf.c_str(), Base64Buf.length()); + + // Format Time String + std::time_t currentTime = std::time(nullptr); + std::tm *timeInfo = std::localtime(¤tTime); + int year = timeInfo->tm_year % 100; + int month = timeInfo->tm_mon + 1; + int day = timeInfo->tm_mday; + int hour = timeInfo->tm_hour; + int minute = timeInfo->tm_min; + int second = timeInfo->tm_sec; + + std::stringstream ss; + ss << std::setfill('0') << std::setw(2) << year << std::setw(2) << month << std::setw(2) << day << std::setw(2) << hour << std::setw(2) << minute << std::setw(2) << second; + std::string dateTimeString = ss.str(); + + // Write 3MF to Disk + char separator = boost::filesystem::path::preferred_separator; + std::string separatorStr(1, separator); + + download_path = wxString::FromUTF8(wxGetApp().app_config->get("download_path")); + download_file = download_path + separatorStr + FileName + "_" + ss.str() + "_" + GenerateRandomString(4) + "."+ FileTail; + + std::ofstream outFile(download_file.ToStdString(), std::ios::binary); + if (!outFile) { + delete DstBuf; + std::cerr << "Error opening file for writing." << std::endl; + return false; + } + outFile.write(DstBuf, nWrite); + if (!outFile) { + delete DstBuf; + std::cerr << "Error writing to file." << std::endl; + return false; + } + + delete DstBuf; + outFile.close(); + std::cout << "Data written to file successfully." << std::endl; + wxLogMessage("Makerlab Binary Write to %s", download_file.ToStdString()); + + return true; +} + +void WebViewPanel::OpenMakerlab3mf(std::string Base64Buf, std::string FileName) +{ + //Save + wxString SavePath, SaveFile; + bool bRet = SaveBase64ToLocal(Base64Buf, FileName, "3mf", SavePath, SaveFile); + if (!bRet) return; + + //Open File + SaveFile = SaveFile.utf8_string(); + wxGetApp().request_open_project(SaveFile.ToStdString()); + + //Remove File + //boost::filesystem::remove(download_file.ToStdString()); +} + +void WebViewPanel::SaveMakerlabStl(int SequenceID, std::string Base64Buf, std::string FileName) +{ + // Save + wxString SavePath, SaveFile; + bool bRet = SaveBase64ToLocal(Base64Buf, FileName, "stl", SavePath, SaveFile); + + // Response + json JFile; + JFile["sequence_id"] = SequenceID; + JFile["command"] = "homepage_makerlab_stl_download"; + JFile["file_name"] = FileName; + JFile["result"] = bRet ? "success" : "fail"; + + std::string strJS = JFile.dump(-1, ' ', false, json::error_handler_t::ignore); + + wxGetApp().CallAfter([this, strJS] { + if (!m_browserML) return; + + WebView::RunScript(m_browserML, strJS); + }); +} + +void WebViewPanel::UpdateMakerlabStatus( ) +{ + if (m_browserML == nullptr) return; + + wxString ml_currenturl; + if (m_MakerLab_LastUrl != "") { + ml_currenturl = m_MakerLab_LastUrl; + } else { + ml_currenturl = m_browserML->GetCurrentURL(); + if (ml_currenturl == "about:blank") { + SetMakerlabUrl(""); + ml_currenturl = m_MakerLab_LastUrl; + } + } + + if (wxGetApp().is_user_login()) + { + NetworkAgent *agent = GUI::wxGetApp().getAgent(); + if (agent == nullptr) { + wxString UrlDisconnect = MakeDisconnectUrl("makerlab"); + m_browserML->LoadURL(UrlDisconnect); + return; + } + + std::string newticket; + int ret = agent->request_bind_ticket(&newticket); + if (ret == 0) + { + GetJumpUrl(login, newticket, ml_currenturl, ml_currenturl); + m_browserML->LoadURL(ml_currenturl); + m_MakerLab_LastUrl = ""; + } + else { + wxString UrlDisconnect = MakeDisconnectUrl("makerlab"); + m_browserML->LoadURL(UrlDisconnect); + } + } + else + { + GetJumpUrl(false, "", ml_currenturl, ml_currenturl); + m_browserML->LoadURL(ml_currenturl); + m_MakerLab_LastUrl = ""; + } +} + unsigned char ToHex(unsigned char x) { return x > 9 ? x + 55 : x + 48; } unsigned char FromHex(unsigned char x) @@ -1092,6 +1294,13 @@ void WebViewPanel::OnNavigationComplete(wxWebViewEvent& evt) } } + if (m_browser != nullptr && evt.GetId() == m_browser->GetId()) + { + SwitchWebContent("home"); + SendDesignStaffpick(true); + SendMakerlabList(); + } + //m_browser->Show(); Layout(); BOOST_LOG_TRIVIAL(trace) << __FUNCTION__ << ": " << evt.GetTarget().ToUTF8().data(); @@ -1435,6 +1644,7 @@ void WebViewPanel::OnError(wxWebViewEvent& evt) wxString UrlDisconnect = MakeDisconnectUrl("online"); m_browserMW->LoadURL(UrlDisconnect); + SetWebviewShow("makerlab", false); SetWebviewShow("online", true); SetWebviewShow("right", false); SetWebviewShow("printhistory", false); @@ -1450,12 +1660,29 @@ void WebViewPanel::OnError(wxWebViewEvent& evt) wxString UrlDisconnect = MakeDisconnectUrl("printhistory"); m_browserPH->LoadURL(UrlDisconnect); + SetWebviewShow("makerlab", false); SetWebviewShow("printhistory", true); SetWebviewShow("online", false); SetWebviewShow("right", false); } } + if (evt.GetInt() == wxWEBVIEW_NAV_ERR_CONNECTION && evt.GetId() == m_browserML->GetId()) { + m_MakerLab_LastUrl = m_browserML->GetCurrentURL(); + + if (m_contentname == "makerlab") { + wxString errurl = evt.GetURL(); + + wxString UrlDisconnect = MakeDisconnectUrl("makerlab"); + m_browserML->LoadURL(UrlDisconnect); + + SetWebviewShow("makerlab", true); + SetWebviewShow("printhistory", false); + SetWebviewShow("online", false); + SetWebviewShow("right", false); + } + } + UpdateState(); } @@ -1508,21 +1735,36 @@ void WebViewPanel::SwitchWebContent(std::string modelname, int refresh) wxString strlang = wxGetApp().current_language_code_safe(); - if (modelname.compare("makerlab") == 0) { - auto host = wxGetApp().get_model_http_url(wxGetApp().app_config->get_country_code()); - std::string LabUrl = (boost::format("%1%makerlab?from=qidistudio") % host).str(); + if (modelname.compare("makersupply") == 0) + { + std::string strRegion = wxGetApp().app_config->get_country_code(); + wxString MakerSupplyUrl; + if (strRegion == "CN") + MakerSupplyUrl = "https://bambulab.tmall.com/category-1761686934.htm?from=bambustudio"; + else + MakerSupplyUrl = "https://store.bambulab.com/collections/makers-supply?from=bambustudio"; - wxString FinalUrl = LabUrl; - NetworkAgent *agent = GUI::wxGetApp().getAgent(); - if (agent && agent->is_user_login()) { - std::string QIDIHost=agent->get_qiditech_host(); - - std::string newticket; - int ret = agent->request_bind_ticket(&newticket); - if (ret == 0) GetJumpUrl(true, newticket, FinalUrl, FinalUrl); + wxLaunchDefaultBrowser(MakerSupplyUrl); } + else if (modelname.compare("makerlab") == 0) + { + wxString FinalUrl; - wxLaunchDefaultBrowser(FinalUrl); + if (!m_MakerLabFirst) + { + UpdateMakerlabStatus(); + } + else { + if (m_MakerLab_LastUrl != "") m_browserML->LoadURL(m_MakerLab_LastUrl); + } + + m_MakerLabFirst = true; + m_MakerLab_LastUrl = ""; + + SetWebviewShow("makerlab", true); + SetWebviewShow("online", false); + SetWebviewShow("right", false); + SetWebviewShow("printhistory", false); // conf save wxGetApp().app_config->set_str("homepage", "makerlab_clicked", "1"); @@ -1549,11 +1791,10 @@ void WebViewPanel::SwitchWebContent(std::string modelname, int refresh) } SetWebviewShow("online", true); + SetWebviewShow("makerlab", false); SetWebviewShow("right", false); SetWebviewShow("printhistory", false); - GetSizer()->Layout(); - // conf save wxGetApp().app_config->set_str("homepage", "online_clicked", "1"); wxGetApp().app_config->save(); @@ -1594,8 +1835,7 @@ void WebViewPanel::SwitchWebContent(std::string modelname, int refresh) SetWebviewShow("online", false); SetWebviewShow("right", false); SetWebviewShow("printhistory", true); - - GetSizer()->Layout(); + SetWebviewShow("makerlab", false); } else if (modelname.compare("home") == 0 || modelname.compare("recent") == 0 || modelname.compare("manual") == 0) { if (!m_browser) return; @@ -1613,9 +1853,10 @@ void WebViewPanel::SwitchWebContent(std::string modelname, int refresh) SetWebviewShow("online", false); SetWebviewShow("printhistory", false); SetWebviewShow("right", true); - - GetSizer()->Layout(); + SetWebviewShow("makerlab", false); } + + GetSizer()->Layout(); } void WebViewPanel::SwitchLeftMenu(std::string strMenu) @@ -1633,22 +1874,6 @@ void WebViewPanel::SwitchLeftMenu(std::string strMenu) WebView::RunScript(m_browserLeft, strJS); } -void WebViewPanel::OpenOneMakerlab(std::string url) { - auto host = wxGetApp().get_model_http_url(wxGetApp().app_config->get_country_code()); - std::string LabUrl = (boost::format("%1%%2%?from=qidistudio") % host % url).str(); - - wxString FinalUrl = LabUrl; - NetworkAgent *agent = GUI::wxGetApp().getAgent(); - if (agent && agent->is_user_login()) { - std::string newticket; - int ret = agent->request_bind_ticket(&newticket); - if (ret == 0) GetJumpUrl(true, newticket, FinalUrl, FinalUrl); - } - - wxLaunchDefaultBrowser(FinalUrl); -} - - void WebViewPanel::CheckMenuNewTag() { std::string sClick = wxGetApp().app_config->get("homepage", "online_clicked"); if (sClick.compare("1")==0) @@ -1700,6 +1925,12 @@ void WebViewPanel::SetLeftMenuShow(std::string menuname, int show) WebView::RunScript(m_browser, strJS); } +void WebViewPanel::SetLeftMenuWidth(int nWidth) { + m_browserLeft->SetSize(wxSize(FromDIP(nWidth), -1)); + m_browserLeft->SetMinSize(wxSize(FromDIP(nWidth), -1)); + m_browserLeft->SetMaxSize(wxSize(FromDIP(nWidth), -1)); +} + void WebViewPanel::SetWebviewShow(wxString name, bool show) { wxWebView *TmpWeb = nullptr; @@ -1711,7 +1942,9 @@ void WebViewPanel::SetWebviewShow(wxString name, bool show) TmpWeb = m_browserMW; else if (name == "printhistory") TmpWeb = m_browserPH; - + else if (name == "makerlab") + TmpWeb = m_browserML; + if (TmpWeb != nullptr) { if (show) diff --git a/src/slic3r/GUI/WebViewDialog.hpp b/src/slic3r/GUI/WebViewDialog.hpp index 4809959..d5525ca 100644 --- a/src/slic3r/GUI/WebViewDialog.hpp +++ b/src/slic3r/GUI/WebViewDialog.hpp @@ -96,40 +96,67 @@ public: public: void ResetWholePage(); - void SetMakerworldModelID(std::string ModelID); - void OpenMakerworldSearchPage(std::string KeyWord); - void SetPrintHistoryTaskID(int TaskID); - void SwitchWebContent(std::string modelname, int refresh=0); - void SwitchLeftMenu(std::string strMenu); - void OpenOneMakerlab(std::string url); - - wxString MakeDisconnectUrl(std::string MenuName); - - void CheckMenuNewTag(); - void ShowMenuNewTag(std::string menuname, std::string show); - void SetLeftMenuShow(std::string menuname, int show); - - void SendRecentList(int images); - void SendDesignStaffpick(bool on); - void SendMakerlabList(); - void OpenModelDetail(std::string id, NetworkAgent *agent); + // Login void SendLoginInfo(); void ShowNetpluginTip(); - void SetWebviewShow(wxString name, bool show); + //MW + void SetMakerworldModelID(std::string ModelID); + void OpenMakerworldSearchPage(std::string KeyWord); + void SetPrintHistoryTaskID(int TaskID); + + //DisconnectPage + wxString MakeDisconnectUrl(std::string MenuName); + //LeftMenu + std::string m_contentname; // CurrentMenu + bool m_leftfirst; // Left First Loaded + void CheckMenuNewTag(); + void ShowMenuNewTag(std::string menuname, std::string show); + void SetLeftMenuShow(std::string menuname, int show); + void SetLeftMenuWidth(int nWidth); + void SwitchWebContent(std::string modelname, int refresh = 0); + void SwitchLeftMenu(std::string strMenu); + + //Recent File + void SendRecentList(int images); + + //Online + bool m_onlinefirst; // Online Page First Load + wxString m_online_type; // recommend & browse + wxString m_online_LastUrl; // PageLastError Url + + void SendDesignStaffpick(bool on); void get_design_staffpick(int offset, int limit, std::function callback); void get_user_mw_4u_config(std::function callback); void get_4u_staffpick(int seed, int limit, std::function callback); - void get_makerlab_list(std::function callback); + void OpenModelDetail(std::string id, NetworkAgent *agent); int get_model_mall_detail_url(std::string *url, std::string id); - - std::string m_TaskInfo; - void ShowUserPrintTask(bool bShow); - void UpdateMakerworldLoginStatus(); void SetMakerworldPageLoginStatus(bool login, wxString ticket = ""); + //Makerlab + bool m_MakerLabFirst; + wxString m_MakerLab_LastUrl; + void SendMakerlabList(); + void get_makerlab_list(std::function callback); + void SetMakerlabUrl(std::string url); + void OpenOneMakerlab(std::string url); + void OpenMakerlab3mf(std::string Base64Buf, std::string FileName); + bool SaveBase64ToLocal(std::string Base64Buf, std::string FileName,std::string FileTail, wxString &download_path, wxString &download_file); + void SaveMakerlabStl(int SequenceID,std::string Base64Buf, std::string FileName); + void UpdateMakerlabStatus(); + + //Common UI + void SetWebviewShow(wxString name, bool show); + + //PrintHistory + std::string m_TaskInfo; + bool m_printhistoryfirst; // print history first load + wxString m_print_history_LastUrl; + void ShowUserPrintTask(bool bShow); + + // bool GetJumpUrl(bool login, wxString ticket, wxString targeturl, wxString &finalurl); void update_mode(); @@ -144,15 +171,9 @@ private: wxWebView* m_browserLeft; wxWebView * m_browserMW; wxWebView *m_browserPH; //PrintHistory - std::string m_contentname; - bool m_leftfirst; //Left First Loaded - bool m_onlinefirst; //Online Page First Load - bool m_printhistoryfirst; //print history first load - //std::string m_online_spec_id; // Online Page Spec_ID - wxString m_online_type; //recommend & browse - wxString m_online_LastUrl; //PageLastError Url - wxString m_print_history_LastUrl; + wxWebView *m_browserML; //MakerLab + //Basic Browser wxBoxSizer *bSizer_toolbar; wxButton * m_button_back; wxButton * m_button_forward; diff --git a/src/slic3r/GUI/Widgets/AMSControl.cpp b/src/slic3r/GUI/Widgets/AMSControl.cpp index a2438c2..914eab5 100644 --- a/src/slic3r/GUI/Widgets/AMSControl.cpp +++ b/src/slic3r/GUI/Widgets/AMSControl.cpp @@ -1908,13 +1908,13 @@ void AmsCans::create(wxWindow *parent) if (m_can_count <= 1) { AddCan(*it, m_can_count, m_info.cans.size(), sizer_can_left); if (m_can_count == 0) { - sizer_can_left->Add(0,0,0,wxTOP,FromDIP(20)); + sizer_can_left->Add(0,0,0,wxTOP,FromDIP(25)); } } else { AddCan(*it, m_can_count, m_info.cans.size(), sizer_can_right); if (m_can_count == 2) { - sizer_can_right->Prepend(0, 0, 0, wxTOP, FromDIP(20)); + sizer_can_right->Prepend(0, 0, 0, wxTOP, FromDIP(25)); } } diff --git a/src/slic3r/GUI/Widgets/PopupWindow.cpp b/src/slic3r/GUI/Widgets/PopupWindow.cpp index 135802c..7349635 100644 --- a/src/slic3r/GUI/Widgets/PopupWindow.cpp +++ b/src/slic3r/GUI/Widgets/PopupWindow.cpp @@ -16,7 +16,7 @@ bool PopupWindow::Create(wxWindow *parent, int style) if (!wxPopupTransientWindow::Create(parent, style)) return false; #ifdef __WXGTK__ - GetTopParent(parent)->Bind(wxEVT_ACTIVATE, &PopupWindow::topWindowActiavate, this); + GetTopParent(parent)->Bind(wxEVT_ACTIVATE, &PopupWindow::topWindowActivate, this); #endif return true; } @@ -24,14 +24,13 @@ bool PopupWindow::Create(wxWindow *parent, int style) PopupWindow::~PopupWindow() { #ifdef __WXGTK__ - GetTopParent(this)->Unbind(wxEVT_ACTIVATE, &PopupWindow::topWindowActiavate, this); + GetTopParent(this)->Unbind(wxEVT_ACTIVATE, &PopupWindow::topWindowActivate, this); #endif } #ifdef __WXGTK__ -void PopupWindow::topWindowActiavate(wxActivateEvent &event) +void PopupWindow::topWindowActivate(wxActivateEvent &event) { event.Skip(); - if (!event.GetActive() && IsShown()) DismissAndNotify(); } #endif diff --git a/src/slic3r/GUI/Widgets/PopupWindow.hpp b/src/slic3r/GUI/Widgets/PopupWindow.hpp index 88993de..f820be8 100644 --- a/src/slic3r/GUI/Widgets/PopupWindow.hpp +++ b/src/slic3r/GUI/Widgets/PopupWindow.hpp @@ -17,7 +17,7 @@ public: private: #ifdef __WXGTK__ - void topWindowActiavate(wxActivateEvent &event); + void topWindowActivate(wxActivateEvent &event); #endif }; diff --git a/src/slic3r/GUI/calib_dlg.cpp b/src/slic3r/GUI/calib_dlg.cpp index 5aff285..d15632f 100644 --- a/src/slic3r/GUI/calib_dlg.cpp +++ b/src/slic3r/GUI/calib_dlg.cpp @@ -524,7 +524,7 @@ void MaxVolumetricSpeed_Test_Dlg::on_start(wxCommandEvent& event) { read_double = read_double && m_tiStep->GetTextCtrl()->GetValue().ToDouble(&m_params.step); if (!read_double || m_params.start <= 0 || m_params.step <= 0 || m_params.end < (m_params.start + m_params.step)) { - MessageDialog msg_dlg(nullptr, _L("Please input valid values:\nstart > 0 \step >= 0\nend > start + step)"), wxEmptyString, wxICON_WARNING | wxOK); + MessageDialog msg_dlg(nullptr, _L("Please input valid values:\nstart > 0 step >= 0\nend > start + step)"), wxEmptyString, wxICON_WARNING | wxOK); msg_dlg.ShowModal(); return; } @@ -632,7 +632,7 @@ void VFA_Test_Dlg::on_start(wxCommandEvent& event) read_double = read_double && m_tiStep->GetTextCtrl()->GetValue().ToDouble(&m_params.step); if (!read_double || m_params.start <= 10 || m_params.step <= 0 || m_params.end < (m_params.start + m_params.step)) { - MessageDialog msg_dlg(nullptr, _L("Please input valid values:\nstart > 10 \step >= 0\nend > start + step)"), wxEmptyString, wxICON_WARNING | wxOK); + MessageDialog msg_dlg(nullptr, _L("Please input valid values:\nstart > 10 step >= 0\nend > start + step)"), wxEmptyString, wxICON_WARNING | wxOK); msg_dlg.ShowModal(); return; } @@ -737,7 +737,7 @@ void Retraction_Test_Dlg::on_start(wxCommandEvent& event) { read_double = read_double && m_tiStep->GetTextCtrl()->GetValue().ToDouble(&m_params.step); if (!read_double || m_params.start < 0 || m_params.step <= 0 || m_params.end < (m_params.start + m_params.step)) { - MessageDialog msg_dlg(nullptr, _L("Please input valid values:\nstart > 0 \step >= 0\nend > start + step)"), wxEmptyString, wxICON_WARNING | wxOK); + MessageDialog msg_dlg(nullptr, _L("Please input valid values:\nstart > 0 step >= 0\nend > start + step)"), wxEmptyString, wxICON_WARNING | wxOK); msg_dlg.ShowModal(); return; } diff --git a/src/slic3r/Utils/Http.cpp b/src/slic3r/Utils/Http.cpp index 079a968..37412e8 100644 --- a/src/slic3r/Utils/Http.cpp +++ b/src/slic3r/Utils/Http.cpp @@ -177,12 +177,11 @@ Http::priv::priv(const std::string &url) ::curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); // curl makes a copy internally ::curl_easy_setopt(curl, CURLOPT_USERAGENT, SLIC3R_APP_NAME "/" SLIC3R_VERSION); ::curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &error_buffer.front()); + ::curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA); #ifdef __WINDOWS__ ::curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_MAX_TLSv1_2); #endif ::curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); - ::curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); - ::curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); } Http::priv::~priv() diff --git a/src/slic3r/Utils/Http.hpp b/src/slic3r/Utils/Http.hpp index 99ada99..4b672ee 100644 --- a/src/slic3r/Utils/Http.hpp +++ b/src/slic3r/Utils/Http.hpp @@ -29,6 +29,7 @@ enum HttpErrorCode HttpErrorTimeout = 13, HttpErrorResourcesExhaust = 14, HttpErrorVersionLimited = 15, + HttpErrorCertRevoked = 101, }; /// Represetns a Http request diff --git a/src/slic3r/Utils/MacDarkMode.mm b/src/slic3r/Utils/MacDarkMode.mm index 58ff02f..0dcce3a 100644 --- a/src/slic3r/Utils/MacDarkMode.mm +++ b/src/slic3r/Utils/MacDarkMode.mm @@ -152,6 +152,8 @@ void openFolderForFile(wxString const & file) { NSURL* url = [NSURL URLFromPasteboard:[info draggingPasteboard]]; NSString * path = [url path]; + if (path == nil) + return TRUE; url = [NSURL fileURLWithPath: path]; [self loadFileURL:url allowingReadAccessToURL:url]; return TRUE; diff --git a/src/slic3r/Utils/NetworkAgent.cpp b/src/slic3r/Utils/NetworkAgent.cpp index 0934a70..fe7c4a9 100644 --- a/src/slic3r/Utils/NetworkAgent.cpp +++ b/src/slic3r/Utils/NetworkAgent.cpp @@ -62,6 +62,8 @@ func_send_message NetworkAgent::send_message_ptr = nullptr; func_connect_printer NetworkAgent::connect_printer_ptr = nullptr; func_disconnect_printer NetworkAgent::disconnect_printer_ptr = nullptr; func_send_message_to_printer NetworkAgent::send_message_to_printer_ptr = nullptr; +func_check_cert NetworkAgent::check_cert_ptr = nullptr; +func_install_device_cert NetworkAgent::install_device_cert_ptr = nullptr; func_start_discovery NetworkAgent::start_discovery_ptr = nullptr; func_change_user NetworkAgent::change_user_ptr = nullptr; func_is_user_login NetworkAgent::is_user_login_ptr = nullptr; @@ -274,6 +276,8 @@ int NetworkAgent::initialize_network_module(bool using_backup) connect_printer_ptr = reinterpret_cast(get_network_function("qidi_network_connect_printer")); disconnect_printer_ptr = reinterpret_cast(get_network_function("qidi_network_disconnect_printer")); send_message_to_printer_ptr = reinterpret_cast(get_network_function("qidi_network_send_message_to_printer")); + check_cert_ptr = reinterpret_cast(get_network_function("bambu_network_update_cert")); + install_device_cert_ptr = reinterpret_cast(get_network_function("bambu_network_install_device_cert")); start_discovery_ptr = reinterpret_cast(get_network_function("qidi_network_start_discovery")); change_user_ptr = reinterpret_cast(get_network_function("qidi_network_change_user")); is_user_login_ptr = reinterpret_cast(get_network_function("qidi_network_is_user_login")); @@ -398,6 +402,7 @@ int NetworkAgent::unload_network_module() connect_printer_ptr = nullptr; disconnect_printer_ptr = nullptr; send_message_to_printer_ptr = nullptr; + check_cert_ptr = nullptr; start_discovery_ptr = nullptr; change_user_ptr = nullptr; is_user_login_ptr = nullptr; @@ -853,11 +858,11 @@ int NetworkAgent::stop_device_subscribe() return ret; } -int NetworkAgent::send_message(std::string dev_id, std::string json_str, int qos) +int NetworkAgent::send_message(std::string dev_id, std::string json_str, int qos, int flag) { int ret = 0; if (network_agent && send_message_ptr) { - ret = send_message_ptr(network_agent, dev_id, json_str, qos); + ret = send_message_ptr(network_agent, dev_id, json_str, qos, flag); if (ret) BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(" error: network_agent=%1%, ret=%2%, dev_id=%3%, json_str=%4%, qos=%5%")%network_agent %ret %dev_id %json_str %qos; } @@ -887,11 +892,11 @@ int NetworkAgent::disconnect_printer() return ret; } -int NetworkAgent::send_message_to_printer(std::string dev_id, std::string json_str, int qos) +int NetworkAgent::send_message_to_printer(std::string dev_id, std::string json_str, int qos, int flag) { int ret = 0; if (network_agent && send_message_to_printer_ptr) { - ret = send_message_to_printer_ptr(network_agent, dev_id, json_str, qos); + ret = send_message_to_printer_ptr(network_agent, dev_id, json_str, qos, flag); if (ret) BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(" error: network_agent=%1%, ret=%2%, dev_id=%3%, json_str=%4%, qos=%5%") %network_agent %ret %dev_id %json_str %qos; @@ -899,6 +904,24 @@ int NetworkAgent::send_message_to_printer(std::string dev_id, std::string json_s return ret; } +int NetworkAgent::check_cert() +{ + int ret = 0; + if (network_agent && check_cert_ptr) { + ret = check_cert_ptr(network_agent); + if (ret) + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(" error: network_agent=%1%, ret=%2%") % network_agent % ret; + } + return ret; +} + +void NetworkAgent::install_device_cert(std::string dev_id, bool lan_only) +{ + if (network_agent && install_device_cert_ptr) { + install_device_cert_ptr(network_agent, dev_id, lan_only); + } +} + bool NetworkAgent::start_discovery(bool start, bool sending) { bool ret = false; diff --git a/src/slic3r/Utils/NetworkAgent.hpp b/src/slic3r/Utils/NetworkAgent.hpp index b13ea0f..2a6e364 100644 --- a/src/slic3r/Utils/NetworkAgent.hpp +++ b/src/slic3r/Utils/NetworkAgent.hpp @@ -38,10 +38,12 @@ typedef int (*func_del_subscribe)(void *agent, std::vector dev_list typedef void (*func_enable_multi_machine)(void *agent, bool enable); typedef int (*func_start_device_subscribe)(void* agent); typedef int (*func_stop_device_subscribe)(void* agent); -typedef int (*func_send_message)(void *agent, std::string dev_id, std::string json_str, int qos); +typedef int (*func_send_message)(void *agent, std::string dev_id, std::string json_str, int qos, int flag); typedef int (*func_connect_printer)(void *agent, std::string dev_id, std::string dev_ip, std::string username, std::string password, bool use_ssl); typedef int (*func_disconnect_printer)(void *agent); -typedef int (*func_send_message_to_printer)(void *agent, std::string dev_id, std::string json_str, int qos); +typedef int (*func_send_message_to_printer)(void *agent, std::string dev_id, std::string json_str, int qos, int flag); +typedef int (*func_check_cert)(void* agent); +typedef void (*func_install_device_cert)(void* agent, std::string dev_id, bool lan_only); typedef bool (*func_start_discovery)(void *agent, bool start, bool sending); typedef int (*func_change_user)(void *agent, std::string user_info); typedef bool (*func_is_user_login)(void *agent); @@ -157,10 +159,12 @@ public: void enable_multi_machine(bool enable); int start_device_subscribe(); int stop_device_subscribe(); - int send_message(std::string dev_id, std::string json_str, int qos); + int send_message(std::string dev_id, std::string json_str, int qos, int flag); int connect_printer(std::string dev_id, std::string dev_ip, std::string username, std::string password, bool use_ssl); int disconnect_printer(); - int send_message_to_printer(std::string dev_id, std::string json_str, int qos); + int send_message_to_printer(std::string dev_id, std::string json_str, int qos, int flag); + int check_cert(); + void install_device_cert(std::string dev_id, bool lan_only); bool start_discovery(bool start, bool sending); int change_user(std::string user_info); bool is_user_login(); @@ -270,6 +274,8 @@ private: static func_connect_printer connect_printer_ptr; static func_disconnect_printer disconnect_printer_ptr; static func_send_message_to_printer send_message_to_printer_ptr; + static func_check_cert check_cert_ptr; + static func_install_device_cert install_device_cert_ptr; static func_start_discovery start_discovery_ptr; static func_change_user change_user_ptr; static func_is_user_login is_user_login_ptr; diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp index 1727c4f..74b4e3e 100644 --- a/src/slic3r/Utils/OctoPrint.cpp +++ b/src/slic3r/Utils/OctoPrint.cpp @@ -620,31 +620,34 @@ GUI::Box_info OctoPrint::get_box_info(wxString& msg) std::string vendor_key = "vendor_slot" + std::to_string(i); std::string slot_key = "slot" + std::to_string(i); - if (line.find(color_key) != std::string::npos) { + // Create regex patterns for exact variable name matches + std::regex color_pattern("^\\s*" + color_key + "\\s*="); + std::regex filament_pattern("^\\s*" + filament_key + "\\s*="); + std::regex vendor_pattern("^\\s*" + vendor_key + "\\s*="); + std::regex slot_pattern("^\\s*" + slot_key + "\\s*="); + + if (std::regex_search(line, color_pattern)) { size_t value_start = line.find("=") + 1; std::string value_str = line.substr(value_start); value_str.erase(0, value_str.find_first_not_of(" ")); value_str.erase(value_str.find_last_not_of(" ") + 1); filament_info.filament_color_index[i] = std::stoi(value_str); } - - if (line.find(filament_key) != std::string::npos) { + else if (std::regex_search(line, filament_pattern)) { size_t value_start = line.find("=") + 1; std::string value_str = line.substr(value_start); value_str.erase(0, value_str.find_first_not_of(" ")); value_str.erase(value_str.find_last_not_of(" ") + 1); filament_info.filament_index[i] = std::stoi(value_str); } - - if (line.find(vendor_key) != std::string::npos) { + else if (std::regex_search(line, vendor_pattern)) { size_t value_start = line.find("=") + 1; std::string value_str = line.substr(value_start); value_str.erase(0, value_str.find_first_not_of(" ")); value_str.erase(value_str.find_last_not_of(" ") + 1); filament_info.filament_vendor[i] = std::stoi(value_str); } - - if (line.find(slot_key) != std::string::npos) { + else if (std::regex_search(line, slot_pattern)) { size_t value_start = line.find("=") + 1; std::string value_str = line.substr(value_start); value_str.erase(0, value_str.find_first_not_of(" ")); diff --git a/src/slic3r/Utils/qidi_networking.hpp b/src/slic3r/Utils/qidi_networking.hpp index 320652c..5a329cf 100644 --- a/src/slic3r/Utils/qidi_networking.hpp +++ b/src/slic3r/Utils/qidi_networking.hpp @@ -36,6 +36,7 @@ namespace QDT { #define QIDI_NETWORK_ERR_PARSE_CONFIG_FAILED -23 #define QIDI_NETWORK_ERR_NO_CORRESPONDING_BUCKET -24 #define QIDI_NETWORK_ERR_GET_INSTANCE_ID_FAILED -25 +#define QIDI_NETWORK_SIGNED_ERROR -26 //bind error #define QIDI_NETWORK_ERR_BIND_CREATE_SOCKET_FAILED -1010 //failed to create socket @@ -78,6 +79,7 @@ namespace QDT { #define QIDI_NETWORK_ERR_PRINT_SP_PATCH_PROJECT_FAILED -3110 //failed to patch project #define QIDI_NETWORK_ERR_PRINT_SP_POST_TASK_FAILED -3120 //failed to post task #define QIDI_NETWORK_ERR_PRINT_SP_WAIT_PRINTER_FAILED -3130 //failed to wait the ack from printer +#define QIDI_NETOWRK_ERR_PRINT_SP_ENC_FLAG_NOT_READY -3140 //enc parse not ready //start_local_print error #define QIDI_NETWORK_ERR_PRINT_LP_FILE_OVER_SIZE -4010 //the size of the uploaded file cannot exceed 1 GB @@ -95,7 +97,7 @@ namespace QDT { #define QIDI_NETWORK_LIBRARY "qidi_networking" #define QIDI_NETWORK_AGENT_NAME "qidi_network_agent" -#define QIDI_NETWORK_AGENT_VERSION "01.10.01.01" +#define QIDI_NETWORK_AGENT_VERSION "01.10.02.28" //iot preset type strings #define IOT_PRINTER_TYPE_STRING "printer" @@ -253,6 +255,13 @@ struct CertificateInformation { std::string serial_number; }; +enum class MessageFlag : int +{ + MSG_FLAG_NONE = 0, + MSG_SIGN = 1 << 0, + MSG_ENCRYPT = 1 << 1, +}; + } #endif