From 70c0b116fce7c41db9c59e5d495bf5f0d11e95c6 Mon Sep 17 00:00:00 2001 From: Kunlong Ma Date: Tue, 1 Apr 2025 14:40:07 +0800 Subject: [PATCH 01/27] FIX: fix some issue in sending files to external storage through cloud JIRA: STUDIO-9372 STUDIO-9374 STUDIO-9368 If the printer version does not support uploading, the original protocol will be used Change-Id: I3d47ac2567c2c6709a5b983ff1ad552d9a8606d4 (cherry picked from commit b8dde8ae7f4f5883fc163c57bb607a08ecdabf2b) --- src/slic3r/GUI/DeviceManager.cpp | 1 + src/slic3r/GUI/DeviceManager.hpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/slic3r/GUI/DeviceManager.cpp b/src/slic3r/GUI/DeviceManager.cpp index d4ece5ebbe..8d1f633b7a 100644 --- a/src/slic3r/GUI/DeviceManager.cpp +++ b/src/slic3r/GUI/DeviceManager.cpp @@ -5562,6 +5562,7 @@ void MachineObject::parse_new_info(json print) is_support_nozzle_blob_detection = get_flag_bits(fun, 13); is_support_upgrade_kit = get_flag_bits(cfg, 14); is_support_command_homing = get_flag_bits(fun, 32); + is_support_brtc = get_flag_bits(fun, 31); } /*aux*/ diff --git a/src/slic3r/GUI/DeviceManager.hpp b/src/slic3r/GUI/DeviceManager.hpp index 85263899e8..5ba30c8837 100644 --- a/src/slic3r/GUI/DeviceManager.hpp +++ b/src/slic3r/GUI/DeviceManager.hpp @@ -870,6 +870,7 @@ public: bool is_support_agora{false}; bool is_support_upgrade_kit{false}; bool is_support_command_homing { false };// fun[32] + bool is_support_brtc { false }; // fun[31], support tcp and upload protocol bool installed_upgrade_kit{false}; int nozzle_max_temperature = -1; From 32c055ca7ad517e8ddeadb76f532ba112266ba47 Mon Sep 17 00:00:00 2001 From: milk Date: Mon, 21 Apr 2025 21:00:14 +0800 Subject: [PATCH 02/27] ENH: Optimization of File Transfer System Part1 jira: [STUDIO-11777] Change-Id: I733fd3532caa19546763ab8a72eb7667d5ffec53 (cherry picked from commit aa52c99076a78e2c8fd8d4e4b0b64de0bc761469) --- src/slic3r/GUI/DeviceManager.cpp | 23 ++++++++++++++++++++ src/slic3r/GUI/DeviceManager.hpp | 2 ++ src/slic3r/GUI/Printer/PrinterFileSystem.cpp | 6 ++++- src/slic3r/GUI/Printer/PrinterFileSystem.h | 1 + 4 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/DeviceManager.cpp b/src/slic3r/GUI/DeviceManager.cpp index 8d1f633b7a..8e2bb16905 100644 --- a/src/slic3r/GUI/DeviceManager.cpp +++ b/src/slic3r/GUI/DeviceManager.cpp @@ -535,6 +535,13 @@ bool MachineObject::is_lan_mode_printer() const return result; } +std::string MachineObject::convertToIp(long long ip) +{ + std::stringstream ss; + ss << ((ip >> 0) & 0xFF) << "." << ((ip >> 8) & 0xFF) << "." << ((ip >> 16) & 0xFF) << "." << ((ip >> 24) & 0xFF); + return ss.str(); +} + PrinterSeries MachineObject::get_printer_series() const { std::string series = DeviceManager::get_printer_series(printer_type); @@ -3423,8 +3430,24 @@ int MachineObject::parse_json(std::string payload, bool key_field_only) if (jj["net"].contains("conf")) { network_wired = (jj["net"]["conf"].get() & (0x1)) != 0; } + if (jj["net"].contains("info")) { + for (auto info_item = jj["net"]["info"].begin(); info_item != jj["net"]["info"].end(); info_item++) { + + if (info_item->contains("ip")) { + auto tmp_dev_ip = (*info_item)["ip"].get(); + if (tmp_dev_ip == 0) + continue ; + else { + set_dev_ip(convertToIp(tmp_dev_ip)); + } + } else { + break; + } + } + } } } + #pragma endregion #pragma region online diff --git a/src/slic3r/GUI/DeviceManager.hpp b/src/slic3r/GUI/DeviceManager.hpp index 5ba30c8837..60cba81537 100644 --- a/src/slic3r/GUI/DeviceManager.hpp +++ b/src/slic3r/GUI/DeviceManager.hpp @@ -509,6 +509,7 @@ public: int subscribe_counter{3}; std::string dev_connection_type; /* lan | cloud */ std::string connection_type() { return dev_connection_type; } + std::string dev_connection_name; /* lan | eth */ void set_dev_ip(std::string ip) {dev_ip = ip;} std::string get_ftp_folder(); @@ -520,6 +521,7 @@ public: void erase_user_access_code(); std::string get_user_access_code() const; bool is_lan_mode_printer() const; + std::string convertToIp(long long ip); //PRINTER_TYPE printer_type = PRINTER_3DPrinter_UKNOWN; std::string printer_type; /* model_id */ diff --git a/src/slic3r/GUI/Printer/PrinterFileSystem.cpp b/src/slic3r/GUI/Printer/PrinterFileSystem.cpp index f97ea156bc..48753a8393 100644 --- a/src/slic3r/GUI/Printer/PrinterFileSystem.cpp +++ b/src/slic3r/GUI/Printer/PrinterFileSystem.cpp @@ -1232,7 +1232,7 @@ void PrinterFileSystem::Reconnect(boost::unique_lock &l, int resul if (m_session.owner == nullptr) return; json r; - while (!m_callbacks.empty()) { + while(!m_callbacks.empty()) { auto c = m_callbacks.front(); m_callbacks.pop_front(); ++m_sequence; @@ -1245,6 +1245,8 @@ void PrinterFileSystem::Reconnect(boost::unique_lock &l, int resul while (m_stopped) { if (m_session.owner == nullptr) return; + m_status = Status::Stopped; + SendChangedEvent(EVT_STATUS_CHANGED, m_status); m_cond.wait(l); } wxLogMessage("PrinterFileSystem::Reconnect Initializing"); @@ -1271,6 +1273,7 @@ void PrinterFileSystem::Reconnect(boost::unique_lock &l, int resul Bambu_Tunnel tunnel = nullptr; int ret = Bambu_Create(&tunnel, url.c_str()); if (ret == 0) { + Bambu_SetLogger(tunnel, DumpLog, this); ret = Bambu_Open(tunnel); } @@ -1297,6 +1300,7 @@ void PrinterFileSystem::Reconnect(boost::unique_lock &l, int resul } wxLogMessage("PrinterFileSystem::Reconnect Failed"); m_status = Status::Failed; + SendChangedEvent(EVT_STATUS_CHANGED, m_status, "", url.size() < 2 ? 1 : m_last_error); m_cond.timed_wait(l, boost::posix_time::seconds(10)); } diff --git a/src/slic3r/GUI/Printer/PrinterFileSystem.h b/src/slic3r/GUI/Printer/PrinterFileSystem.h index 237378744c..43a53c8687 100644 --- a/src/slic3r/GUI/Printer/PrinterFileSystem.h +++ b/src/slic3r/GUI/Printer/PrinterFileSystem.h @@ -170,6 +170,7 @@ public: ListSyncing, ListReady, Failed, + Stopped, }; Status GetStatus() const { return m_status; } From 10428f71c0f52bd06839e6223b26ddb30b6c7dfd Mon Sep 17 00:00:00 2001 From: milk Date: Sun, 27 Apr 2025 15:30:43 +0800 Subject: [PATCH 03/27] ENH:Optimization of File Transfer System Part2 jira: [STUDIO-11777] Change-Id: I12744db7d2e3b53425454d632533768c54524677 (cherry picked from commit 4358e9ce351c5784e392a75d1ffcf2f54e1916ec) --- src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/DeviceManager.hpp | 2 +- src/slic3r/GUI/Printer/PrinterFileSystem.cpp | 6 +- src/slic3r/GUI/Printer/PrinterFileSystem.h | 2 +- src/slic3r/GUI/Widgets/AnimaController.cpp | 77 ++++++++++++++++++++ src/slic3r/GUI/Widgets/AnimaController.hpp | 28 +++++++ src/slic3r/GUI/Widgets/ProgressBar.cpp | 42 +++++------ 7 files changed, 133 insertions(+), 26 deletions(-) create mode 100644 src/slic3r/GUI/Widgets/AnimaController.cpp create mode 100644 src/slic3r/GUI/Widgets/AnimaController.hpp diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 8af6530b8a..0e954f93b1 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -4,6 +4,8 @@ project(libslic3r_gui) include(PrecompiledHeader) set(SLIC3R_GUI_SOURCES + GUI/Widgets/AnimaController.hpp + GUI/Widgets/AnimaController.cpp Config/Snapshot.cpp Config/Snapshot.hpp Config/Version.cpp diff --git a/src/slic3r/GUI/DeviceManager.hpp b/src/slic3r/GUI/DeviceManager.hpp index 60cba81537..e3c90b884b 100644 --- a/src/slic3r/GUI/DeviceManager.hpp +++ b/src/slic3r/GUI/DeviceManager.hpp @@ -872,7 +872,7 @@ public: bool is_support_agora{false}; bool is_support_upgrade_kit{false}; bool is_support_command_homing { false };// fun[32] - bool is_support_brtc { false }; // fun[31], support tcp and upload protocol + bool is_support_brtc{false}; // fun[31], support tcp and upload protocol bool installed_upgrade_kit{false}; int nozzle_max_temperature = -1; diff --git a/src/slic3r/GUI/Printer/PrinterFileSystem.cpp b/src/slic3r/GUI/Printer/PrinterFileSystem.cpp index 48753a8393..28ac337ae7 100644 --- a/src/slic3r/GUI/Printer/PrinterFileSystem.cpp +++ b/src/slic3r/GUI/Printer/PrinterFileSystem.cpp @@ -1245,9 +1245,9 @@ void PrinterFileSystem::Reconnect(boost::unique_lock &l, int resul while (m_stopped) { if (m_session.owner == nullptr) return; - m_status = Status::Stopped; - SendChangedEvent(EVT_STATUS_CHANGED, m_status); - m_cond.wait(l); + m_status = Status::Reconnecting; + SendChangedEvent(EVT_STATUS_CHANGED, m_status); + m_cond.wait(l); } wxLogMessage("PrinterFileSystem::Reconnect Initializing"); m_status = Status::Initializing; diff --git a/src/slic3r/GUI/Printer/PrinterFileSystem.h b/src/slic3r/GUI/Printer/PrinterFileSystem.h index 43a53c8687..5a40e063d6 100644 --- a/src/slic3r/GUI/Printer/PrinterFileSystem.h +++ b/src/slic3r/GUI/Printer/PrinterFileSystem.h @@ -170,7 +170,7 @@ public: ListSyncing, ListReady, Failed, - Stopped, + Reconnecting, }; Status GetStatus() const { return m_status; } diff --git a/src/slic3r/GUI/Widgets/AnimaController.cpp b/src/slic3r/GUI/Widgets/AnimaController.cpp new file mode 100644 index 0000000000..10f0061dbf --- /dev/null +++ b/src/slic3r/GUI/Widgets/AnimaController.cpp @@ -0,0 +1,77 @@ +#include "AnimaController.hpp" + +#include +#include +#ifdef __APPLE__ +#include "libslic3r/MacUtils.hpp" +#endif + +AnimaIcon::AnimaIcon(wxWindow *parent, wxWindowID id, std::vector img_list, std::string img_enable, int ivt) + : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize), m_ivt(ivt) +{ + SetBackgroundColour((wxColour(255, 255, 255))); + m_size = 20; + + //add ScalableBitmap + for (const auto &filename : img_list) m_images.emplace_back(create_scaled_bitmap(filename, this, m_size)); + m_image_enable = create_scaled_bitmap(img_enable, this, m_size-8); + + // show first wxStaticBitmap + if (!m_images.empty()) m_bitmap = new wxStaticBitmap(this, wxID_ANY, m_images[0], wxDefaultPosition, wxSize(FromDIP(m_size), FromDIP(m_size))); + + + m_timer = new wxTimer(); + m_timer->SetOwner(this); + + Bind(wxEVT_TIMER, [this](wxTimerEvent &) { + if (m_timer->IsRunning() && !m_images.empty()) { + m_current_frame = (m_current_frame + 1) % 4; + m_bitmap->SetBitmap(m_images[m_current_frame]); + } + }); + + m_bitmap->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &e) { + wxMouseEvent evt(wxEVT_LEFT_DOWN); + evt.SetEventObject(this); + wxPostEvent(this, evt); + }); + + m_bitmap->Bind(wxEVT_ENTER_WINDOW, [this](auto &e) { + if (!m_timer->IsRunning()) + SetCursor(wxCursor(wxCURSOR_HAND)); + else + SetCursor(wxCursor(wxCURSOR_ARROW)); + e.Skip(); + }); + m_bitmap->Bind(wxEVT_LEAVE_WINDOW, [this](auto &e) { + SetCursor(wxCursor(wxCURSOR_ARROW)); + e.Skip(); + }); + + SetSize(wxSize(FromDIP(m_size), FromDIP(m_size))); + SetMaxSize(wxSize(FromDIP(m_size), FromDIP(m_size))); + SetMinSize(wxSize(FromDIP(m_size), FromDIP(m_size))); + Refresh(); + + Play(); +} + + +void AnimaIcon::Play() +{ + if (true) + m_timer->Start(m_ivt); + +} + +void AnimaIcon::Stop() +{ + m_timer->Stop(); +} + +void AnimaIcon::Enable() +{ + if (m_bitmap) { m_bitmap->SetBitmap(m_image_enable); } +} + + diff --git a/src/slic3r/GUI/Widgets/AnimaController.hpp b/src/slic3r/GUI/Widgets/AnimaController.hpp new file mode 100644 index 0000000000..ac8caffc6a --- /dev/null +++ b/src/slic3r/GUI/Widgets/AnimaController.hpp @@ -0,0 +1,28 @@ +#ifndef slic3r_GUI_AnimaController_hpp_ +#define slic3r_GUI_AnimaController_hpp_ + +#include "../wxExtensions.hpp" +#include "Label.hpp" + + +class AnimaIcon : public wxPanel +{ +public: + AnimaIcon(wxWindow *parent, wxWindowID id, std::vector img_list, std::string img_enable, int ivt = 1000); + + void Play(); + void Stop(); + void Enable(); + bool IsRunning() const; + +private: + wxBitmap m_image_enable; + wxStaticBitmap * m_bitmap{nullptr}; + std::vector m_images; + wxTimer * m_timer; + int m_current_frame = 0; + int m_ivt; + int m_size; +}; + +#endif // !slic3r_GUI_AnimaController_hpp_ diff --git a/src/slic3r/GUI/Widgets/ProgressBar.cpp b/src/slic3r/GUI/Widgets/ProgressBar.cpp index c89c14dd0f..6c38d80a86 100644 --- a/src/slic3r/GUI/Widgets/ProgressBar.cpp +++ b/src/slic3r/GUI/Widgets/ProgressBar.cpp @@ -15,7 +15,7 @@ ProgressBar::ProgressBar(wxWindow *parent, wxWindowID id, int max, const wxPoint { m_shownumber = shown; SetBackgroundColour(wxColour(255,255,255)); - + if (size.y >= miniHeight) { m_miniHeight = size.y; } else { @@ -64,20 +64,20 @@ void ProgressBar::create(wxWindow *parent, wxWindowID id, const wxPoint &pos, w } -void ProgressBar::SetRadius(double radius) { +void ProgressBar::SetRadius(double radius) { m_radius = radius; Refresh(); } -void ProgressBar::SetProgressForedColour(wxColour colour) +void ProgressBar::SetProgressForedColour(wxColour colour) { m_progress_background_colour = colour; Refresh(); } -void ProgressBar::SetProgressBackgroundColour(wxColour colour) -{ - m_progress_colour = colour; +void ProgressBar::SetProgressBackgroundColour(wxColour colour) +{ + m_progress_colour = colour; Refresh(); } @@ -86,29 +86,29 @@ void ProgressBar::Rescale() ; } -void ProgressBar::ShowNumber(bool shown) +void ProgressBar::ShowNumber(bool shown) { m_shownumber = shown; Refresh(); } -void ProgressBar::Disable(wxString text) -{ +void ProgressBar::Disable(wxString text) +{ if (m_disable) return; m_disable_text = text; m_disable = true; Refresh(); } -void ProgressBar::SetValue(int step) -{ +void ProgressBar::SetValue(int step) +{ m_disable = false; SetProgress(step); } -void ProgressBar::Reset() -{ - m_step = 0; +void ProgressBar::Reset() +{ + m_step = 0; SetValue(0); } @@ -122,9 +122,9 @@ void ProgressBar::SetProgress(int step) } -void ProgressBar::SetMinSize(const wxSize &size) -{ - if (size.y >= miniHeight) { +void ProgressBar::SetMinSize(const wxSize &size) +{ + if (size.y >= miniHeight) { m_miniHeight = size.y; } else { return; @@ -177,7 +177,7 @@ void ProgressBar::doRender(wxDC &dc) dc.DrawRoundedRectangle(0, 0, size.x, size.y, m_radius); } - //draw progress + //draw progress if (m_disable) { m_proportion = float(size.x * float(this->m_step) / float(this->m_max)); if (m_proportion < m_radius * 2 && m_proportion != 0) { m_proportion = m_radius * 2; } @@ -228,11 +228,11 @@ void ProgressBar::doRender(wxDC &dc) dc.DrawText(text + wxString("%"), pt); } } - + } -void ProgressBar::DoSetSize(int x, int y, int width, int height, int sizeFlags) -{ +void ProgressBar::DoSetSize(int x, int y, int width, int height, int sizeFlags) +{ wxWindow::DoSetSize(x, y, width, height, sizeFlags); } From df5bf0ae29e212ae5ff81cded2945c11c5582741 Mon Sep 17 00:00:00 2001 From: hemai Date: Fri, 20 Jun 2025 17:59:30 +0800 Subject: [PATCH 04/27] ENH: support parts skipping function Jira: STUDIO-12687 Change-Id: I244cb611954590bd5e741f0d2701f359426a33a2 (cherry picked from commit e7e90e0f8ca0106a51d18d83efa0de56b332ddc0) --- .gitignore | 3 + resources/images/canvas_drag.svg | 3 + resources/images/canvas_drag_active.svg | 3 + resources/images/canvas_zoom_in.svg | 3 + resources/images/canvas_zoom_in_disable.svg | 3 + resources/images/canvas_zoom_out.svg | 3 + resources/images/canvas_zoom_out_disable.svg | 3 + resources/images/print_control_partskip.svg | 4 + .../images/print_control_partskip_disable.svg | 4 + src/earcut/CHANGELOG.md | 27 + src/earcut/CMakeLists.txt | 151 +++ src/earcut/LICENSE | 15 + src/earcut/README.md | 131 +++ src/earcut/earcut.hpp | 814 +++++++++++++++++ src/slic3r/CMakeLists.txt | 5 + src/slic3r/GUI/DeviceManager.cpp | 25 +- src/slic3r/GUI/DeviceManager.hpp | 4 + src/slic3r/GUI/PartSkipCommon.hpp | 18 + src/slic3r/GUI/PartSkipDialog.cpp | 863 ++++++++++++++++++ src/slic3r/GUI/PartSkipDialog.hpp | 153 ++++ src/slic3r/GUI/Printer/PrinterFileSystem.cpp | 141 ++- src/slic3r/GUI/Printer/PrinterFileSystem.h | 19 +- src/slic3r/GUI/SkipPartCanvas.cpp | 628 +++++++++++++ src/slic3r/GUI/SkipPartCanvas.hpp | 163 ++++ src/slic3r/GUI/StatusPanel.cpp | 73 +- src/slic3r/GUI/StatusPanel.hpp | 12 +- 26 files changed, 3262 insertions(+), 9 deletions(-) create mode 100644 resources/images/canvas_drag.svg create mode 100644 resources/images/canvas_drag_active.svg create mode 100644 resources/images/canvas_zoom_in.svg create mode 100644 resources/images/canvas_zoom_in_disable.svg create mode 100644 resources/images/canvas_zoom_out.svg create mode 100644 resources/images/canvas_zoom_out_disable.svg create mode 100644 resources/images/print_control_partskip.svg create mode 100644 resources/images/print_control_partskip_disable.svg create mode 100644 src/earcut/CHANGELOG.md create mode 100644 src/earcut/CMakeLists.txt create mode 100644 src/earcut/LICENSE create mode 100644 src/earcut/README.md create mode 100644 src/earcut/earcut.hpp create mode 100644 src/slic3r/GUI/PartSkipCommon.hpp create mode 100644 src/slic3r/GUI/PartSkipDialog.cpp create mode 100644 src/slic3r/GUI/PartSkipDialog.hpp create mode 100644 src/slic3r/GUI/SkipPartCanvas.cpp create mode 100644 src/slic3r/GUI/SkipPartCanvas.hpp diff --git a/.gitignore b/.gitignore index 9c59ebdab5..09abcc8dee 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,6 @@ src/OrcaSlicer-doc/ resources/profiles/user/default *.code-workspace deps_src/build/ +test.js +/.cache/ +.clangd diff --git a/resources/images/canvas_drag.svg b/resources/images/canvas_drag.svg new file mode 100644 index 0000000000..821fa700de --- /dev/null +++ b/resources/images/canvas_drag.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/canvas_drag_active.svg b/resources/images/canvas_drag_active.svg new file mode 100644 index 0000000000..5aa863efee --- /dev/null +++ b/resources/images/canvas_drag_active.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/canvas_zoom_in.svg b/resources/images/canvas_zoom_in.svg new file mode 100644 index 0000000000..3fbf01ee09 --- /dev/null +++ b/resources/images/canvas_zoom_in.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/canvas_zoom_in_disable.svg b/resources/images/canvas_zoom_in_disable.svg new file mode 100644 index 0000000000..dc3388481f --- /dev/null +++ b/resources/images/canvas_zoom_in_disable.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/canvas_zoom_out.svg b/resources/images/canvas_zoom_out.svg new file mode 100644 index 0000000000..70d98c9c00 --- /dev/null +++ b/resources/images/canvas_zoom_out.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/canvas_zoom_out_disable.svg b/resources/images/canvas_zoom_out_disable.svg new file mode 100644 index 0000000000..be4f1d5a18 --- /dev/null +++ b/resources/images/canvas_zoom_out_disable.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/print_control_partskip.svg b/resources/images/print_control_partskip.svg new file mode 100644 index 0000000000..21fe0bd031 --- /dev/null +++ b/resources/images/print_control_partskip.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/images/print_control_partskip_disable.svg b/resources/images/print_control_partskip_disable.svg new file mode 100644 index 0000000000..79d8a90432 --- /dev/null +++ b/resources/images/print_control_partskip_disable.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/earcut/CHANGELOG.md b/src/earcut/CHANGELOG.md new file mode 100644 index 0000000000..41d9e6703b --- /dev/null +++ b/src/earcut/CHANGELOG.md @@ -0,0 +1,27 @@ +## Earcut.hpp changelog + +### master + + - Fixed a bunch of rare edge cases that led to bad triangulation (parity with Earcut v2.2.2) + - Removed use of deprecated `std::allocator::construct` + - Fixed a minor z-order hashing bug + - Improved visualization app, better docs + +### v0.12.4 + + - Fixed a crash in Crash in Earcut::findHoleBridge + - Added coverage checks + - Added macOS, MinGW builds + +### v0.12.3 + + - Fixed -Wunused-lambda-capture + +### v0.12.2 + + - Fixed potential division by zero + - Fixed -fsanitize=integer warning + +### v0.12.1 + + - Fixed cast precision warning diff --git a/src/earcut/CMakeLists.txt b/src/earcut/CMakeLists.txt new file mode 100644 index 0000000000..08c7bb1bed --- /dev/null +++ b/src/earcut/CMakeLists.txt @@ -0,0 +1,151 @@ +cmake_minimum_required(VERSION 3.2) +project(earcut_hpp LANGUAGES CXX C) + +option(EARCUT_BUILD_TESTS "Build the earcut test program" ON) +option(EARCUT_BUILD_BENCH "Build the earcut benchmark program" ON) +option(EARCUT_BUILD_VIZ "Build the earcut visualizer program" ON) +option(EARCUT_WARNING_IS_ERROR "Treat warnings as errors" OFF) + +if (NOT CMAKE_BUILD_TYPE AND NOT GENERATOR_IS_MULTI_CONFIG) + message(STATUS "No build type specified. Setting to 'Release'") + set(CMAKE_BUILD_TYPE "Release" CACHE STRING "The type of build." FORCE) +endif() + + +include(GNUInstallDirs) + +add_library(earcut_hpp INTERFACE) +add_library(earcut_hpp::earcut_hpp ALIAS earcut_hpp) + +target_include_directories(earcut_hpp INTERFACE + $ + $ +) + +set(CMAKE_CXX_STANDARD 11) + +if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" GREATER 3.7) + # Allow C++11 requirements to propagate when using recent CMake versions + target_compile_features(earcut_hpp INTERFACE cxx_std_11) +endif() + +file(GLOB FIXTURE_SOURCE_FILES test/fixtures/*.cpp test/fixtures/*.hpp) +source_group(fixtures FILES ${FIXTURE_SOURCE_FILES}) +add_library(fixtures OBJECT ${FIXTURE_SOURCE_FILES}) +target_compile_options(fixtures PRIVATE $<$:/Od>) + +# In CMake 3.12, use target_link_libraries(fixtures PUBLIC earcut_hpp libtess2). +# Since we support down to CMake 3.2, we need to manually propagate usage requirements of earcut_hpp +target_include_directories(fixtures PRIVATE "$") +target_compile_features(fixtures PRIVATE "$") + + +file(GLOB COMPARISON_SOURCE_FILES test/comparison/*.cpp test/comparison/*.hpp) +source_group(comparison FILES ${COMPARISON_SOURCE_FILES}) +# this is interface since there is no cpp files in the comparison directory +add_library(comparison INTERFACE) + + +file(GLOB LIBTESS2_SOURCE_FILES test/comparison/libtess2/*.c test/comparison/libtess2/*.h) +source_group(comparison/libtess2 FILES ${LIBTESS2_SOURCE_FILES}) +add_library(libtess2 ${LIBTESS2_SOURCE_FILES}) +target_compile_options(libtess2 PRIVATE + $<$:/wd4244 /wd4267> + $<$>:-w> +) + +add_library(common INTERFACE) +target_link_libraries(common INTERFACE libtess2 comparison) + +# optional: -march=native (builds with the optimizations available on the build machine (only for local use!)) +target_compile_options(common INTERFACE + $<$>:-pipe -Wall -Wextra -Wconversion -Wpedantic> +) + +if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang$" OR CMAKE_COMPILER_IS_GNUCXX) + if ("${CMAKE_CXX_FLAGS}" MATCHES "--coverage") + # We disable debug code for the coverage so it won't see assertion and other things only enabled for debugging + target_compile_definitions(common INTERFACE NDEBUG) + else() + # Here we enable the undefined behavior sanitizer for the tests, benchmarks and the viz + include(CheckCXXCompilerFlag) + check_cxx_compiler_flag("-fsanitize=undefined" HAVE_FLAG_SANITIZE_UNDEFINED) + if(HAVE_FLAG_SANITIZE_UNDEFINED) + target_compile_options(common INTERFACE $<$:-fsanitize=undefined>) + # TODO: Replace with target link option once we support CMake 3.13 + target_link_libraries(common INTERFACE $<$:-fsanitize=undefined>) + endif() + endif() +endif() + +if (EARCUT_WARNING_IS_ERROR) + target_compile_options(common INTERFACE + $<$:/WX> + $<$>:-Werror> + ) +endif() + +if (EARCUT_BUILD_TESTS) + enable_testing() + add_executable(tests test/tap.cpp test/tap.hpp test/test.cpp $) + target_link_libraries(tests PRIVATE earcut_hpp common) + add_test(NAME earcut_tests COMMAND tests) +endif() +if (EARCUT_BUILD_BENCH) + add_executable(bench test/bench.cpp $) + target_link_libraries(bench PRIVATE earcut_hpp common) +endif() +if (EARCUT_BUILD_VIZ) + add_executable(viz test/viz.cpp $) + + # Setup viz target + # OpenGL + # linux: xorg-dev libgl1-mesa-glx libgl1-mesa-dev + # windows: in the windows sdk + find_package(OpenGL REQUIRED) + + # GLFW3 + find_package(glfw3 QUIET) # try to use the system default + if (NOT glfw3_FOUND) + if(EXISTS "${PROJECT_SOURCE_DIR}/.gitmodules") + find_package(Git REQUIRED) + execute_process( + COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + OUTPUT_QUIET + ERROR_QUIET + ) + endif() + + set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "Build the GLFW example programs" FORCE) + set(GLFW_BUILD_TESTS OFF CACHE BOOL "Build the GLFW test programs" FORCE) + set(GLFW_BUILD_DOCS OFF CACHE BOOL "Build the GLFW documentation" FORCE) + set(GLFW_INSTALL OFF CACHE BOOL "Generate installation target" FORCE) + add_subdirectory(glfw) + endif() + + target_compile_definitions(viz PRIVATE GL_SILENCE_DEPRECATION) + + # TODO: Using old variables for OpenGL package since they were added in CMake 3.8 + target_link_libraries(viz PRIVATE earcut_hpp common glfw ${OPENGL_LIBRARIES}) + target_include_directories(viz PRIVATE ${OPENGL_INCLUDE_DIR}) +endif() + +install( + DIRECTORY include/mapbox + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "*.hpp" +) + +install(TARGETS earcut_hpp EXPORT earcut_hpp-config) + +# Since there is two projects, we need to export into the parent directory +export( + TARGETS earcut_hpp + NAMESPACE earcut_hpp:: + FILE "${PROJECT_BINARY_DIR}/earcut_hpp-config.cmake" +) + +install(EXPORT earcut_hpp-config + DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/earcut_hpp" + NAMESPACE earcut_hpp:: +) diff --git a/src/earcut/LICENSE b/src/earcut/LICENSE new file mode 100644 index 0000000000..8bafb57730 --- /dev/null +++ b/src/earcut/LICENSE @@ -0,0 +1,15 @@ +ISC License + +Copyright (c) 2015, Mapbox + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. diff --git a/src/earcut/README.md b/src/earcut/README.md new file mode 100644 index 0000000000..67f235b7ff --- /dev/null +++ b/src/earcut/README.md @@ -0,0 +1,131 @@ +## Earcut + +A C++ port of [earcut.js](https://github.com/mapbox/earcut), a fast, [header-only](https://github.com/mapbox/earcut.hpp/blob/master/include/mapbox/earcut.hpp) polygon triangulation library. + +[![Travis](https://img.shields.io/travis/com/mapbox/earcut.hpp.svg)](https://travis-ci.com/github/mapbox/earcut.hpp) +[![AppVeyor](https://ci.appveyor.com/api/projects/status/a1ysrqd69mqn7coo/branch/master?svg=true)](https://ci.appveyor.com/project/Mapbox/earcut-hpp-8wm4o/branch/master) +[![Coverage](https://img.shields.io/coveralls/github/mapbox/earcut.hpp.svg)](https://coveralls.io/github/mapbox/earcut.hpp) +[![Coverity Scan](https://img.shields.io/coverity/scan/14000.svg)](https://scan.coverity.com/projects/14000) +[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/mapbox/earcut.hpp.svg)](http://isitmaintained.com/project/mapbox/earcut.hpp "Average time to resolve an issue") +[![Percentage of issues still open](http://isitmaintained.com/badge/open/mapbox/earcut.hpp.svg)](http://isitmaintained.com/project/mapbox/earcut.hpp "Percentage of issues still open") +[![Mourner](https://img.shields.io/badge/simply-awesome-brightgreen.svg)](https://github.com/mourner/projects) + +The library implements a modified ear slicing algorithm, optimized by [z-order curve](http://en.wikipedia.org/wiki/Z-order_curve) hashing and extended to handle holes, twisted polygons, degeneracies and self-intersections in a way that doesn't _guarantee_ correctness of triangulation, but attempts to always produce acceptable results for practical data like geographical shapes. + +It's based on ideas from [FIST: Fast Industrial-Strength Triangulation of Polygons](http://www.cosy.sbg.ac.at/~held/projects/triang/triang.html) by Martin Held and [Triangulation by Ear Clipping](http://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf) by David Eberly. + +## Usage + +```cpp +#include +``` +```cpp +// The number type to use for tessellation +using Coord = double; + +// The index type. Defaults to uint32_t, but you can also pass uint16_t if you know that your +// data won't have more than 65536 vertices. +using N = uint32_t; + +// Create array +using Point = std::array; +std::vector> polygon; + +// Fill polygon structure with actual data. Any winding order works. +// The first polyline defines the main polygon. +polygon.push_back({{100, 0}, {100, 100}, {0, 100}, {0, 0}}); +// Following polylines define holes. +polygon.push_back({{75, 25}, {75, 75}, {25, 75}, {25, 25}}); + +// Run tessellation +// Returns array of indices that refer to the vertices of the input polygon. +// e.g: the index 6 would refer to {25, 75} in this example. +// Three subsequent indices form a triangle. Output triangles are clockwise. +std::vector indices = mapbox::earcut(polygon); +``` + +Earcut can triangulate a simple, planar polygon of any winding order including holes. It will even return a robust, acceptable solution for non-simple poygons. Earcut works on a 2D plane. If you have three or more dimensions, you can project them onto a 2D surface before triangulation, or use a more suitable library for the task (e.g [CGAL](https://doc.cgal.org/latest/Triangulation_3/index.html)). + + +It is also possible to use your custom point type as input. There are default accessors defined for `std::tuple`, `std::pair`, and `std::array`. For a custom type (like Clipper's `IntPoint` type), do this: + +```cpp +// struct IntPoint { +// int64_t X, Y; +// }; + +namespace mapbox { +namespace util { + +template <> +struct nth<0, IntPoint> { + inline static auto get(const IntPoint &t) { + return t.X; + }; +}; +template <> +struct nth<1, IntPoint> { + inline static auto get(const IntPoint &t) { + return t.Y; + }; +}; + +} // namespace util +} // namespace mapbox +``` + +You can also use a custom container type for your polygon. Similar to std::vector, it has to meet the requirements of [Container](https://en.cppreference.com/w/cpp/named_req/Container), in particular `size()`, `empty()` and `operator[]`. + +

+ example triangulation +

+ +## Additional build instructions +In case you just want to use the earcut triangulation library; copy and include the header file [``](https://github.com/mapbox/earcut.hpp/blob/master/include/mapbox/earcut.hpp) in your project and follow the steps documented in the section [Usage](#usage). + +If you want to build the test, benchmark and visualization programs instead, follow these instructions: + +### Dependencies + +Before you continue, make sure to have the following tools and libraries installed: + * git ([Ubuntu](https://help.ubuntu.com/lts/serverguide/git.html)/[Windows/macOS](http://git-scm.com/downloads)) + * cmake 3.2+ ([Ubuntu](https://launchpad.net/~george-edison55/+archive/ubuntu/cmake-3.x)/[Windows/macOS](https://cmake.org/download/)) + * OpenGL SDK ([Ubuntu](http://packages.ubuntu.com/de/trusty/libgl1-mesa-dev)/[Windows](https://dev.windows.com/en-us/downloads/windows-10-sdk)/[macOS](https://developer.apple.com/opengl/)) + * Compiler such as [GCC 4.9+, Clang 3.4+](https://launchpad.net/~ubuntu-toolchain-r/+archive/ubuntu/test), [MSVC12+](https://www.visualstudio.com/) + +Note: On some operating systems such as Windows, manual steps are required to add cmake and [git](http://blog.countableset.ch/2012/06/07/adding-git-to-windows-7-path/) to your PATH environment variable. + +### Manual compilation + +```bash +git clone --recursive https://github.com/mapbox/earcut.hpp.git +cd earcut.hpp +mkdir build +cd build +cmake .. +make +# ./tests +# ./bench +# ./viz +``` + +### [Visual Studio](https://www.visualstudio.com/), [Eclipse](https://eclipse.org/), [XCode](https://developer.apple.com/xcode/), ... + +```batch +git clone --recursive https://github.com/mapbox/earcut.hpp.git +cd earcut.hpp +mkdir project +cd project +cmake .. -G "Visual Studio 14 2015" +::you can also generate projects for "Visual Studio 12 2013", "XCode", "Eclipse CDT4 - Unix Makefiles" +``` +After completion, open the generated project with your IDE. + + +### [CLion](https://www.jetbrains.com/clion/), [Visual Studio 2017+](https://www.visualstudio.com/) + +Import the project from https://github.com/mapbox/earcut.hpp.git and you should be good to go! + +## Status + +This is currently based on [earcut 2.2.4](https://github.com/mapbox/earcut#224-jul-5-2022). diff --git a/src/earcut/earcut.hpp b/src/earcut/earcut.hpp new file mode 100644 index 0000000000..fd3381484c --- /dev/null +++ b/src/earcut/earcut.hpp @@ -0,0 +1,814 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mapbox { + +namespace util { + +template struct nth { + inline static typename std::tuple_element::type + get(const T& t) { return std::get(t); }; +}; + +} + +namespace detail { + +template +class Earcut { +public: + std::vector indices; + std::size_t vertices = 0; + + template + void operator()(const Polygon& points); + +private: + struct Node { + Node(N index, double x_, double y_) : i(index), x(x_), y(y_) {} + Node(const Node&) = delete; + Node& operator=(const Node&) = delete; + Node(Node&&) = delete; + Node& operator=(Node&&) = delete; + + const N i; + const double x; + const double y; + + // previous and next vertice nodes in a polygon ring + Node* prev = nullptr; + Node* next = nullptr; + + // z-order curve value + int32_t z = 0; + + // previous and next nodes in z-order + Node* prevZ = nullptr; + Node* nextZ = nullptr; + + // indicates whether this is a steiner point + bool steiner = false; + }; + + template Node* linkedList(const Ring& points, const bool clockwise); + Node* filterPoints(Node* start, Node* end = nullptr); + void earcutLinked(Node* ear, int pass = 0); + bool isEar(Node* ear); + bool isEarHashed(Node* ear); + Node* cureLocalIntersections(Node* start); + void splitEarcut(Node* start); + template Node* eliminateHoles(const Polygon& points, Node* outerNode); + Node* eliminateHole(Node* hole, Node* outerNode); + Node* findHoleBridge(Node* hole, Node* outerNode); + bool sectorContainsSector(const Node* m, const Node* p); + void indexCurve(Node* start); + Node* sortLinked(Node* list); + int32_t zOrder(const double x_, const double y_); + Node* getLeftmost(Node* start); + bool pointInTriangle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const; + bool isValidDiagonal(Node* a, Node* b); + double area(const Node* p, const Node* q, const Node* r) const; + bool equals(const Node* p1, const Node* p2); + bool intersects(const Node* p1, const Node* q1, const Node* p2, const Node* q2); + bool onSegment(const Node* p, const Node* q, const Node* r); + int sign(double val); + bool intersectsPolygon(const Node* a, const Node* b); + bool locallyInside(const Node* a, const Node* b); + bool middleInside(const Node* a, const Node* b); + Node* splitPolygon(Node* a, Node* b); + template Node* insertNode(std::size_t i, const Point& p, Node* last); + void removeNode(Node* p); + + bool hashing; + double minX, maxX; + double minY, maxY; + double inv_size = 0; + + template > + class ObjectPool { + public: + ObjectPool() { } + ObjectPool(std::size_t blockSize_) { + reset(blockSize_); + } + ~ObjectPool() { + clear(); + } + template + T* construct(Args&&... args) { + if (currentIndex >= blockSize) { + currentBlock = alloc_traits::allocate(alloc, blockSize); + allocations.emplace_back(currentBlock); + currentIndex = 0; + } + T* object = ¤tBlock[currentIndex++]; + alloc_traits::construct(alloc, object, std::forward(args)...); + return object; + } + void reset(std::size_t newBlockSize) { + for (auto allocation : allocations) { + alloc_traits::deallocate(alloc, allocation, blockSize); + } + allocations.clear(); + blockSize = std::max(1, newBlockSize); + currentBlock = nullptr; + currentIndex = blockSize; + } + void clear() { reset(blockSize); } + private: + T* currentBlock = nullptr; + std::size_t currentIndex = 1; + std::size_t blockSize = 1; + std::vector allocations; + Alloc alloc; + typedef typename std::allocator_traits alloc_traits; + }; + ObjectPool nodes; +}; + +template template +void Earcut::operator()(const Polygon& points) { + // reset + indices.clear(); + vertices = 0; + + if (points.empty()) return; + + double x; + double y; + int threshold = 80; + std::size_t len = 0; + + for (size_t i = 0; threshold >= 0 && i < points.size(); i++) { + threshold -= static_cast(points[i].size()); + len += points[i].size(); + } + + //estimate size of nodes and indices + nodes.reset(len * 3 / 2); + indices.reserve(len + points[0].size()); + + Node* outerNode = linkedList(points[0], true); + if (!outerNode || outerNode->prev == outerNode->next) return; + + if (points.size() > 1) outerNode = eliminateHoles(points, outerNode); + + // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox + hashing = threshold < 0; + if (hashing) { + Node* p = outerNode->next; + minX = maxX = outerNode->x; + minY = maxY = outerNode->y; + do { + x = p->x; + y = p->y; + minX = std::min(minX, x); + minY = std::min(minY, y); + maxX = std::max(maxX, x); + maxY = std::max(maxY, y); + p = p->next; + } while (p != outerNode); + + // minX, minY and inv_size are later used to transform coords into integers for z-order calculation + inv_size = std::max(maxX - minX, maxY - minY); + inv_size = inv_size != .0 ? (32767. / inv_size) : .0; + } + + earcutLinked(outerNode); + + nodes.clear(); +} + +// create a circular doubly linked list from polygon points in the specified winding order +template template +typename Earcut::Node* +Earcut::linkedList(const Ring& points, const bool clockwise) { + using Point = typename Ring::value_type; + double sum = 0; + const std::size_t len = points.size(); + std::size_t i, j; + Node* last = nullptr; + + // calculate original winding order of a polygon ring + for (i = 0, j = len > 0 ? len - 1 : 0; i < len; j = i++) { + const auto& p1 = points[i]; + const auto& p2 = points[j]; + const double p20 = util::nth<0, Point>::get(p2); + const double p10 = util::nth<0, Point>::get(p1); + const double p11 = util::nth<1, Point>::get(p1); + const double p21 = util::nth<1, Point>::get(p2); + sum += (p20 - p10) * (p11 + p21); + } + + // link points into circular doubly-linked list in the specified winding order + if (clockwise == (sum > 0)) { + for (i = 0; i < len; i++) last = insertNode(vertices + i, points[i], last); + } else { + for (i = len; i-- > 0;) last = insertNode(vertices + i, points[i], last); + } + + if (last && equals(last, last->next)) { + removeNode(last); + last = last->next; + } + + vertices += len; + + return last; +} + +// eliminate colinear or duplicate points +template +typename Earcut::Node* +Earcut::filterPoints(Node* start, Node* end) { + if (!end) end = start; + + Node* p = start; + bool again; + do { + again = false; + + if (!p->steiner && (equals(p, p->next) || area(p->prev, p, p->next) == 0)) { + removeNode(p); + p = end = p->prev; + + if (p == p->next) break; + again = true; + + } else { + p = p->next; + } + } while (again || p != end); + + return end; +} + +// main ear slicing loop which triangulates a polygon (given as a linked list) +template +void Earcut::earcutLinked(Node* ear, int pass) { + if (!ear) return; + + // interlink polygon nodes in z-order + if (!pass && hashing) indexCurve(ear); + + Node* stop = ear; + Node* prev; + Node* next; + + // iterate through ears, slicing them one by one + while (ear->prev != ear->next) { + prev = ear->prev; + next = ear->next; + + if (hashing ? isEarHashed(ear) : isEar(ear)) { + // cut off the triangle + indices.emplace_back(prev->i); + indices.emplace_back(ear->i); + indices.emplace_back(next->i); + + removeNode(ear); + + // skipping the next vertice leads to less sliver triangles + ear = next->next; + stop = next->next; + + continue; + } + + ear = next; + + // if we looped through the whole remaining polygon and can't find any more ears + if (ear == stop) { + // try filtering points and slicing again + if (!pass) earcutLinked(filterPoints(ear), 1); + + // if this didn't work, try curing all small self-intersections locally + else if (pass == 1) { + ear = cureLocalIntersections(filterPoints(ear)); + earcutLinked(ear, 2); + + // as a last resort, try splitting the remaining polygon into two + } else if (pass == 2) splitEarcut(ear); + + break; + } + } +} + +// check whether a polygon node forms a valid ear with adjacent nodes +template +bool Earcut::isEar(Node* ear) { + const Node* a = ear->prev; + const Node* b = ear; + const Node* c = ear->next; + + if (area(a, b, c) >= 0) return false; // reflex, can't be an ear + + // now make sure we don't have other points inside the potential ear + Node* p = ear->next->next; + + while (p != ear->prev) { + if (pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) && + area(p->prev, p, p->next) >= 0) return false; + p = p->next; + } + + return true; +} + +template +bool Earcut::isEarHashed(Node* ear) { + const Node* a = ear->prev; + const Node* b = ear; + const Node* c = ear->next; + + if (area(a, b, c) >= 0) return false; // reflex, can't be an ear + + // triangle bbox; min & max are calculated like this for speed + const double minTX = std::min(a->x, std::min(b->x, c->x)); + const double minTY = std::min(a->y, std::min(b->y, c->y)); + const double maxTX = std::max(a->x, std::max(b->x, c->x)); + const double maxTY = std::max(a->y, std::max(b->y, c->y)); + + // z-order range for the current triangle bbox; + const int32_t minZ = zOrder(minTX, minTY); + const int32_t maxZ = zOrder(maxTX, maxTY); + + // first look for points inside the triangle in increasing z-order + Node* p = ear->nextZ; + + while (p && p->z <= maxZ) { + if (p != ear->prev && p != ear->next && + pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) && + area(p->prev, p, p->next) >= 0) return false; + p = p->nextZ; + } + + // then look for points in decreasing z-order + p = ear->prevZ; + + while (p && p->z >= minZ) { + if (p != ear->prev && p != ear->next && + pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) && + area(p->prev, p, p->next) >= 0) return false; + p = p->prevZ; + } + + return true; +} + +// go through all polygon nodes and cure small local self-intersections +template +typename Earcut::Node* +Earcut::cureLocalIntersections(Node* start) { + Node* p = start; + do { + Node* a = p->prev; + Node* b = p->next->next; + + // a self-intersection where edge (v[i-1],v[i]) intersects (v[i+1],v[i+2]) + if (!equals(a, b) && intersects(a, p, p->next, b) && locallyInside(a, b) && locallyInside(b, a)) { + indices.emplace_back(a->i); + indices.emplace_back(p->i); + indices.emplace_back(b->i); + + // remove two nodes involved + removeNode(p); + removeNode(p->next); + + p = start = b; + } + p = p->next; + } while (p != start); + + return filterPoints(p); +} + +// try splitting polygon into two and triangulate them independently +template +void Earcut::splitEarcut(Node* start) { + // look for a valid diagonal that divides the polygon into two + Node* a = start; + do { + Node* b = a->next->next; + while (b != a->prev) { + if (a->i != b->i && isValidDiagonal(a, b)) { + // split the polygon in two by the diagonal + Node* c = splitPolygon(a, b); + + // filter colinear points around the cuts + a = filterPoints(a, a->next); + c = filterPoints(c, c->next); + + // run earcut on each half + earcutLinked(a); + earcutLinked(c); + return; + } + b = b->next; + } + a = a->next; + } while (a != start); +} + +// link every hole into the outer loop, producing a single-ring polygon without holes +template template +typename Earcut::Node* +Earcut::eliminateHoles(const Polygon& points, Node* outerNode) { + const size_t len = points.size(); + + std::vector queue; + for (size_t i = 1; i < len; i++) { + Node* list = linkedList(points[i], false); + if (list) { + if (list == list->next) list->steiner = true; + queue.push_back(getLeftmost(list)); + } + } + std::sort(queue.begin(), queue.end(), [](const Node* a, const Node* b) { + return a->x < b->x; + }); + + // process holes from left to right + for (size_t i = 0; i < queue.size(); i++) { + outerNode = eliminateHole(queue[i], outerNode); + } + + return outerNode; +} + +// find a bridge between vertices that connects hole with an outer ring and and link it +template +typename Earcut::Node* +Earcut::eliminateHole(Node* hole, Node* outerNode) { + Node* bridge = findHoleBridge(hole, outerNode); + if (!bridge) { + return outerNode; + } + + Node* bridgeReverse = splitPolygon(bridge, hole); + + // filter collinear points around the cuts + filterPoints(bridgeReverse, bridgeReverse->next); + + // Check if input node was removed by the filtering + return filterPoints(bridge, bridge->next); +} + +// David Eberly's algorithm for finding a bridge between hole and outer polygon +template +typename Earcut::Node* +Earcut::findHoleBridge(Node* hole, Node* outerNode) { + Node* p = outerNode; + double hx = hole->x; + double hy = hole->y; + double qx = -std::numeric_limits::infinity(); + Node* m = nullptr; + + // find a segment intersected by a ray from the hole's leftmost Vertex to the left; + // segment's endpoint with lesser x will be potential connection Vertex + do { + if (hy <= p->y && hy >= p->next->y && p->next->y != p->y) { + double x = p->x + (hy - p->y) * (p->next->x - p->x) / (p->next->y - p->y); + if (x <= hx && x > qx) { + qx = x; + m = p->x < p->next->x ? p : p->next; + if (x == hx) return m; // hole touches outer segment; pick leftmost endpoint + } + } + p = p->next; + } while (p != outerNode); + + if (!m) return 0; + + // look for points inside the triangle of hole Vertex, segment intersection and endpoint; + // if there are no points found, we have a valid connection; + // otherwise choose the Vertex of the minimum angle with the ray as connection Vertex + + const Node* stop = m; + double tanMin = std::numeric_limits::infinity(); + double tanCur = 0; + + p = m; + double mx = m->x; + double my = m->y; + + do { + if (hx >= p->x && p->x >= mx && hx != p->x && + pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p->x, p->y)) { + + tanCur = std::abs(hy - p->y) / (hx - p->x); // tangential + + if (locallyInside(p, hole) && + (tanCur < tanMin || (tanCur == tanMin && (p->x > m->x || sectorContainsSector(m, p))))) { + m = p; + tanMin = tanCur; + } + } + + p = p->next; + } while (p != stop); + + return m; +} + +// whether sector in vertex m contains sector in vertex p in the same coordinates +template +bool Earcut::sectorContainsSector(const Node* m, const Node* p) { + return area(m->prev, m, p->prev) < 0 && area(p->next, m, m->next) < 0; +} + +// interlink polygon nodes in z-order +template +void Earcut::indexCurve(Node* start) { + assert(start); + Node* p = start; + + do { + p->z = p->z ? p->z : zOrder(p->x, p->y); + p->prevZ = p->prev; + p->nextZ = p->next; + p = p->next; + } while (p != start); + + p->prevZ->nextZ = nullptr; + p->prevZ = nullptr; + + sortLinked(p); +} + +// Simon Tatham's linked list merge sort algorithm +// http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html +template +typename Earcut::Node* +Earcut::sortLinked(Node* list) { + assert(list); + Node* p; + Node* q; + Node* e; + Node* tail; + int i, numMerges, pSize, qSize; + int inSize = 1; + + for (;;) { + p = list; + list = nullptr; + tail = nullptr; + numMerges = 0; + + while (p) { + numMerges++; + q = p; + pSize = 0; + for (i = 0; i < inSize; i++) { + pSize++; + q = q->nextZ; + if (!q) break; + } + + qSize = inSize; + + while (pSize > 0 || (qSize > 0 && q)) { + + if (pSize == 0) { + e = q; + q = q->nextZ; + qSize--; + } else if (qSize == 0 || !q) { + e = p; + p = p->nextZ; + pSize--; + } else if (p->z <= q->z) { + e = p; + p = p->nextZ; + pSize--; + } else { + e = q; + q = q->nextZ; + qSize--; + } + + if (tail) tail->nextZ = e; + else list = e; + + e->prevZ = tail; + tail = e; + } + + p = q; + } + + tail->nextZ = nullptr; + + if (numMerges <= 1) return list; + + inSize *= 2; + } +} + +// z-order of a Vertex given coords and size of the data bounding box +template +int32_t Earcut::zOrder(const double x_, const double y_) { + // coords are transformed into non-negative 15-bit integer range + int32_t x = static_cast((x_ - minX) * inv_size); + int32_t y = static_cast((y_ - minY) * inv_size); + + x = (x | (x << 8)) & 0x00FF00FF; + x = (x | (x << 4)) & 0x0F0F0F0F; + x = (x | (x << 2)) & 0x33333333; + x = (x | (x << 1)) & 0x55555555; + + y = (y | (y << 8)) & 0x00FF00FF; + y = (y | (y << 4)) & 0x0F0F0F0F; + y = (y | (y << 2)) & 0x33333333; + y = (y | (y << 1)) & 0x55555555; + + return x | (y << 1); +} + +// find the leftmost node of a polygon ring +template +typename Earcut::Node* +Earcut::getLeftmost(Node* start) { + Node* p = start; + Node* leftmost = start; + do { + if (p->x < leftmost->x || (p->x == leftmost->x && p->y < leftmost->y)) + leftmost = p; + p = p->next; + } while (p != start); + + return leftmost; +} + +// check if a point lies within a convex triangle +template +bool Earcut::pointInTriangle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const { + return (cx - px) * (ay - py) >= (ax - px) * (cy - py) && + (ax - px) * (by - py) >= (bx - px) * (ay - py) && + (bx - px) * (cy - py) >= (cx - px) * (by - py); +} + +// check if a diagonal between two polygon nodes is valid (lies in polygon interior) +template +bool Earcut::isValidDiagonal(Node* a, Node* b) { + return a->next->i != b->i && a->prev->i != b->i && !intersectsPolygon(a, b) && // dones't intersect other edges + ((locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible + (area(a->prev, a, b->prev) != 0.0 || area(a, b->prev, b) != 0.0)) || // does not create opposite-facing sectors + (equals(a, b) && area(a->prev, a, a->next) > 0 && area(b->prev, b, b->next) > 0)); // special zero-length case +} + +// signed area of a triangle +template +double Earcut::area(const Node* p, const Node* q, const Node* r) const { + return (q->y - p->y) * (r->x - q->x) - (q->x - p->x) * (r->y - q->y); +} + +// check if two points are equal +template +bool Earcut::equals(const Node* p1, const Node* p2) { + return p1->x == p2->x && p1->y == p2->y; +} + +// check if two segments intersect +template +bool Earcut::intersects(const Node* p1, const Node* q1, const Node* p2, const Node* q2) { + int o1 = sign(area(p1, q1, p2)); + int o2 = sign(area(p1, q1, q2)); + int o3 = sign(area(p2, q2, p1)); + int o4 = sign(area(p2, q2, q1)); + + if (o1 != o2 && o3 != o4) return true; // general case + + if (o1 == 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1 + if (o2 == 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1 + if (o3 == 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2 + if (o4 == 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2 + + return false; +} + +// for collinear points p, q, r, check if point q lies on segment pr +template +bool Earcut::onSegment(const Node* p, const Node* q, const Node* r) { + return q->x <= std::max(p->x, r->x) && + q->x >= std::min(p->x, r->x) && + q->y <= std::max(p->y, r->y) && + q->y >= std::min(p->y, r->y); +} + +template +int Earcut::sign(double val) { + return (0.0 < val) - (val < 0.0); +} + +// check if a polygon diagonal intersects any polygon segments +template +bool Earcut::intersectsPolygon(const Node* a, const Node* b) { + const Node* p = a; + do { + if (p->i != a->i && p->next->i != a->i && p->i != b->i && p->next->i != b->i && + intersects(p, p->next, a, b)) return true; + p = p->next; + } while (p != a); + + return false; +} + +// check if a polygon diagonal is locally inside the polygon +template +bool Earcut::locallyInside(const Node* a, const Node* b) { + return area(a->prev, a, a->next) < 0 ? + area(a, b, a->next) >= 0 && area(a, a->prev, b) >= 0 : + area(a, b, a->prev) < 0 || area(a, a->next, b) < 0; +} + +// check if the middle Vertex of a polygon diagonal is inside the polygon +template +bool Earcut::middleInside(const Node* a, const Node* b) { + const Node* p = a; + bool inside = false; + double px = (a->x + b->x) / 2; + double py = (a->y + b->y) / 2; + do { + if (((p->y > py) != (p->next->y > py)) && p->next->y != p->y && + (px < (p->next->x - p->x) * (py - p->y) / (p->next->y - p->y) + p->x)) + inside = !inside; + p = p->next; + } while (p != a); + + return inside; +} + +// link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits +// polygon into two; if one belongs to the outer ring and another to a hole, it merges it into a +// single ring +template +typename Earcut::Node* +Earcut::splitPolygon(Node* a, Node* b) { + Node* a2 = nodes.construct(a->i, a->x, a->y); + Node* b2 = nodes.construct(b->i, b->x, b->y); + Node* an = a->next; + Node* bp = b->prev; + + a->next = b; + b->prev = a; + + a2->next = an; + an->prev = a2; + + b2->next = a2; + a2->prev = b2; + + bp->next = b2; + b2->prev = bp; + + return b2; +} + +// create a node and util::optionally link it with previous one (in a circular doubly linked list) +template template +typename Earcut::Node* +Earcut::insertNode(std::size_t i, const Point& pt, Node* last) { + Node* p = nodes.construct(static_cast(i), util::nth<0, Point>::get(pt), util::nth<1, Point>::get(pt)); + + if (!last) { + p->prev = p; + p->next = p; + + } else { + assert(last); + p->next = last->next; + p->prev = last; + last->next->prev = p; + last->next = p; + } + return p; +} + +template +void Earcut::removeNode(Node* p) { + p->next->prev = p->prev; + p->prev->next = p->next; + + if (p->prevZ) p->prevZ->nextZ = p->nextZ; + if (p->nextZ) p->nextZ->prevZ = p->prevZ; +} +} + +template +std::vector earcut(const Polygon& poly) { + mapbox::detail::Earcut earcut; + earcut(poly); + return std::move(earcut.indices); +} +} diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 0e954f93b1..81d8d616da 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -373,6 +373,11 @@ set(SLIC3R_GUI_SOURCES GUI/SavePresetDialog.hpp GUI/SceneRaycaster.cpp GUI/SceneRaycaster.hpp + GUI/PartSkipCommon.hpp + GUI/PartSkipDialog.cpp + GUI/PartSkipDialog.hpp + GUI/SkipPartCanvas.cpp + GUI/SkipPartCanvas.hpp GUI/Search.cpp GUI/Search.hpp GUI/Selection.cpp diff --git a/src/slic3r/GUI/DeviceManager.cpp b/src/slic3r/GUI/DeviceManager.cpp index 8e2bb16905..ada83175d0 100644 --- a/src/slic3r/GUI/DeviceManager.cpp +++ b/src/slic3r/GUI/DeviceManager.cpp @@ -1860,6 +1860,17 @@ int MachineObject::command_control_fan_val(FanType fan_type, int val) } +int MachineObject::command_task_partskip(std::vector part_ids) +{ + BOOST_LOG_TRIVIAL(trace) << "command_task_partskip: "; + json j; + j["print"]["command"] = "skip_objects"; + j["print"]["obj_list"] = part_ids; + j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); + + return this->publish_json(j.dump(), 1); +} + int MachineObject::command_task_abort() { BOOST_LOG_TRIVIAL(trace) << "command_task_abort: "; @@ -2730,7 +2741,7 @@ void MachineObject::reset() vt_tray.reset(); subtask_ = nullptr; - + m_partskip_ids.clear(); } void MachineObject::set_print_state(std::string status) @@ -2966,8 +2977,20 @@ int MachineObject::parse_json(std::string payload, bool key_field_only) print_json.load_compatible_settings(printer_type, ""); print_json.diff2all_base_reset(j_pre); } + + if (j_pre["print"].contains("s_obj")){ + if(j_pre["print"]["s_obj"].is_array()){ + m_partskip_ids.clear(); + for(auto it=j_pre["print"]["s_obj"].begin(); it!=j_pre["print"]["s_obj"].end(); it++){ + m_partskip_ids.push_back(it.value().get()); + } + } + } } } + if (j_pre["print"].contains("plate_idx") && m_plate_index == -1){ + m_plate_index = j_pre["print"]["plate_idx"].get(); + } } if (j_pre.contains("system")) { if (j_pre["system"].contains("command")) { diff --git a/src/slic3r/GUI/DeviceManager.hpp b/src/slic3r/GUI/DeviceManager.hpp index e3c90b884b..7f66c30f25 100644 --- a/src/slic3r/GUI/DeviceManager.hpp +++ b/src/slic3r/GUI/DeviceManager.hpp @@ -833,6 +833,9 @@ public: int xcam_filament_tangle_detect_count = 0; int ams_print_option_count = 0; + // part skip + std::vector m_partskip_ids; + //supported features bool is_support_chamber_edit{false}; bool is_support_extrusion_cali{false}; @@ -955,6 +958,7 @@ public: int command_control_fan_val(FanType fan_type, int val); int command_task_abort(); /* cancelled the job_id */ + int command_task_partskip(std::vector part_ids); int command_task_cancel(std::string job_id); int command_task_pause(); int command_task_resume(); diff --git a/src/slic3r/GUI/PartSkipCommon.hpp b/src/slic3r/GUI/PartSkipCommon.hpp new file mode 100644 index 0000000000..bd158edbf8 --- /dev/null +++ b/src/slic3r/GUI/PartSkipCommon.hpp @@ -0,0 +1,18 @@ +#ifndef PARTSKIPCOMMON_H +#define PARTSKIPCOMMON_H + + +namespace Slic3r { namespace GUI { + +enum PartState { + psUnCheck, + psChecked, + psSkipped +}; + + +typedef std::vector> PartsInfo; + +}} + +#endif // PARTSKIPCOMMON_H \ No newline at end of file diff --git a/src/slic3r/GUI/PartSkipDialog.cpp b/src/slic3r/GUI/PartSkipDialog.cpp new file mode 100644 index 0000000000..2f72abdb0f --- /dev/null +++ b/src/slic3r/GUI/PartSkipDialog.cpp @@ -0,0 +1,863 @@ +#include "GUI_Utils.hpp" +#include "GUI_App.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Widgets/CheckBox.hpp" +#include "Widgets/Label.hpp" + +#include "MsgDialog.hpp" +#include "Printer/PrinterFileSystem.h" +#include "PartSkipDialog.hpp" +#include "SkipPartCanvas.hpp" +#include "MediaPlayCtrl.h" + + +namespace Slic3r { namespace GUI { + +extern wxString hide_passwd(wxString url, std::vector const &passwords); +extern void refresh_agora_url(char const *device, char const *dev_ver, char const *channel, void *context, void (*callback)(void *context, char const *url)); + + +PartSkipDialog::PartSkipDialog(wxWindow* parent): DPIDialog(parent, wxID_ANY, _L("Skip Objects"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX) +{ + std::time_t t = std::time(0); + std::stringstream buf; + buf << put_time(std::localtime(&t), "%a_%b_%d_%H_%M_%S/"); + m_timestamp = buf.str(); + + std::string icon_path = (boost::format("%1%/images/BambuStudioTitle.ico") % Slic3r::resources_dir()).str(); + SetIcon(wxIcon(Slic3r::encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO)); + SetBackgroundColour(*wxWHITE); + SetSizeHints( wxDefaultSize, wxDefaultSize ); + + m_sizer = new wxBoxSizer( wxVERTICAL ); + auto m_line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL); + m_line_top->SetBackgroundColour(wxColour(0xA6, 0xa9, 0xAA)); + m_sizer->Add(m_line_top, 0, wxEXPAND, FromDIP(5)); + m_sizer->Add(0, 0, 0, wxTOP, FromDIP(5)); + + + m_simplebook = new wxSimplebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0); + m_book_first_panel = new wxPanel( m_simplebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_book_third_panel = new wxPanel(m_simplebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + m_book_third_panel->SetBackgroundColour(*wxWHITE); + + m_dlg_sizer = new wxBoxSizer( wxVERTICAL ); + m_dlg_content_sizer = new wxBoxSizer( wxHORIZONTAL ); + m_canvas_sizer = new wxBoxSizer( wxVERTICAL ); + + // page 3 + wxGLAttributes canvasAttrs; + canvasAttrs.PlatformDefaults().Defaults().Stencil(8).EndList(); + m_canvas = new SkipPartCanvas(m_book_third_panel, canvasAttrs); + m_canvas->SetPosition(wxPoint(FromDIP(10), FromDIP(10))); + m_canvas->SetMinSize( wxSize( FromDIP(400),FromDIP(400) ) ); + + m_canvas_sizer->Add( m_canvas, 0, wxALL, FromDIP(10) ); + + m_canvas_btn_sizer = new wxBoxSizer( wxHORIZONTAL ); + m_canvas_btn_sizer->SetMinSize( wxSize( FromDIP(214),FromDIP(28) ) ); + + StateColor zoom_bg(std::pair(wxColour(255, 255, 255), StateColor::Disabled), std::pair(wxColour(206, 206, 206), StateColor::Pressed), + std::pair(wxColour(238, 238, 238), StateColor::Hovered), std::pair(wxColour(255, 255, 255), StateColor::Enabled), + std::pair(wxColour(255, 255, 255), StateColor::Normal)); + zoom_bg.setTakeFocusedAsHovered(false); + StateColor zoom_bd(std::pair(wxColour(144, 144, 144), StateColor::Disabled), std::pair(wxColour(38, 46, 48), StateColor::Enabled)); + StateColor zoom_text(std::pair(wxColour(144, 144, 144), StateColor::Disabled), std::pair(wxColour(38, 46, 48), StateColor::Enabled)); + + m_zoom_out_btn = new Button(m_book_third_panel, _L("")); + m_zoom_out_btn->SetIcon("canvas_zoom_out"); + m_zoom_out_btn->SetToolTip(_L("Zoom Out")); + m_zoom_out_btn->SetBackgroundColor(zoom_bg); + m_zoom_out_btn->SetBorderColor(wxColour(238, 238, 238)); + m_zoom_out_btn->SetCornerRadius(0); + m_zoom_out_btn->SetMinSize(wxSize(FromDIP(54), FromDIP(26))); + + m_canvas_btn_sizer->Add(m_zoom_out_btn, 0, wxEXPAND, 0); + + StateColor percent_bg(std::pair(wxColour(255, 255, 255), StateColor::Disabled), std::pair(wxColour(255, 255, 255), StateColor::Pressed), + std::pair(wxColour(255, 255, 255), StateColor::Hovered), std::pair(wxColour(255, 255, 255), StateColor::Enabled), + std::pair(wxColour(255, 255, 255), StateColor::Normal)); + m_percent_label = new Button(m_book_third_panel, _L("100 %")); + m_percent_label->SetBackgroundColor(percent_bg); + m_percent_label->SetBorderColor(wxColour(238, 238, 238)); + m_percent_label->SetMinSize(wxSize(FromDIP(54), FromDIP(26))); + m_percent_label->SetCornerRadius(0); + m_canvas_btn_sizer->Add( m_percent_label, 0, wxEXPAND, 0 ); + + m_zoom_in_btn = new Button(m_book_third_panel, _L("")); + m_zoom_in_btn->SetIcon("canvas_zoom_in"); + m_zoom_in_btn->SetToolTip(_L("Zoom In")); + m_zoom_in_btn->SetBackgroundColor(zoom_bg); + m_zoom_in_btn->SetBorderColor(wxColour(238, 238, 238)); + m_zoom_in_btn->SetMinSize(wxSize(FromDIP(54), FromDIP(26))); + m_zoom_in_btn->SetCornerRadius(0); + m_canvas_btn_sizer->Add(m_zoom_in_btn, 0, wxEXPAND, 0); + + m_switch_drag_btn = new Button(m_book_third_panel, _L("")); + m_switch_drag_btn->SetIcon("canvas_drag"); + m_switch_drag_btn->SetToolTip(_L("Drag")); + m_switch_drag_btn->SetBackgroundColor(*wxWHITE); + m_switch_drag_btn->SetBorderColor(wxColour(238, 238, 238)); + m_switch_drag_btn->SetMinSize(wxSize(FromDIP(54),FromDIP(26))); + m_switch_drag_btn->SetCornerRadius(0); + m_canvas_btn_sizer->Add( m_switch_drag_btn, 0, wxEXPAND, FromDIP(5) ); + + m_canvas_sizer->Add(m_canvas_btn_sizer, 0, wxALIGN_CENTER_HORIZONTAL, FromDIP(10)); + + m_dlg_content_sizer->Add( m_canvas_sizer, 0, wxALL, FromDIP(10) ); + + m_list_sizer = new wxBoxSizer( wxVERTICAL ); + m_list_sizer->SetMinSize( wxSize( FromDIP(267),FromDIP(422) ) ); + + auto all_checkbox_sizer = new wxBoxSizer(wxHORIZONTAL); + m_all_checkbox = new CheckBox(m_book_third_panel, wxID_ANY); + m_all_checkbox->SetValue(false); + m_all_checkbox->SetBackgroundColour(*wxWHITE); + m_all_label = new Label(m_book_third_panel, _L("Select All")); + m_all_label->Wrap(-1); + m_all_label->SetBackgroundColour(*wxWHITE); + + m_all_label->SetMinSize(wxSize(267, -1)); + m_all_label->SetMaxSize(wxSize(267, -1)); + all_checkbox_sizer->Add(m_all_checkbox, 0, wxALL | wxALIGN_CENTER_VERTICAL, 0); + all_checkbox_sizer->Add(m_all_label, 0, wxALL | wxALIGN_CENTER_VERTICAL, 0); + + m_list_sizer->Add(all_checkbox_sizer, 0, wxALL, FromDIP(5)); + + m_line = new wxPanel(m_book_third_panel, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(267), FromDIP(1)), wxTAB_TRAVERSAL); + m_line->SetBackgroundColour( wxColor(238, 238, 238) ); + + m_list_sizer->Add(m_line, 0, wxEXPAND, 0); + + m_list_view = new wxScrolledWindow( m_book_third_panel, wxID_ANY, wxDefaultPosition, wxSize( 267,-1 ), wxHSCROLL|wxVSCROLL ); + m_list_view->SetScrollRate( 5, 5 ); + m_list_view->SetMinSize( wxSize( FromDIP(267),FromDIP(410) ) ); + m_list_view->SetMaxSize(wxSize(FromDIP(267), FromDIP(410))); + m_list_view->SetBackgroundColour(*wxWHITE); + + m_scroll_sizer = new wxBoxSizer(wxVERTICAL); + m_list_view->SetSizer(m_scroll_sizer); + m_list_view->Layout(); + + m_list_sizer->Add(m_list_view, 0, wxEXPAND, 0); + + m_dlg_content_sizer->Add( m_list_sizer, 0, wxALL, FromDIP(15) ); + + + m_dlg_sizer->Add( m_dlg_content_sizer, 0, wxEXPAND, FromDIP(5) ); + + m_dlg_btn_sizer = new wxBoxSizer( wxHORIZONTAL ); + m_dlg_btn_sizer->SetMinSize(wxSize(FromDIP(720), FromDIP(54))); + + m_cnt_label = new Label(m_book_third_panel, _L("cnt 1")); + m_cnt_label->Wrap( -1 ); + m_cnt_label->SetBackgroundColour(*wxWHITE); + m_cnt_label->SetForegroundColour(wxColour(0, 174, 66)); + + m_cnt_label->SetFont(Label::Head_15); + m_dlg_btn_sizer->Add(0, 0, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, FromDIP(5)); + m_dlg_btn_sizer->Add( m_cnt_label, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(0) ); + + m_tot_label = new Label(m_book_third_panel, _L("tot 15")); + m_tot_label->Wrap( -1 ); + m_tot_label->SetBackgroundColour(*wxWHITE); + + m_dlg_btn_sizer->Add(m_tot_label, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5)); + m_dlg_btn_sizer->Add(0, 0, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL, FromDIP(5)); + + StateColor btn_bg_white(std::pair(wxColour(206, 206, 206), StateColor::Pressed), std::pair(wxColour(238, 238, 238), StateColor::Hovered), + std::pair(*wxWHITE, StateColor::Normal)); + + + StateColor btn_bg_green(std::pair(wxColour(27, 136, 68), StateColor::Pressed), std::pair(wxColour(61, 203, 115), StateColor::Hovered), + std::pair(wxColour(0, 177, 66), StateColor::Normal)); + + m_apply_btn = new Button(m_book_third_panel, wxString(_L("Skip"))); + m_apply_btn->SetBackgroundColor(btn_bg_green); + m_apply_btn->SetTextColor(*wxWHITE); + // m_apply_btn->SetBorderColor(wxColour(38, 46, 48)); + m_apply_btn->SetFont(Label::Body_14); + m_apply_btn->SetSize(wxSize(-1, FromDIP(32))); + m_apply_btn->SetMinSize(wxSize(FromDIP(80), FromDIP(32))); + m_apply_btn->SetCornerRadius(FromDIP(16)); + m_dlg_btn_sizer->Add(m_apply_btn, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(15)); + + m_dlg_sizer->Add( m_dlg_btn_sizer, 0, wxEXPAND, FromDIP(5) ); + + m_book_third_panel->SetSizer( m_dlg_sizer ); + m_book_third_panel->Layout(); + m_dlg_sizer->Fit( m_book_third_panel ); + + // page 2 + m_book_second_panel = new wxPanel( m_simplebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_book_second_panel->SetBackgroundColour(*wxWHITE); + m_book_second_sizer = new wxBoxSizer( wxVERTICAL ); + m_book_second_btn_sizer = new wxBoxSizer( wxHORIZONTAL ); + + m_retry_label = new Label( m_book_second_panel, _L("Load Skipping Objects Information Failed. \nPlease try again.")); + m_retry_label->Wrap( -1 ); + m_retry_label->SetBackgroundColour(*wxWHITE); + m_book_second_sizer->Add(0, 0, 1, wxEXPAND, 0); + m_book_second_sizer->Add(m_retry_label, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_CENTER_HORIZONTAL, 0); + m_book_second_sizer->Add(0, 0, 1, wxEXPAND, 0); + + m_second_retry_btn = new Button(m_book_second_panel, _L("Retry")); + m_second_retry_btn->SetBackgroundColor(btn_bg_green); + // m_second_retry_btn->SetBorderColor(wxColour(38, 46, 48)); + m_second_retry_btn->SetTextColor(*wxWHITE); + m_second_retry_btn->SetFont(Label::Body_14); + m_second_retry_btn->SetSize(wxSize(FromDIP(80), FromDIP(32))); + m_second_retry_btn->SetMinSize(wxSize(FromDIP(80), FromDIP(32))); + m_second_retry_btn->SetCornerRadius(FromDIP(16)); + m_second_retry_btn->Bind(wxEVT_BUTTON, &PartSkipDialog::OnRetryButton, this); + m_book_second_btn_sizer->Add(m_second_retry_btn, 0, wxALL, FromDIP(24)); + + m_book_second_sizer->Add(m_book_second_btn_sizer, 0, wxALIGN_RIGHT, 0); + m_book_second_panel->SetSizer( m_book_second_sizer ); + m_book_second_panel->Layout(); + m_book_second_sizer->Fit( m_book_second_panel ); + + // page 1 + m_book_first_panel = new wxPanel( m_simplebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_book_first_sizer = new wxBoxSizer( wxVERTICAL ); + m_book_first_sizer->SetMinSize(wxSize(FromDIP(720), FromDIP(500))); + + // auto m_loading_sizer = new wxBoxSizer(wxHORIZONTAL); + // std::vector list{"ams_rfid_1", "ams_rfid_2", "ams_rfid_3", "ams_rfid_4"}; + // m_loading_icon = new AnimaIcon(m_book_first_panel, wxID_ANY, list, "refresh_printer", 100); + + m_loading_label = new Label( m_book_first_panel, wxString(_L("Skipping Objects Information Loading ..."))); + m_loading_label->Wrap( -1 ); + m_loading_label->SetBackgroundColour(*wxWHITE); + + // m_loading_sizer->Add(0, 0, 1, wxEXPAND, 0); + // m_loading_sizer->Add(m_loading_icon, 0, wxALL, FromDIP(5)); + // m_loading_sizer->Add(m_loading_label, 0, wxALL, FromDIP(5)); + // m_loading_sizer->Add(0, 0, 1, wxEXPAND, 0); + + m_book_first_sizer->Add(0, 0, 1, wxEXPAND, 0); + m_book_first_sizer->Add( m_loading_label, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 0); + m_book_first_sizer->Add(0, 0, 1, wxEXPAND, 0); + + m_book_first_panel->SetSizer( m_book_first_sizer ); + m_book_first_panel->Layout(); + m_book_first_sizer->Fit( m_book_first_panel ); + + m_simplebook->AddPage( m_book_first_panel, _("loading page"), false ); + m_simplebook->AddPage( m_book_second_panel, _("retry page"), false ); + m_simplebook->AddPage( m_book_third_panel, _("dialog page"), false ); + m_sizer->Add( m_simplebook, 1, wxEXPAND | wxALL, 5 ); + + + SetSizer( m_sizer ); + m_zoom_in_btn->Bind(wxEVT_BUTTON, &PartSkipDialog::OnZoomIn, this); + m_zoom_out_btn->Bind(wxEVT_BUTTON, &PartSkipDialog::OnZoomOut, this); + m_switch_drag_btn->Bind(wxEVT_BUTTON, &PartSkipDialog::OnSwitchDrag, this); + m_canvas->Bind(EVT_ZOOM_PERCENT, &PartSkipDialog::OnZoomPercent, this); + m_canvas->Bind(EVT_CANVAS_PART, &PartSkipDialog::UpdatePartsStateFromCanvas, this); + + m_apply_btn->Bind(wxEVT_BUTTON, &PartSkipDialog::OnApplyDialog, this); + m_all_checkbox->Bind(wxEVT_TOGGLEBUTTON, &PartSkipDialog::OnAllCheckbox, this); + + Fit(); + Layout(); + CentreOnParent(); +} + +PartSkipDialog::~PartSkipDialog() {} + +void PartSkipDialog::on_dpi_changed(const wxRect& suggested_rect) { Fit(); } + + +std::string PartSkipDialog::create_tmp_path(){ + boost::filesystem::path parent_path(temporary_dir()); + + std::stringstream buf; + buf << "/bamboo_task/"; + buf << m_timestamp; + if (m_obj) { + buf << m_obj->dev_id << "_"; + buf << m_obj->job_id_ << "/"; + } else { + buf << 1 << "_" << 1 << "/"; + } + std::string tmp_path = (parent_path / buf.str() ).string(); + + if (!std::filesystem::exists(tmp_path + "Metadata/") && !fs::create_directories(tmp_path + "Metadata/")) { + wxMessageBox("create file failed."); + } + return tmp_path; +} + + +bool PartSkipDialog::is_local_file_existed(const std::vector &local_paths) +{ + for (auto path : local_paths) { + if (!std::filesystem::exists(path)) { + return false; + } + } + return true; +} + +void PartSkipDialog::DownloadPartsFile() +{ + m_tmp_path = create_tmp_path(); //wxGetApp().app_config->get("download_path"); + + m_local_paths.clear(); + m_target_paths.clear(); + + int plate_idx = m_obj ? m_obj->m_plate_index : 1; + + m_local_paths.push_back(m_tmp_path + "Metadata/pick_" + std::to_string(plate_idx) + ".png"); + m_local_paths.push_back(m_tmp_path + "Metadata/model_settings.config"); + m_local_paths.push_back(m_tmp_path + "Metadata/slice_info.config"); + + m_target_paths.push_back("Metadata/pick_" + std::to_string(plate_idx) + ".png"); + m_target_paths.push_back("Metadata/model_settings.config"); + m_target_paths.push_back("Metadata/slice_info.config"); + + if (!is_local_file_existed(m_local_paths)) { + m_file_sys = boost::make_shared(); + m_file_sys->Attached(); + m_file_sys->Bind(EVT_STATUS_CHANGED, &PartSkipDialog::OnFileSystemEvent, this); + m_file_sys->Bind(EVT_RAMDOWNLOAD, &PartSkipDialog::OnFileSystemResult, this); + m_file_sys->Start(); + } else { + m_file_sys->SendExistedFile(); + } +} +void PartSkipDialog::fetchUrl(boost::weak_ptr wfs) +{ + boost::shared_ptr fs(wfs.lock()); + if (!fs) return; + + DeviceManager *dm = GUI::wxGetApp().getDeviceManager(); + MachineObject *obj = dm->get_selected_machine(); + + if (obj == nullptr) { + fs->SetUrl("0"); + return; + } + std::string dev_ver = obj->get_ota_version(); + std::string dev_id = obj->dev_id; + int remote_proto = obj->get_file_remote(); + + NetworkAgent *agent = wxGetApp().getAgent(); + std::string agent_version = agent ? agent->get_version() : ""; + + if (agent) { + switch (m_url_state) { + case URL_TCP: { + std::string devIP = obj->dev_ip; + std::string accessCode = obj->get_access_code(); + std::string tcp_url = "bambu:///local/" + devIP + "?port=6000&user=" + "bblp" + "&passwd=" + accessCode; + CallAfter([=] { + boost::shared_ptr fs(wfs.lock()); + if (!fs) return; + if (boost::algorithm::starts_with(tcp_url, "bambu:///")) { + fs->SetUrl(tcp_url); + } else { + fs->SetUrl("3"); + } + }); + break; + } + case URL_TUTK: { + std::string protocols[] = {"", "\"tutk\"", "\"agora\"", "\"tutk\",\"agora\""}; + agent->get_camera_url(obj->dev_id + "|" + dev_ver + "|" + protocols[1], [this, wfs, m = dev_id, v = agent->get_version(), dv = dev_ver](std::string url) { + if (boost::algorithm::starts_with(url, "bambu:///")) { + url += "&device=" + m; + url += "&net_ver=" + v; + url += "&dev_ver=" + dv; + url += "&refresh_url=" + boost::lexical_cast(&refresh_agora_url); + url += "&cli_id=" + wxGetApp().app_config->get("slicer_uuid"); + url += "&cli_ver=" + std::string(SLIC3R_VERSION); + } + BOOST_LOG_TRIVIAL(info) << "SendToPrinter::fetchUrl: camera_url: " << hide_passwd(url, {"?uid=", "authkey=", "passwd="}); + std::cout << "SendToPrinter::fetchUrl: camera_url: " << hide_passwd(url, {"?uid=", "authkey=", "passwd="}); + CallAfter([=] { + boost::shared_ptr fs(wfs.lock()); + if (!fs) return; + if (boost::algorithm::starts_with(url, "bambu:///")) { + fs->SetUrl(url); + } else { + fs->SetUrl("3"); + } + }); + }); + break; + } + default: break; + } + } +} + +void PartSkipDialog::OnFileSystemEvent(wxCommandEvent &e) +{ + e.Skip(); + auto wfs = boost::weak_ptr(m_file_sys); + boost::shared_ptr fs(wfs.lock()); + if (!fs) return; + + wxString msg; + int status = e.GetInt(); + int extra = e.GetExtraLong(); + + switch (status) { + case PrinterFileSystem::Initializing: + case PrinterFileSystem::Connecting: break; + case PrinterFileSystem::ListSyncing: { + m_file_sys->GetPickImages(m_local_paths, m_target_paths); + break; + } + case PrinterFileSystem::Failed: { + m_file_sys->SendConnectFail(); + break; + } + case PrinterFileSystem::Reconnecting: break; + } + if (e.GetInt() == PrinterFileSystem::Initializing) { + CallAfter([=] { + boost::shared_ptr fs(wfs.lock()); + if (!fs) return; + fetchUrl(boost::weak_ptr(fs)); + }); + } +} +void PartSkipDialog::OnFileSystemResult(wxCommandEvent &event){ + int result = event.GetInt(); + if (result == 0) { + InitDialogUI(); + SetSimplebookPage(2); + }else{ + switch (m_url_state) { + case URL_TCP: { + m_url_state = URL_TUTK; + break; + } + case URL_TUTK: { + m_url_state = URL_TCP; + break; + } + } + SetSimplebookPage(1); + } + // wxMilliSleep(1000); + // m_loading_icon->Stop(); + Refresh(); +} + +void PartSkipDialog::InitSchedule(MachineObject *obj){ + m_obj = obj; + SetSimplebookPage(0); + // m_loading_icon->Play(); + DownloadPartsFile(); +} + +void PartSkipDialog::OnRetryButton(wxCommandEvent &event) { + event.Skip(); + InitSchedule(m_obj); + Refresh(); +} + + +bool PartSkipDialog::is_drag_mode(){ + return m_is_drag == true; +} + +PartsInfo PartSkipDialog::GetPartsInfo(){ + PartsInfo parts_info; + for (auto [part_id, part_state] : this->m_parts_state) + { + parts_info.push_back(std::pair(part_id, part_state)); + } + return parts_info; +} + +void PartSkipDialog::OnZoomIn(wxCommandEvent &event){ + m_canvas->ZoomIn(20); + UpdateZoomPercent(); +} + +void PartSkipDialog::OnZoomOut(wxCommandEvent &event){ + m_canvas->ZoomOut(20); + UpdateZoomPercent(); +} + +void PartSkipDialog::OnSwitchDrag(wxCommandEvent& event){ + if (this->is_drag_mode()) { + m_is_drag = false; + m_switch_drag_btn->SetBackgroundColor(*wxWHITE); + m_switch_drag_btn->SetIcon("canvas_drag"); + } else { + m_is_drag = true; + m_switch_drag_btn->SetBackgroundColor(wxColour(0, 174, 66)); + m_switch_drag_btn->SetIcon("canvas_drag_active"); + } + m_canvas->SwitchDrag(m_is_drag); +} + +void PartSkipDialog::OnZoomPercent(wxCommandEvent &event) { + m_zoom_percent = event.GetInt(); + if (m_zoom_percent >= 1000) { + m_zoom_percent = 1000; + m_zoom_in_btn->Enable(false); + m_zoom_in_btn->SetIcon("canvas_zoom_in_disable"); + } else if (m_zoom_percent <= 100) { + m_zoom_percent = 100; + m_zoom_out_btn->Enable(false); + m_zoom_out_btn->SetIcon("canvas_zoom_out_disable"); + } else { + m_zoom_in_btn->Enable(true); + m_zoom_out_btn->Enable(true); + m_zoom_in_btn->SetIcon("canvas_zoom_in"); + m_zoom_out_btn->SetIcon("canvas_zoom_out"); + } + + UpdateZoomPercent(); +} + +void PartSkipDialog::UpdatePartsStateFromCanvas(wxCommandEvent &event) { + int part_id = event.GetExtraLong(); + PartState part_state = PartState(event.GetInt()); + + m_parts_state[part_id] = part_state; + if(part_state == psUnCheck){ + m_all_checkbox->SetValue(false); + } + if(IsAllChecked()){ + m_all_checkbox->SetValue(true); + } + UpdateDialogUI(); +} + +void PartSkipDialog::UpdateZoomPercent() { + m_percent_label->SetLabel(wxString::Format(_L("%d%%"), m_zoom_percent)); +} + +void PartSkipDialog::UpdateCountLabel() { + int check_cnt = 0; + int tot_cnt = 0; + for (auto [part_id, part_state] : m_parts_state) { + if (part_state == PartState::psChecked) check_cnt++; + if (part_state != PartState::psSkipped) tot_cnt++; + } + m_cnt_label->SetLabel(wxString::Format(_L("%d"), check_cnt)); + m_tot_label->SetLabel(wxString::Format(_L("/%d Selected"), tot_cnt)); +} + +bool PartSkipDialog::Show(bool show) +{ + if (show) { + wxGetApp().UpdateDlgDarkUI(this); + CentreOnParent(); + + Layout(); + Fit(); + } + return DPIDialog::Show(show); +} + +void PartSkipDialog::InitDialogUI() { + m_print_lock = true; + m_scroll_sizer->Clear(true); + m_all_checkbox->SetValue(false); + m_parts_state.clear(); + m_parts_name.clear(); + + string pick_img = m_local_paths[0]; + string model_settings = m_local_paths[1]; + + m_switch_drag_btn->SetIcon("canvas_drag"); + m_switch_drag_btn->SetBackgroundColor(*wxWHITE); + m_is_drag = false; + m_canvas->SwitchDrag(false); + m_canvas->SetZoomPercent(100); + m_canvas->SetOffset(wxPoint(0, 0)); + m_canvas->LoadPickImage(pick_img); + ModelSettingHelper helper(model_settings); + + if (helper.Parse()) { + auto parse_result = helper.GetResults(); + for (const auto& part : parse_result) { + m_parts_state[part.identify_id] = part.state; + m_parts_name[part.identify_id] = part.name; + } + if (m_obj) { + std::vector partskip_ids = m_obj->m_partskip_ids; + for (auto part_id : partskip_ids) { + m_parts_state[part_id] = PartState::psSkipped; + } + } + + for(const auto& [part_id, part_state] : m_parts_state) { + auto line_sizer = new wxBoxSizer(wxHORIZONTAL); + auto checkbox = new CheckBox(m_list_view); + auto label = new Label(m_list_view, _L("file 0")); + + checkbox->Bind(wxEVT_TOGGLEBUTTON, [this, part_id=part_id](wxCommandEvent& event) { + m_parts_state[part_id] = event.IsChecked() ? PartState::psChecked : PartState::psUnCheck; + if(!event.IsChecked()){ + m_all_checkbox->SetValue(false); + }else if(IsAllChecked()){ + m_all_checkbox->SetValue(true); + } + m_canvas->UpdatePartsInfo(GetPartsInfo()); + UpdateCountLabel(); + event.Skip(); + }, checkbox->GetId()); + + if (part_state == PartState::psChecked) { + checkbox->SetValue(true); + checkbox->Enable(true); + } else if(part_state == PartState::psUnCheck) { + checkbox->SetValue(false); + checkbox->Enable(true); + } else if (part_state == PartState::psSkipped) { + checkbox->SetValue(true); + checkbox->Enable(false); + } + label->SetLabel(wxString::FromUTF8(m_parts_name[part_id])); + label->SetBackgroundColour(*wxWHITE); + label->SetForegroundColour(wxColor(107,107,107)); + label->Wrap(-1); + checkbox->SetBackgroundColour(*wxWHITE); + + line_sizer->Add(checkbox, 0, wxALL | wxALIGN_CENTER_VERTICAL, 0); + line_sizer->Add(label, 1, wxALL | wxALIGN_CENTER_VERTICAL, 0); + m_scroll_sizer->Add(line_sizer, 0, wxALL, FromDIP(5)); + } + m_canvas->UpdatePartsInfo(GetPartsInfo()); + } + + m_scroll_sizer->Layout(); + UpdateCountLabel(); + Refresh(); + m_print_lock = false; +} + +void PartSkipDialog::UpdatePartsStateFromPrinter(MachineObject *obj) { + if (m_print_lock) return; + m_obj = obj; + if (m_obj) { + std::vector partskip_ids = m_obj->m_partskip_ids; + for(auto part_id : partskip_ids) { + m_parts_state[part_id] = PartState::psSkipped; + } + m_canvas->UpdatePartsInfo(GetPartsInfo()); + UpdateDialogUI(); + } +} + + +void PartSkipDialog::UpdateDialogUI(){ + if(m_parts_state.size() != m_scroll_sizer->GetItemCount()){ + BOOST_LOG_TRIVIAL(warning) << "m_parts_state and m_scroll_sizer mismatch."; + return; + } + + for (auto it = m_parts_state.begin(); it != m_parts_state.end(); ++it) { + int idx = std::distance(m_parts_state.begin(), it); + auto part_id = it->first; + auto part_state = it->second; + + wxSizerItem *item = m_scroll_sizer->GetItem(idx); + if (item && item->IsSizer()) { + wxSizer* sizer = item->GetSizer(); + auto check_item = sizer->GetItem((size_t)0); + + if (check_item && check_item->IsWindow()) + { + wxWindow *window = check_item->GetWindow(); + CheckBox *checkbox = dynamic_cast(window); + if (part_state == PartState::psChecked) { + checkbox->SetValue(true); + } else if (part_state == PartState::psUnCheck) { + checkbox->SetValue(false); + } else { + checkbox->SetValue(true); + checkbox->Enable(false); + } + } + } + } + + UpdateCountLabel(); + Refresh(); +} + +void PartSkipDialog::SetSimplebookPage(int page) { + m_simplebook->SetSelection(page); +} + +bool PartSkipDialog::IsAllChecked() { + for (auto& [part_id, part_state] : m_parts_state) { + if (part_state == PartState::psUnCheck) + return false; + } + return true; +} + +void PartSkipDialog::OnAllCheckbox(wxCommandEvent &event) +{ + if (m_all_checkbox->GetValue()) { + for (auto& [part_id, part_state] : m_parts_state) { + if (part_state == PartState::psUnCheck) + part_state = PartState::psChecked; + } + } + else{ + for (auto& [part_id, part_state] : m_parts_state) { + if (part_state == PartState::psChecked) + part_state = PartState::psUnCheck; + } + } + m_canvas->UpdatePartsInfo(GetPartsInfo()); + UpdateDialogUI(); + event.Skip(); +} + + +void PartSkipDialog::OnApplyDialog(wxCommandEvent &event) +{ + event.Skip(); + m_partskip_ids.clear(); + for (const auto& [part_id, part_state] : m_parts_state) { + if (part_state == PartState::psChecked) { + m_partskip_ids.push_back(part_id); + } + } + PartSkipConfirmDialog confirm_dialog(this); + confirm_dialog.SetMsgLabel(wxString::Format(_L("Skipping %d objects."), m_partskip_ids.size())); + + if(confirm_dialog.ShowModal() == wxID_OK){ + if (m_obj) { + BOOST_LOG_TRIVIAL(info) << "monitor: skipping "<< m_partskip_ids.size() <<" parts dev_id =" << m_obj->dev_id; + + bool all_skipped = true; + for (auto [part_id, part_state] : m_parts_state) { + if (part_state == PartState::psUnCheck) all_skipped = false; + } + + if (all_skipped) { + m_obj->command_task_abort(); + } else { + m_obj->command_task_partskip(m_partskip_ids); + } + EndModal(wxID_OK); + } else { + BOOST_LOG_TRIVIAL(warning) << "machine object is null."; + } + } +} + + +PartSkipConfirmDialog::PartSkipConfirmDialog(wxWindow *parent) : DPIDialog(parent, wxID_ANY, _L("Skip Objects"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX) +{ + std::string icon_path = (boost::format("%1%/images/BambuStudioTitle.ico") % Slic3r::resources_dir()).str(); + SetIcon(wxIcon(Slic3r::encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO)); + SetBackgroundColour(*wxWHITE); + SetSizeHints(wxDefaultSize, wxDefaultSize); + + wxBoxSizer* m_sizer; + m_sizer = new wxBoxSizer( wxVERTICAL ); + + auto m_line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL); + m_line_top->SetBackgroundColour(wxColour(0xA6, 0xa9, 0xAA)); + m_sizer->Add(m_line_top, 0, wxEXPAND, 0); + m_sizer->Add(0, 0, 0, wxALL, FromDIP(15)); + + m_msg_label = new Label( this, wxString(_L("Skipping objects."))); + m_msg_label->Wrap( -1 ); + m_msg_label->SetBackgroundColour(*wxWHITE); + + m_sizer->Add(m_msg_label, 0, wxLEFT, FromDIP(15)); + + auto m_tip_label = new Label(this, wxString(_("This action cannot be undone. Continue?"))); + m_tip_label->Wrap(-1); + m_tip_label->SetBackgroundColour(*wxWHITE); + m_tip_label->SetForegroundColour(wxColor(92,92,92)); + m_sizer->Add(m_tip_label, 0, wxLEFT | wxTOP, FromDIP(15)); + + wxBoxSizer* m_button_sizer; + m_button_sizer = new wxBoxSizer(wxHORIZONTAL); + m_button_sizer->SetMinSize(wxSize(FromDIP(480), FromDIP(54))); + m_button_sizer->Add(0, 0, 1, wxEXPAND, 0); + + + StateColor btn_bg_white(std::pair(wxColour(206, 206, 206), StateColor::Pressed), std::pair(wxColour(238, 238, 238), StateColor::Hovered), + std::pair(*wxWHITE, StateColor::Normal)); + + StateColor btn_bg_green(std::pair(wxColour(27, 136, 68), StateColor::Pressed), std::pair(wxColour(61, 203, 115), StateColor::Hovered), + std::pair(wxColour(0, 177, 66), StateColor::Normal)); + + m_apply_button = new Button(this, wxString(_L("Continue"))); + m_apply_button->SetBackgroundColor(btn_bg_green); + m_apply_button->SetTextColor(*wxWHITE); + // m_apply_button->SetBorderColor(wxColour(38, 46, 48)); + m_apply_button->SetFont(Label::Body_14); + m_apply_button->SetSize(wxSize(FromDIP(80), FromDIP(32))); + m_apply_button->SetMinSize(wxSize(FromDIP(80), FromDIP(32))); + m_apply_button->SetCornerRadius(FromDIP(16)); + m_apply_button->Bind(wxEVT_BUTTON, [this](auto& e){ + EndModal(wxID_OK); + e.Skip(); + }); + + m_button_sizer->Add(m_apply_button, 0, wxALL, FromDIP(15)); + m_sizer->Add(m_button_sizer, 1, wxEXPAND, FromDIP(5)); + m_sizer->Fit( this ); + + SetSizer( m_sizer ); + Layout(); + Fit(); +} + +PartSkipConfirmDialog::~PartSkipConfirmDialog() +{ +} + +bool PartSkipConfirmDialog::Show(bool show) +{ + if (show) { + wxGetApp().UpdateDlgDarkUI(this); + CentreOnParent(); + + Layout(); + Fit(); + } + return DPIDialog::Show(show); +} + +void PartSkipConfirmDialog::on_dpi_changed(const wxRect& suggested_rect) { Fit(); } + + +Button* PartSkipConfirmDialog::GetConfirmButton() +{ + return m_apply_button; +} + +void PartSkipConfirmDialog::SetMsgLabel(wxString msg){ + m_msg_label->SetLabel(msg); +} + + +}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/PartSkipDialog.hpp b/src/slic3r/GUI/PartSkipDialog.hpp new file mode 100644 index 0000000000..cd8fca3be7 --- /dev/null +++ b/src/slic3r/GUI/PartSkipDialog.hpp @@ -0,0 +1,153 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Widgets/Label.hpp" +#include "Widgets/CheckBox.hpp" +#include "Widgets/Button.hpp" +#include "Widgets/AnimaController.hpp" +#include "DeviceManager.hpp" +#include "PartSkipCommon.hpp" +#include "Printer/PrinterFileSystem.h" +#include "I18N.hpp" +#include "GUI_Utils.hpp" + + +namespace Slic3r { namespace GUI { + +class SkipPartCanvas; + +enum URL_STATE { + URL_TCP, + URL_TUTK, +}; + +class PartSkipConfirmDialog : public DPIDialog +{ +private: +protected: + Label *m_msg_label; + Button *m_apply_button; + +public: + PartSkipConfirmDialog(wxWindow *parent); + ~PartSkipConfirmDialog(); + + void on_dpi_changed(const wxRect &suggested_rect); + Button* GetConfirmButton(); + void SetMsgLabel(wxString msg); + bool Show(bool show); +}; + +class PartSkipDialog : public DPIDialog +{ +public: + PartSkipDialog(wxWindow *parent); + ~PartSkipDialog(); + void on_dpi_changed(const wxRect &suggested_rect); + bool Show(bool show); + + void UpdatePartsStateFromPrinter(MachineObject *obj_); + void SetSimplebookPage(int page); + void InitSchedule(MachineObject *obj_); + void InitDialogUI(); + + + MachineObject *m_obj{nullptr}; + + wxSimplebook* m_simplebook; + wxPanel* m_book_third_panel; + wxPanel* m_book_second_panel; + wxPanel* m_book_first_panel; + + SkipPartCanvas* m_canvas; + Button* m_zoom_in_btn; + Button* m_zoom_out_btn; + Button* m_switch_drag_btn; + CheckBox* m_all_checkbox; + Button* m_percent_label; + Label * m_all_label; + wxPanel* m_line; + wxScrolledWindow* m_list_view; + + Label* m_cnt_label; + Label* m_tot_label; + + Button* m_apply_btn; + + Label* m_loading_label; + Label* m_retry_label; + + wxBoxSizer* m_sizer; + wxBoxSizer* m_dlg_sizer; + wxBoxSizer* m_dlg_content_sizer; + wxBoxSizer* m_dlg_btn_sizer; + wxBoxSizer* m_canvas_sizer; + wxBoxSizer* m_canvas_btn_sizer; + wxBoxSizer* m_list_sizer; + wxBoxSizer* m_scroll_sizer; + wxBoxSizer* m_book_first_sizer; + wxBoxSizer* m_book_second_sizer; + wxBoxSizer* m_book_second_btn_sizer; + Button* m_second_retry_btn; + AnimaIcon* m_loading_icon; + +private: + int m_zoom_percent{100}; + bool m_is_drag{false}; + bool m_print_lock{true}; + + std::map m_parts_state; + std::map m_parts_name; + std::vector m_partskip_ids; + + enum URL_STATE m_url_state = URL_STATE::URL_TCP; + + PartsInfo GetPartsInfo(); + bool is_drag_mode(); + + boost::shared_ptr m_file_sys; + bool m_file_sys_result{false}; + std::string m_timestamp; + std::string m_tmp_path; + std::vector m_local_paths; + std::vector m_target_paths; + std::string create_tmp_path(); + + bool is_local_file_existed(const std::vector &local_paths); + + void DownloadPartsFile(); + void OnFileSystemEvent(wxCommandEvent &event); + void OnFileSystemResult(wxCommandEvent &event); + void fetchUrl(boost::weak_ptr wfs); + + + void OnZoomIn(wxCommandEvent &event); + void OnZoomOut(wxCommandEvent &event); + void OnSwitchDrag(wxCommandEvent &event); + void OnZoomPercent(wxCommandEvent &event); + void UpdatePartsStateFromCanvas(wxCommandEvent &event); + + void UpdateZoomPercent(); + void UpdateCountLabel(); + void UpdateDialogUI(); + bool IsAllChecked(); + + void OnRetryButton(wxCommandEvent &event); + void OnAllCheckbox(wxCommandEvent &event); + void OnApplyDialog(wxCommandEvent &event); +}; + +}} \ No newline at end of file diff --git a/src/slic3r/GUI/Printer/PrinterFileSystem.cpp b/src/slic3r/GUI/Printer/PrinterFileSystem.cpp index 28ac337ae7..4385cbfb52 100644 --- a/src/slic3r/GUI/Printer/PrinterFileSystem.cpp +++ b/src/slic3r/GUI/Printer/PrinterFileSystem.cpp @@ -39,6 +39,7 @@ wxDEFINE_EVENT(EVT_FILE_CHANGED, wxCommandEvent); wxDEFINE_EVENT(EVT_SELECT_CHANGED, wxCommandEvent); wxDEFINE_EVENT(EVT_THUMBNAIL, wxCommandEvent); wxDEFINE_EVENT(EVT_DOWNLOAD, wxCommandEvent); +wxDEFINE_EVENT(EVT_RAMDOWNLOAD, wxCommandEvent); wxDEFINE_EVENT(EVT_FILE_CALLBACK, wxCommandEvent); @@ -248,6 +249,131 @@ struct PrinterFileSystem::Download : Progress boost::uuids::detail::md5 boost_md5; }; + +void PrinterFileSystem::GetPickImages(const std::vector &local_paths, const std::vector &targetpaths) +{ + + GetPickImage(1, local_paths[0], targetpaths[0]); + GetPickImage(2, local_paths[1], targetpaths[1]); + GetPickImage(3, local_paths[2], targetpaths[2]); + +} + +void PrinterFileSystem::GetPickImage(int id, const std::string &local_path, const std::string &targetpath) +{ + json j; + + j["sequence_id"] = id; + j["version"] = 1; + j["peer_host"] = "studio"; + j["command"] = "get_project_file"; + j["file_rel_path"] = targetpath; + + std::string param = j.dump(); + + DownloadRamFile(16, local_path, param); +} + + +void PrinterFileSystem::DownloadRamFile(int index, const std::string &local_path, const std::string & param) +{ + std::shared_ptr download(new Download); + download->local_path = local_path; + + json req; + req["path"] = "mem:/" + std::to_string(index); + req["offset"] = 0; + req["mem_dl_param_size"] = param.size(); + + m_download_seq = SendRequest( + FILE_DOWNLOAD, req, + [download](json const &resp, Progress &prog, unsigned char const *data) -> int { + size_t size = resp.value("size", 0); + prog.size = resp["offset"]; + prog.total = resp["total"]; + + if (resp.contains("mem_dl_param_size")) { + size_t s = resp["mem_dl_param_size"].get(); + std::string json_str(reinterpret_cast(data), s); + // OutputDebugStringA(json_str.c_str()); + // OutputDebugStringA("\n"); + json mem_dl_json = json::parse(json_str); + // download->mem_dl_param_size = size; + if (!mem_dl_json.contains("result") || mem_dl_json["result"] == 1 ) { + wxLogWarning("Download failed: result = 1"); + return ERROR_JSON; + } + return CONTINUE; + } + + if (prog.size == 0 ) { + download->ofs.open(download->local_path, std::ios::binary); + if (!download->ofs) { + download->error = last_system_error(); + wxLogWarning("DownloadImageFromRam open error: %s\n", wxString::FromUTF8(download->error)); + return FILE_OPEN_ERR; + } + } + + download->ofs.write(reinterpret_cast(data), size); + if (!download->ofs) { + download->error = last_system_error(); + wxLogWarning("DownloadImageFromRam write error: %s\n", wxString::FromUTF8(download->error)); + return FILE_READ_WRITE_ERR; + } + + download->boost_md5.process_bytes(data, size); + + prog.size += size; + download->total = prog.total; + download->size = prog.size; + + if (prog.size < prog.total) { + return 0; + } + download->ofs.close(); + + std::string md5 = resp["file_md5"]; + boost::uuids::detail::md5::digest_type digest; + download->boost_md5.get_digest(digest); + for (int i = 0; i < 4; ++i) digest[i] = boost::endian::endian_reverse(digest[i]); + std::string str_md5; + const auto char_digest = reinterpret_cast(&digest[0]); + boost::algorithm::hex(char_digest, char_digest + sizeof(digest), std::back_inserter(str_md5)); + if (!boost::iequals(str_md5, md5)) { + wxLogWarning("DownloadImageFromRam checksum error: %s != %s\n", str_md5, md5); + boost::system::error_code ec; + boost::filesystem::rename(download->local_path, download->local_path + ".tmp", ec); + return FILE_CHECK_ERR; + } + return SUCCESS; + }, + + [this, download](int result, Progress const &data) { + //OutputDebugStringA(std::to_string(result).c_str()); + //OutputDebugStringA("\n"); + if (result == CONTINUE) { return; } + std::string msg; + if (result == SUCCESS) { + wxLogMessage("DownloadImageFromRam finished: %s", download->local_path); + msg = "SUCCESS"; + SendChangedEvent(EVT_RAMDOWNLOAD, result, result ? download->error : download->local_path); + } else if (result != CONTINUE) { + wxLogWarning("DownloadImageFromRam failed: %s", download->error); + msg = "ERROR"; + SendChangedEvent(EVT_RAMDOWNLOAD, result, result ? download->error : download->local_path); + } + },param); +} + +void PrinterFileSystem::SendExistedFile(){ + SendChangedEvent(EVT_RAMDOWNLOAD, SUCCESS); +} +void PrinterFileSystem::SendConnectFail(){ + SendChangedEvent(EVT_RAMDOWNLOAD, ERROR_PIPE); +} + + void PrinterFileSystem::DownloadFiles(size_t index, std::string const &path) { if (index == (size_t) -1) { @@ -281,6 +407,10 @@ void PrinterFileSystem::DownloadFiles(size_t index, std::string const &path) DownloadNextFile(); } + + + + void PrinterFileSystem::DownloadCheckFiles(std::string const &path) { for (size_t i = 0; i < m_file_list.size(); ++i) { @@ -706,7 +836,7 @@ void PrinterFileSystem::DownloadNextFile() file.download.reset(), file.flags &= ~FF_DOWNLOAD; else // FAILED file.download.reset(); - if (&file_index.first == &m_file_list) + if (&file_index.first == &m_file_list) SendChangedEvent(EVT_DOWNLOAD, download->index, result ? download->error : file.local_path, result); } } @@ -1030,7 +1160,7 @@ void PrinterFileSystem::DumpLog(void * thiz, int, tchar const *msg) static_cast(thiz)->Bambu_FreeLogMsg(msg); } -boost::uint32_t PrinterFileSystem::SendRequest(int type, json const &req, callback_t2 const &callback) +boost::uint32_t PrinterFileSystem::SendRequest(int type, json const &req, callback_t2 const &callback,const std::string& param) { if (m_session.tunnel == nullptr) { Retry(); @@ -1044,6 +1174,13 @@ boost::uint32_t PrinterFileSystem::SendRequest(int type, json const &req, callba root["req"] = req; std::ostringstream oss; oss << root; + + if (!param.empty()) { + oss << "\n\n"; + oss << param; + } + // OutputDebugStringA(oss.str().c_str()); + // OutputDebugStringA("\n"); auto msg = oss.str(); boost::unique_lock l(m_mutex); m_messages.push_back(msg); diff --git a/src/slic3r/GUI/Printer/PrinterFileSystem.h b/src/slic3r/GUI/Printer/PrinterFileSystem.h index 5a40e063d6..a94a5e3e96 100644 --- a/src/slic3r/GUI/Printer/PrinterFileSystem.h +++ b/src/slic3r/GUI/Printer/PrinterFileSystem.h @@ -22,6 +22,7 @@ wxDECLARE_EVENT(EVT_FILE_CHANGED, wxCommandEvent); wxDECLARE_EVENT(EVT_SELECT_CHANGED, wxCommandEvent); wxDECLARE_EVENT(EVT_THUMBNAIL, wxCommandEvent); wxDECLARE_EVENT(EVT_DOWNLOAD, wxCommandEvent); +wxDECLARE_EVENT(EVT_RAMDOWNLOAD, wxCommandEvent); class PrinterFileSystem : public wxEvtHandler, public boost::enable_shared_from_this, BambuLib { @@ -138,6 +139,17 @@ public: void DownloadFiles(size_t index, std::string const &path); + void GetPickImage(int id, const std::string &local_path, const std::string &path); + + void GetPickImages(const std::vector &local_paths, const std::vector &targetpaths); + + + void DownloadRamFile(int index, const std::string &local_path, const std::string ¶m); + + void SendExistedFile(); + + void SendConnectFail(); + void DownloadCheckFiles(std::string const &path); bool DownloadCheckFile(size_t index); @@ -218,8 +230,7 @@ private: typedef std::function callback_t2; - template - boost::uint32_t SendRequest(int type, json const& req, Translator const& translator, Callback const& callback) + template boost::uint32_t SendRequest(int type, json const &req, Translator const &translator, Callback const &callback, const std::string ¶m = "") { auto c = [translator, callback, this](int result, json const &resp, unsigned char const *data) -> int { @@ -236,7 +247,7 @@ private: PostCallback(callback, result, t); return result; }; - return SendRequest(type, req, c); + return SendRequest(type, req, c, param); } template using Applier = std::function; @@ -266,7 +277,7 @@ private: InstallNotify(type, c); } - boost::uint32_t SendRequest(int type, json const &req, callback_t2 const &callback); + boost::uint32_t SendRequest(int type, json const &req, callback_t2 const &callback, const std::string ¶m = ""); void InstallNotify(int type, callback_t2 const &callback); diff --git a/src/slic3r/GUI/SkipPartCanvas.cpp b/src/slic3r/GUI/SkipPartCanvas.cpp new file mode 100644 index 0000000000..e9218aa714 --- /dev/null +++ b/src/slic3r/GUI/SkipPartCanvas.cpp @@ -0,0 +1,628 @@ +#include +#include "SkipPartCanvas.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +wxDEFINE_EVENT(EVT_ZOOM_PERCENT, wxCommandEvent); +wxDEFINE_EVENT(EVT_CANVAS_PART, wxCommandEvent); + +namespace Slic3r { +namespace GUI { + +SkipPartCanvas::SkipPartCanvas(wxWindow *parent, const wxGLAttributes& dispAttrs) + : wxGLCanvas(parent, dispAttrs) { + context_ = new wxGLContext(this); + this->Bind(wxEVT_PAINT, &SkipPartCanvas::OnPaint, this); + this->Bind(wxEVT_MOUSEWHEEL, &SkipPartCanvas::OnMouseWheel, this); + this->Bind(wxEVT_LEFT_DOWN, &SkipPartCanvas::OnMouseLeftDown, this); + this->Bind(wxEVT_LEFT_DCLICK, &SkipPartCanvas::OnMouseLeftDown, this); + this->Bind(wxEVT_LEFT_UP, &SkipPartCanvas::OnMouseLeftUp, this); + this->Bind(wxEVT_RIGHT_DOWN, &SkipPartCanvas::OnMouseRightDown, this); + this->Bind(wxEVT_RIGHT_UP, &SkipPartCanvas::OnMouseRightUp, this); + this->Bind(wxEVT_SIZE, &SkipPartCanvas::OnSize, this); + this->Bind(wxEVT_MOTION, &SkipPartCanvas::OnMouseMotion, this); +} + +void SkipPartCanvas::LoadPickImage(const std::string & path) +{ + parts_state_.clear(); + int preffered_w{FromDIP(400)}, preffered_h{FromDIP(400)}; + cv::Mat src_image = cv::imread(path, cv::IMREAD_UNCHANGED); + float zoom_x{static_cast(preffered_w) / src_image.cols}; + float zoom_y{static_cast(preffered_h) / src_image.rows}; + float image_scale{0}; + if (abs(zoom_x - 1) > abs(zoom_y - 1)) + image_scale = zoom_x; + else + image_scale = zoom_y; + cv::resize(src_image, pick_image_, cv::Size(), image_scale, image_scale); + std::vector channels; + cv::split(pick_image_, channels); // channels[3] 是 Alpha + cv::Mat alpha = channels[3]; + cv::Mat mask; + cv::threshold(alpha, mask, 0, 255, cv::THRESH_BINARY); + std::vector> pick_counters; + cv::findContours(mask, pick_counters, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_TC89_KCOS); + std::vector> polygon; + for (const auto& counter : pick_counters) { + cv::Point center_pos{0,0}; + for (const auto& pt : counter) { + center_pos += pt; + } + center_pos = center_pos / static_cast(counter.size()); + auto id = GetIdAtImagePt(wxPoint(center_pos.x, center_pos.y)); + if (id > 0) { + polygon.clear(); + polygon.emplace_back(std::vector{}); + for (const auto& pt : counter) { + polygon[0].push_back(FloatPoint{pt.x * 1.0f, pt.y * 1.0f}); + } + std::vector indices = mapbox::earcut(polygon); + std::vector final_counter{}; + for (size_t i = 0; i < indices.size(); i += 3) { + FloatPoint a = polygon[0][indices[i]]; + FloatPoint b = polygon[0][indices[i+1]]; + FloatPoint c = polygon[0][indices[i+2]]; + final_counter.push_back(FloatPoint{a[0], a[1]}); + final_counter.push_back(FloatPoint{b[0], b[1]}); + final_counter.push_back(FloatPoint{c[0], c[1]}); + } + parts_triangles_.emplace(id, final_counter); + pick_parts_.emplace(id, counter); + if (parts_state_.find(id) == parts_state_.end()) + parts_state_.emplace(id, psUnCheck); + } + } +} + +void SkipPartCanvas::ZoomIn(const int zoom_percent) +{ + SetZoomPercent(zoom_percent_ + zoom_percent); + Refresh(); +} + +void SkipPartCanvas::ZoomOut(const int zoom_percent) +{ + SetZoomPercent(zoom_percent_ - zoom_percent); + Refresh(); +} + +void SkipPartCanvas::SwitchDrag(const bool drag_on) +{ + fixed_draging_ = drag_on; + AutoSetCursor(); +} + + +void SkipPartCanvas::UpdatePartsInfo(const PartsInfo& parts) +{ + for (auto const& part : parts) { + if (auto res = parts_state_.find(part.first); res != parts_state_.end()) + res->second = part.second; + } + Refresh(); +} + +void DrawRoundedRect(float x, float y, float width, float height, float radius, const ColorRGB& color, int segments = 16) +{ + glColor3f(color.r(), color.g(), color.b()); + + // 1. Draw center rectangle + glBegin(GL_QUADS); + glVertex2f(x + radius, y + radius); + glVertex2f(x + width - radius, y + radius); + glVertex2f(x + width - radius, y + height - radius); + glVertex2f(x + radius, y + height - radius); + glEnd(); + + // 2. Draw side rectangles (excluding corners) + glBegin(GL_QUADS); + // Left + glVertex2f(x, y + radius); + glVertex2f(x + radius, y + radius); + glVertex2f(x + radius, y + height - radius); + glVertex2f(x, y + height - radius); + + // Right + glVertex2f(x + width - radius, y + radius); + glVertex2f(x + width, y + radius); + glVertex2f(x + width, y + height - radius); + glVertex2f(x + width - radius, y + height - radius); + + // Top + glVertex2f(x + radius, y + height - radius); + glVertex2f(x + width - radius, y + height - radius); + glVertex2f(x + width - radius, y + height); + glVertex2f(x + radius, y + height); + + // Bottom + glVertex2f(x + radius, y); + glVertex2f(x + width - radius, y); + glVertex2f(x + width - radius, y + radius); + glVertex2f(x + radius, y + radius); + glEnd(); + + // 3. Draw corners + auto drawCorner = [&](float cx, float cy, float startAngle) { + glBegin(GL_TRIANGLE_FAN); + glVertex2f(cx, cy); + for (int i = 0; i <= segments; ++i) { + float angle = startAngle + (M_PI * 0.5f) * (float)i / segments; + glVertex2f(cx + cosf(angle) * radius, cy + sinf(angle) * radius); + } + glEnd(); + }; + + drawCorner(x + radius, y + radius, M_PI); // bottom-left + drawCorner(x + width - radius, y + radius, 1.5f * M_PI); // bottom-right + drawCorner(x + width - radius, y + height - radius, 0.0f); // top-right + drawCorner(x + radius, y + height - radius, 0.5f * M_PI); // top-left +} + + +void SkipPartCanvas::Render() +{ + constexpr float border_w = 3.f; + constexpr int uncheckd_stencil =1; + constexpr int checkd_stencil = 2; + constexpr int skipped_stencil = 3; + + SetCurrent(*context_); + glPushAttrib(GL_ALL_ATTRIB_BITS); + + int w, h; + GetClientSize(&w, &h); + glViewport(0, 0, w, h); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + auto view_rect = ViewPtToImagePt(wxPoint(w, h)); + glOrtho(offset_.x, view_rect.x, view_rect.y, offset_.y, -1, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glClearColor(parent_color_.r(), parent_color_.g(), parent_color_.b(), 1.f); + glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + + float rx = offset_.x; + float ry = offset_.y; + float rw = view_rect.x - offset_.x; + float rh = view_rect.y - offset_.y; + float radius = std::min(rw, rh) * 0.05f; + + DrawRoundedRect(rx, ry, rw, rh, radius, ColorRGB{0.9f, 0.9f, 0.9f}); + + glEnable(GL_BLEND); + glEnable(GL_MULTISAMPLE); + glEnable(GL_LINE_SMOOTH); + glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_CULL_FACE); + glEnable(GL_STENCIL_TEST); + + auto draw_shape = [this, border_w](const int stencil, const PartState part_type, const ColorRGB& rgb) { + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glStencilFunc(GL_ALWAYS, stencil, 0xFF); + glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); + + for (const auto& contour : parts_triangles_) { + auto part_info = parts_state_.find(contour.first); + if (part_info == parts_state_.end() || part_info->second != part_type) + continue; + glColor3f(1, 1, 1); + glBegin(GL_TRIANGLES); + for (size_t i = 0; i < contour.second.size(); i += 3) { + glVertex2f(contour.second[i][0], contour.second[i][1]); + glVertex2f(contour.second[i+1][0], contour.second[i+1][1]); + glVertex2f(contour.second[i+2][0], contour.second[i+2][1]); + } + glEnd(); + } + + for (const auto& contour : pick_parts_) { + if (contour.first != this->hover_id_) continue; + auto part_info = parts_state_.find(contour.first); + if (part_info == parts_state_.end() || part_info->second != part_type) + continue; + + glColor3f(rgb.r(), rgb.g(), rgb.b()); + glLineWidth(border_w); + glBegin(GL_LINE_LOOP); + for (const auto& pt : contour.second) { + glVertex2f(pt.x, pt.y); + } + glEnd(); + } + }; + // draw unchecked shapes + // stencil1 => unchecked + draw_shape(uncheckd_stencil, psUnCheck, ColorRGB{0, 174 / 255.f, 66 / 255.f}); + + // draw checked shapes + // stencil2 => checked + draw_shape(checkd_stencil, psChecked, ColorRGB{208 / 255.f, 27 / 255.f, 66 / 255.f}); + + // draw skipped shapes + // stencil3 => skipped + draw_shape(skipped_stencil, psSkipped, ColorRGB{95 / 255.f, 95 / 255.f, 95 / 255.f}); + + auto draw_mask = [this, view_rect, border_w, w, h](const int stencil, const PartState part_type, + const ColorRGB& background, const ColorRGB& line, const ColorRGB& bound) { + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glStencilFunc(GL_EQUAL, stencil, 0xFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // Don't change stencil + glColor3f(background.r(), background.g(), background.b()); + glBegin(GL_POLYGON); + glVertex2f(offset_.x, offset_.y); + glVertex2f(offset_.x, view_rect.y); + glVertex2f(view_rect.x, view_rect.y); + glVertex2f(view_rect.x, offset_.y); + glEnd(); + // draw main color + glColor3f(line.r(), line.g(), line.b()); + // re-draw shape bound + for (const auto& contour : pick_parts_) { + auto part_info = parts_state_.find(contour.first); + if (part_info == parts_state_.end() || part_info->second != part_type) + continue; + glColor3f(bound.r(), bound.g(), bound.b()); + glLineWidth(border_w); + glBegin(GL_LINE_LOOP); + for (const auto& pt : contour.second) { + glVertex2f(pt.x, pt.y); + } + glEnd(); + } + }; + + draw_mask(checkd_stencil, psChecked, ColorRGB{239 / 255.f, 175 / 255.f, 175 / 255.f}, + ColorRGB{225 / 255.f, 71 / 255.f, 71 / 255.f}, ColorRGB{208 / 255.f, 27 / 255.f, 27 / 255.f}); + + draw_mask(skipped_stencil, psSkipped, ColorRGB{159 / 255.f, 159 / 255.f, 159 / 255.f}, + ColorRGB{95 / 255.f, 95 / 255.f, 95 / 255.f}, ColorRGB{95 / 255.f, 95 / 255.f, 95 / 255.f}); + + glDisable(GL_STENCIL_TEST); + + glPopAttrib(); + glFlush(); +} + +void SkipPartCanvas::DebugLogLine(std::string str) +{ + //if (!log_ctrl) + // return; + //log_ctrl->AppendText(str + "\n"); +} + +void SkipPartCanvas::SendSelectEvent(int id, PartState state) { + wxCommandEvent evt(EVT_CANVAS_PART); + evt.SetExtraLong(id); + evt.SetInt(static_cast(state)); + wxPostEvent(this, evt); +} +void SkipPartCanvas::SendZoomEvent(int zoom_percent) { + wxCommandEvent evt(EVT_ZOOM_PERCENT); + evt.SetInt(zoom_percent_); + wxPostEvent(this, evt); +} + +inline double SkipPartCanvas::Zoom() const +{ + return zoom_percent_ / 100.0f; +} + +inline wxPoint SkipPartCanvas::ViewPtToImagePt(const wxPoint& view_pt) const +{ + return wxPoint(view_pt.x / Zoom(), view_pt.y / Zoom()) + offset_; +} + +uint32_t SkipPartCanvas::GetIdAtImagePt(const wxPoint& image_pt) const +{ + if (image_pt.x >= 0 && image_pt.x < pick_image_.cols + && image_pt.y >= 0 && image_pt.y < pick_image_.rows) { + // at(row, col)=>at(y, x) + cv::Vec4b bgr = pick_image_.at(image_pt.y, image_pt.x); + SkipIdHelper helper{bgr[2], bgr[1], bgr[0]}; + helper.reverse(); + return helper.value; + } else { + return 0; + } +} + +inline uint32_t SkipPartCanvas::GetIdAtViewPt(const wxPoint& view_pt) const +{ + wxPoint pt_at_image = ViewPtToImagePt(view_pt); + return GetIdAtImagePt(pt_at_image); +} + +void SkipPartCanvas::SetZoomPercent(const int value) +{ + zoom_percent_ = std::clamp(value, 100, 1000); + std::ostringstream oss; + oss << "zoom to " << zoom_percent_; + DebugLogLine(oss.str()); + + SendZoomEvent(zoom_percent_); +} + +void SkipPartCanvas::SetOffset(const wxPoint& value) +{ + int w, h; + GetClientSize(&w, &h); + int max_w = static_cast(w * (1 - 1 / Zoom())) >= 0 ? static_cast(w * (1 - 1 / Zoom())) : 0; + int max_h = static_cast(w * (1 - 1 / Zoom())) >= 0 ? static_cast(h * (1 - 1 / Zoom())) : 0; + offset_.x = std::clamp(value.x, 0, max_w); + offset_.y = std::clamp(value.y, 0, max_h); +} + +void SkipPartCanvas::AutoSetCursor() +{ + if(is_draging_ || fixed_draging_) + SetCursor(wxCursor(wxCURSOR_HAND)); + else + SetCursor(wxCursor(wxCURSOR_NONE)); +} + +void SkipPartCanvas::StartDrag(const wxPoint& mouse_pt) +{ + drag_start_pt_ = mouse_pt; + drag_start_offset_ = offset_; + is_draging_ = true; + AutoSetCursor(); +} + +void SkipPartCanvas::ProcessDrag(const wxPoint& mouse_pt) +{ + wxPoint drag_offset = mouse_pt - drag_start_pt_; + SetOffset(- wxPoint(drag_offset.x / Zoom(), drag_offset.y / Zoom()) + drag_start_offset_); + Refresh(); +} + +void SkipPartCanvas::ProcessHover(const wxPoint& mouse_pt) +{ + auto id_at_mouse = GetIdAtViewPt(mouse_pt); + int new_hover_id { -1 }; + auto part_state = parts_state_.find(id_at_mouse); + if (part_state != parts_state_.end() && part_state->second == psUnCheck) { + new_hover_id = id_at_mouse; + }; + if (new_hover_id != this->hover_id_) { + this->hover_id_ = new_hover_id; + Refresh(); + } +} + +void SkipPartCanvas::EndDrag() +{ + is_draging_ = false; + AutoSetCursor(); +} + + void SkipPartCanvas::OnPaint(wxPaintEvent &event) + { + wxPaintDC dc(this); + if (!IsShown()) return; + + SetCurrent(*context_); + + Render(); + SwapBuffers(); + } + +void SkipPartCanvas::OnSize(wxSizeEvent& event) +{ + event.Skip(); +} + +void SkipPartCanvas::OnMouseLeftDown(wxMouseEvent& event) +{ + DebugLogLine("OnMouseLeftDown"); + if (!event.LeftIsDown()) { + event.Skip(); + DebugLogLine("skip----OnMouseLeftDown"); + return; + } + if (fixed_draging_) + StartDrag(wxPoint(event.GetX(), event.GetY())); + left_down_ = true; +} + +void SkipPartCanvas::OnMouseLeftUp(wxMouseEvent& event) +{ + DebugLogLine("OnMouseLeftUp"); + if (event.LeftIsDown() || !left_down_) { + event.Skip(); + DebugLogLine("skip----OnMouseLeftUp"); + return; + } + auto id_at_mouse = GetIdAtViewPt(wxPoint(event.GetX(), event.GetY())); + auto part_state = parts_state_.find(id_at_mouse); + if (part_state != parts_state_.end() && part_state->second != psSkipped) { + if (part_state->second == psUnCheck) + part_state = parts_state_.insert_or_assign(part_state->first, psChecked).first; + else + part_state = parts_state_.insert_or_assign(part_state->first, psUnCheck).first; + // if (select_callback_) + // select_callback_(part_state->first, part_state->second); + SendSelectEvent(part_state->first, part_state->second); + } + left_down_ = false; + if (fixed_draging_) + EndDrag(); + else { + Refresh(); + } +} + +void SkipPartCanvas::OnMouseRightDown(wxMouseEvent& event) +{ + DebugLogLine("OnMouseRightDown"); + if (!event.RightIsDown()) { + event.Skip(); + DebugLogLine("skip----OnMouseRightDown"); + return; + } + StartDrag(wxPoint(event.GetX(), event.GetY())); +} + +void SkipPartCanvas::OnMouseRightUp(wxMouseEvent& event) +{ + DebugLogLine("OnMouseRightUp"); + if (event.RightIsDown() || !is_draging_) { + event.Skip(); + DebugLogLine("skip----OnMouseRightUp"); + return; + } + EndDrag(); +} + +void SkipPartCanvas::OnMouseMotion(wxMouseEvent& event) +{ + ProcessHover(wxPoint(event.GetX(), event.GetY())); + if (!event.RightIsDown() && !(event.LeftIsDown() && fixed_draging_)) { + event.Skip(); + return; + } + ProcessDrag(wxPoint(event.GetX(), event.GetY())); +} + +void SkipPartCanvas::OnMouseWheel(wxMouseEvent& event) +{ + wxPoint view_mouse = wxPoint(event.GetX(), event.GetY()); + auto pre_image_pos = ViewPtToImagePt(view_mouse); + SetZoomPercent(zoom_percent_ + 10 * (event.GetWheelRotation() / 120.0)); + auto now_image_pos = ViewPtToImagePt(view_mouse); + SetOffset(offset_ - (now_image_pos - pre_image_pos)); + Refresh(); +} + +// Base class with error messages management + +void _BBS_3MF_Base::add_error(const std::string &error) const +{ + boost::unique_lock l(mutex); + m_errors.push_back(error); +} +void _BBS_3MF_Base::clear_errors() { m_errors.clear(); } + +void _BBS_3MF_Base::log_errors() +{ + for (const std::string &error : m_errors) BOOST_LOG_TRIVIAL(error) << error; +} + + +ModelSettingHelper::ModelSettingHelper(const std::string &path) : path_(path) {} + +bool ModelSettingHelper::Parse() +{ + boost::nowide::fstream fs(path_); + if (!fs) { + add_error("Failed to open file\n"); + return false; + } + XML_Parser parser = XML_ParserCreate(nullptr); + if (!parser) { + add_error("Unable to create parser"); + return false; + } + XML_SetUserData(parser, this); + XML_SetElementHandler(parser, ModelSettingHelper::StartElementHandler, ModelSettingHelper::EndElementHandler); + + try { + char buffer[4000]; + while (fs.read(buffer, sizeof(buffer)) || fs.gcount() > 0) { + auto ret = XML_Parse(parser, buffer, static_cast(fs.gcount()), fs.eof()); + if (ret != XML_STATUS_OK) { + add_error("return value of XML_Parse doesn't match XM_STATUS_OK"); + XML_ParserFree(parser); + return false; + } + } + } + catch (std::exception& e) { + add_error(std::string("exception:") + e.what()); + XML_ParserFree(parser); + return false; + } + XML_ParserFree(parser); + return true; +} + +std::vector ModelSettingHelper::GetResults() { return context_.objects; } + + +void XMLCALL ModelSettingHelper::StartElementHandler(void *userData, const XML_Char *name, const XML_Char **atts) +{ + ModelSettingHelper* self = static_cast(userData); + if (strcmp(name, "object") == 0) { + for (int i = 0; atts[i]; i += 2) { + if (strcmp(atts[i], "id") == 0) { + self->context_.current_object_id = atoi(atts[i+1]); + } + } + } else if ((self->context_.current_object_id != -1) + && (strcmp(name, "metadata") == 0) + && (self->context_.node_name.top() == "object")) { + const XML_Char *key{nullptr}, *value{nullptr}; + for (int i = 0; atts[i]; i += 2) { + if (strcmp(atts[i], "key") == 0) key = atts[i+1]; + if (strcmp(atts[i], "value") == 0) value = atts[i+1]; + } + if (key && value && (strcmp(key, "name") == 0)) { + self->context_.temp_object.id = self->context_.current_object_id; + self->context_.temp_object.name = std::string(value); + } + } else if ((strcmp(name, "metadata") == 0) + && (self->context_.node_name.top() == "model_instance")) { + const XML_Char *key{nullptr}, *value{nullptr}; + for (int i = 0; atts[i]; i += 2) { + if (strcmp(atts[i], "key") == 0) key = atts[i+1]; + if (strcmp(atts[i], "value") == 0) value = atts[i+1]; + } + if (key && value && (strcmp(key, "object_id") == 0)) { + self->context_.current_instance_object_id = atoi(value); + } else if (key && value && (strcmp(key, "identify_id") == 0)) { + self->context_.current_instance_identify_id = atoi(value); + } + } + self->context_.node_name.push(std::string(name)); +} + +void XMLCALL ModelSettingHelper::EndElementHandler(void *userData, const XML_Char *name) +{ + ModelSettingHelper* self = static_cast(userData); + if (strcmp(name, "object") == 0) { + self->context_.objects.push_back(self->context_.temp_object); + self->context_.current_object_id = -1; + } + else if ((strcmp(name, "model_instance") == 0) + && (self->context_.current_instance_identify_id != -1) + && (self->context_.current_instance_object_id != -1)) { + for (auto& obj : self->context_.objects) { + if (obj.id == self->context_.current_instance_object_id) { + obj.identify_id = self->context_.current_instance_identify_id; + break; + } + } + self->context_.current_instance_identify_id = -1; + self->context_.current_instance_object_id = -1; + } else if (strcmp(name, "object") == 0) { + self->context_.current_object_id = -1; + } + self->context_.node_name.pop(); +} + +void ModelSettingHelper::DataHandler(const XML_Char *s, int len) +{ + // do nothing +} +} +} \ No newline at end of file diff --git a/src/slic3r/GUI/SkipPartCanvas.hpp b/src/slic3r/GUI/SkipPartCanvas.hpp new file mode 100644 index 0000000000..b4a6ad3d2c --- /dev/null +++ b/src/slic3r/GUI/SkipPartCanvas.hpp @@ -0,0 +1,163 @@ +#ifndef SKIPPARTCANVAS_H +#define SKIPPARTCANVAS_H +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "PartSkipCommon.hpp" + +wxDECLARE_EVENT(EVT_ZOOM_PERCENT, wxCommandEvent); +wxDECLARE_EVENT(EVT_CANVAS_PART, wxCommandEvent); + +namespace Slic3r { +namespace GUI { + + +using Coord = float; +using FloatPoint = std::array; + + +struct ObjectInfo { + int id{-1}; + std::string name{""}; + int identify_id{-1}; + PartState state{psUnCheck}; +}; + +class SkipPartCanvas : public wxGLCanvas +{ + union SkipIdHelper + { + uint32_t value = 0; + + struct + { + uint8_t b; + uint8_t g; + uint8_t r; + uint8_t _padding; + }; + + uint8_t bytes[4]; + + SkipIdHelper() = default; + + SkipIdHelper(uint8_t red, uint8_t green, uint8_t blue) + : r(red), g(green), b(blue), _padding(0) {} + + SkipIdHelper(uint32_t val): value(val){} + void reverse() { + uint8_t tmp{r}; + r = b; + b = tmp; + } + }; +public: + SkipPartCanvas(wxWindow *parent, const wxGLAttributes& dispAttrs); + ~SkipPartCanvas() = default; + + void SetParentBackground(const ColorRGB& color) { + parent_color_ = color; + } + + void LoadPickImage(const std::string& path); + void ZoomIn(const int zoom_percent); + void ZoomOut(const int zoom_percent); + void SwitchDrag(const bool drag_on); + void UpdatePartsInfo(const PartsInfo& parts); + void SetZoomPercent(const int value); + void SetOffset(const wxPoint &value); + + wxTextCtrl* log_ctrl; +protected: + void OnPaint(wxPaintEvent& event); + void OnSize(wxSizeEvent& event); + void OnMouseLeftDown(wxMouseEvent& event); + void OnMouseLeftUp(wxMouseEvent& event); + void OnMouseRightDown(wxMouseEvent& event); + void OnMouseRightUp(wxMouseEvent& event); + void OnMouseMotion(wxMouseEvent& event); + void OnMouseWheel(wxMouseEvent& event); +private: + wxGLContext* context_; + cv::Mat pick_image_; + std::unordered_map> parts_triangles_; + std::unordered_map> pick_parts_; + std::unordered_map parts_state_; + bool gl_inited_{false}; + int zoom_percent_{100}; + wxPoint offset_{0,0}; + wxPoint drag_start_offset_{0,0}; + wxPoint drag_start_pt_{0,0}; + bool is_draging_{false}; + bool fixed_draging_{false}; + bool left_down_{false}; + ColorRGB parent_color_ = ColorRGB(); + int hover_id_{-1}; + + void SendSelectEvent(int id, PartState state); + void SendZoomEvent(int zoom_percent); + + inline double Zoom() const; + inline wxPoint ViewPtToImagePt(const wxPoint& view_pt) const; + uint32_t GetIdAtImagePt(const wxPoint& image_pt) const; + inline uint32_t GetIdAtViewPt(const wxPoint& view_pt) const; + + void ProcessHover(const wxPoint& mouse_pt); + void AutoSetCursor(); + void StartDrag(const wxPoint& mouse_pt); + void ProcessDrag(const wxPoint& mouse_pt); + void EndDrag(); + + void Render(); + + void DebugLogLine(std::string str); +}; + +class _BBS_3MF_Base +{ + mutable boost::mutex mutex; + mutable std::vector m_errors; + +protected: + void add_error(const std::string& error) const; + void clear_errors(); + +public: + void log_errors(); +}; + +class ModelSettingHelper : public _BBS_3MF_Base { + struct ParseContext{ + std::vector objects; + ObjectInfo temp_object; + + std::stack node_name; + int current_object_id{-1}; + int current_instance_identify_id{-1}; + int current_instance_object_id{-1}; + }; + +public: + ModelSettingHelper(const std::string& path); + + + bool Parse(); + std::vector GetResults(); +private: + std::string path_; + ParseContext context_; + + void static XMLCALL StartElementHandler(void* userData, const XML_Char* name, const XML_Char** atts); + void static XMLCALL EndElementHandler(void* userData, const XML_Char* name); + void DataHandler(const XML_Char* s, int len); +}; + +} +} +#endif //SKIPPARTCANVAS_H \ No newline at end of file diff --git a/src/slic3r/GUI/StatusPanel.cpp b/src/slic3r/GUI/StatusPanel.cpp index 88aca02cb1..25138c0168 100644 --- a/src/slic3r/GUI/StatusPanel.cpp +++ b/src/slic3r/GUI/StatusPanel.cpp @@ -278,6 +278,20 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) bSizer_task_btn->Add(FromDIP(10), 0, 0); + StateColor white_bg(std::pair(wxColour(255, 255, 255), StateColor::Disabled), std::pair(wxColour(255, 255, 255), StateColor::Pressed), + std::pair(wxColour(255, 255, 255), StateColor::Hovered), std::pair(wxColour(255, 255, 255), StateColor::Enabled), + std::pair(wxColour(255, 255, 255), StateColor::Normal)); + m_button_partskip = new Button(m_panel_progress, ""); + m_button_partskip->Enable(false); + m_button_partskip->SetBackgroundColor(white_bg); + m_button_partskip->SetIcon("print_control_partskip_disable"); + m_button_partskip->SetBorderColor(*wxWHITE); + m_button_partskip->SetFont(Label::Body_12); + m_button_partskip->SetCornerRadius(0); + m_button_partskip->SetToolTip(_L("Parts Skip")); + m_button_partskip->Bind(wxEVT_ENTER_WINDOW, [this](auto &e) { m_button_partskip->SetIcon("print_control_partskip"); }); + m_button_partskip->Bind(wxEVT_LEAVE_WINDOW, [this](auto &e) { m_button_partskip->SetIcon("print_control_partskip"); }); + m_button_pause_resume = new ScalableButton(m_panel_progress, wxID_ANY, "print_control_pause", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER,true); m_button_pause_resume->Bind(wxEVT_ENTER_WINDOW, [this](auto &e) { @@ -314,6 +328,8 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) m_sizer_progressbar->Add(m_gauge_progress, 1, wxALIGN_CENTER_VERTICAL, 0); m_sizer_progressbar->Add(0, 0, 0, wxEXPAND|wxLEFT, FromDIP(18)); + m_sizer_progressbar->Add(m_button_partskip, 0, wxALL, FromDIP(5)); + m_sizer_progressbar->Add(0, 0, 0, wxEXPAND | wxLEFT, FromDIP(18)); m_sizer_progressbar->Add(m_button_pause_resume, 0, wxALL, FromDIP(5)); m_sizer_progressbar->Add(0, 0, 0, wxEXPAND|wxLEFT, FromDIP(18)); m_sizer_progressbar->Add(m_button_abort, 0, wxALL, FromDIP(5)); @@ -638,6 +654,22 @@ void PrintingTaskPanel::reset_printing_value() this->set_plate_index(-1); } +void PrintingTaskPanel::update_machine_object(MachineObject* obj){ + if(obj) m_obj = obj; +} + +void PrintingTaskPanel::enable_partskip_button(bool enable) +{ + if (!enable) { + m_button_partskip->Enable(false); + m_button_partskip->SetLabel(""); + m_button_partskip->SetIcon("print_control_partskip_disable"); + }else if(m_obj && m_obj->is_support_brtc){ + m_button_partskip->Enable(true); + m_button_partskip->SetIcon("print_control_partskip"); + } +} + void PrintingTaskPanel::enable_pause_resume_button(bool enable, std::string type) { if (!enable) { @@ -1731,6 +1763,7 @@ StatusPanel::StatusPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, co m_switch_cham_fan->SetValue(false); /* set default enable state */ + m_project_task_panel->enable_partskip_button(false); m_project_task_panel->enable_pause_resume_button(false, "resume_disable"); m_project_task_panel->enable_abort_button(false); @@ -1751,6 +1784,7 @@ StatusPanel::StatusPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, co // Connect Events m_project_task_panel->get_bitmap_thumbnail()->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(StatusPanel::refresh_thumbnail_webrequest), NULL, this); + m_project_task_panel->get_partskip_button()->Connect(wxEVT_LEFT_DOWN, wxCommandEventHandler(StatusPanel::on_subtask_partskip), NULL, this); m_project_task_panel->get_pause_resume_button()->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_pause_resume), NULL, this); m_project_task_panel->get_abort_button()->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_abort), NULL, this); m_project_task_panel->get_market_scoring_button()->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_market_scoring), NULL, this); @@ -1810,6 +1844,7 @@ StatusPanel::~StatusPanel() { // Disconnect Events m_project_task_panel->get_bitmap_thumbnail()->Disconnect(wxEVT_LEFT_DOWN, wxMouseEventHandler(StatusPanel::refresh_thumbnail_webrequest), NULL, this); + m_project_task_panel->get_partskip_button()->Disconnect(wxEVT_LEFT_DOWN, wxCommandEventHandler(StatusPanel::on_subtask_partskip), NULL, this); m_project_task_panel->get_pause_resume_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_pause_resume), NULL, this); m_project_task_panel->get_abort_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_subtask_abort), NULL, this); m_project_task_panel->get_market_scoring_button()->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(StatusPanel::on_market_scoring), NULL, this); @@ -1940,6 +1975,17 @@ void StatusPanel::on_market_retry(wxCommandEvent &event) } } +void StatusPanel::on_subtask_partskip(wxCommandEvent &event) +{ + if (m_partskip_dlg == nullptr) { + m_partskip_dlg = new PartSkipDialog(this->GetParent()); + } + + auto dm = GUI::wxGetApp().getDeviceManager(); + m_partskip_dlg->InitSchedule(dm->get_selected_machine()); + m_partskip_dlg->ShowModal(); +} + void StatusPanel::on_subtask_pause_resume(wxCommandEvent &event) { if (obj) { @@ -3089,9 +3135,12 @@ void StatusPanel::update_subtask(MachineObject *obj) || obj->is_in_calibration()) { reset_printing_values(); } else if (obj->is_in_printing() || obj->print_status == "FINISH") { + update_partskip_subtask(obj); + if (obj->is_in_prepare() || obj->print_status == "SLICING") { m_project_task_panel->market_scoring_hide(); m_project_task_panel->get_request_failed_panel()->Hide(); + m_project_task_panel->enable_partskip_button(false); m_project_task_panel->enable_abort_button(false); m_project_task_panel->enable_pause_resume_button(false, "pause_disable"); wxString prepare_text; @@ -3133,7 +3182,7 @@ void StatusPanel::update_subtask(MachineObject *obj) } else { m_project_task_panel->enable_pause_resume_button(true, "pause"); } - + m_project_task_panel->enable_partskip_button(true); // update printing stage m_project_task_panel->update_left_time(obj->mc_left_time); if (obj->subtask_) { @@ -3150,6 +3199,7 @@ void StatusPanel::update_subtask(MachineObject *obj) if (obj->is_printing_finished()) { obj->update_model_task(); m_project_task_panel->enable_abort_button(false); + m_project_task_panel->enable_partskip_button(false); m_project_task_panel->enable_pause_resume_button(false, "resume_disable"); // is makeworld subtask if (wxGetApp().has_model_mall() && obj->is_makeworld_subtask()) { @@ -3217,6 +3267,26 @@ void StatusPanel::update_subtask(MachineObject *obj) Layout(); } +void StatusPanel::update_partskip_subtask(MachineObject *obj){ + if (!obj) return; + if (!obj->subtask_) return; + + m_project_task_panel->update_machine_object(obj); + + auto partskip_button = m_project_task_panel->get_partskip_button(); + if (partskip_button) { + int part_cnt = obj->m_partskip_ids.size(); + if (part_cnt > 0) + partskip_button->SetLabel(wxString::Format(_L("(%d)"), part_cnt)); + else + partskip_button->SetLabel(""); + } + + if(m_partskip_dlg && m_partskip_dlg->IsShown()) { + m_partskip_dlg->UpdatePartsStateFromPrinter(obj); + } +} + void StatusPanel::update_cloud_subtask(MachineObject *obj) { if (!obj) return; @@ -3281,6 +3351,7 @@ void StatusPanel::update_sdcard_subtask(MachineObject *obj) void StatusPanel::reset_printing_values() { + m_project_task_panel->enable_partskip_button(false); m_project_task_panel->enable_pause_resume_button(false, "pause_disable"); m_project_task_panel->enable_abort_button(false); m_project_task_panel->reset_printing_value(); diff --git a/src/slic3r/GUI/StatusPanel.hpp b/src/slic3r/GUI/StatusPanel.hpp index 287858de9a..6afc194cd8 100644 --- a/src/slic3r/GUI/StatusPanel.hpp +++ b/src/slic3r/GUI/StatusPanel.hpp @@ -32,6 +32,7 @@ #include "Widgets/AMSControl.hpp" #include "Widgets/FanControl.hpp" #include "HMS.hpp" +#include "PartSkipDialog.hpp" class StepIndicator; @@ -160,7 +161,7 @@ public: private: - MachineObject* m_obj; + MachineObject* m_obj{nullptr}; ScalableBitmap m_thumbnail_placeholder; wxBitmap m_thumbnail_bmp_display; ScalableBitmap m_bitmap_use_time; @@ -192,6 +193,7 @@ private: wxStaticBitmap* m_bitmap_static_use_weight; ScalableButton* m_button_pause_resume; ScalableButton* m_button_abort; + Button* m_button_partskip; Button* m_button_market_scoring; Button* m_button_clean; Button * m_button_market_retry; @@ -217,6 +219,7 @@ public: void msw_rescale(); public: + void enable_partskip_button(bool enable); void enable_pause_resume_button(bool enable, std::string type); void enable_abort_button(bool enable); void update_subtask_name(wxString name); @@ -232,10 +235,12 @@ public: void set_plate_index(int plate_idx = -1); void market_scoring_show(); void market_scoring_hide(); + void update_machine_object(MachineObject* obj); public: ScalableButton* get_abort_button() {return m_button_abort;}; ScalableButton* get_pause_resume_button() {return m_button_pause_resume;}; + Button* get_partskip_button() { return m_button_partskip; }; Button* get_market_scoring_button() {return m_button_market_scoring;}; Button * get_market_retry_buttom() { return m_button_market_retry; }; Button* get_clean_button() {return m_button_clean;}; @@ -328,6 +333,7 @@ protected: wxStaticText * m_staticText_progress_left; wxStaticText * m_staticText_layers; Button * m_button_report; + Button * m_button_partskip; ScalableButton *m_button_pause_resume; ScalableButton *m_button_abort; Button * m_button_clean; @@ -402,6 +408,7 @@ protected: PrintingTaskPanel * m_project_task_panel; // Virtual event handlers, override them in your derived class + virtual void on_subtask_partskip(wxCommandEvent &event) { event.Skip(); } virtual void on_subtask_pause_resume(wxCommandEvent &event) { event.Skip(); } virtual void on_subtask_abort(wxCommandEvent &event) { event.Skip(); } virtual void on_lamp_switch(wxCommandEvent &event) { event.Skip(); } @@ -484,6 +491,7 @@ protected: FanControlPopup* m_fan_control_popup{nullptr}; ExtrusionCalibration *m_extrusion_cali_dlg{nullptr}; + PartSkipDialog *m_partskip_dlg{nullptr}; wxString m_request_url; bool m_start_loading_thumbnail = false; @@ -525,6 +533,7 @@ protected: void on_market_scoring(wxCommandEvent &event); void on_market_retry(wxCommandEvent &event); + void on_subtask_partskip(wxCommandEvent &event); void on_subtask_pause_resume(wxCommandEvent &event); void on_subtask_abort(wxCommandEvent &event); void on_print_error_clean(wxCommandEvent &event); @@ -602,6 +611,7 @@ protected: void update_basic_print_data(bool def = false); void update_model_info(); void update_subtask(MachineObject* obj); + void update_partskip_subtask(MachineObject *obj); void update_cloud_subtask(MachineObject *obj); void update_sdcard_subtask(MachineObject *obj); void update_temp_ctrl(MachineObject *obj); From 7a90b26e57449567f864efce2d312f348a0d4328 Mon Sep 17 00:00:00 2001 From: Noisyfox Date: Fri, 26 Sep 2025 13:29:14 +0800 Subject: [PATCH 05/27] OpenCV don't build its own libjpeg, to avoid symbol conflict --- deps/OpenCV/OpenCV.cmake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/deps/OpenCV/OpenCV.cmake b/deps/OpenCV/OpenCV.cmake index 3794094fcf..4446936fd0 100644 --- a/deps/OpenCV/OpenCV.cmake +++ b/deps/OpenCV/OpenCV.cmake @@ -12,6 +12,7 @@ orcaslicer_add_cmake_project(OpenCV URL https://github.com/opencv/opencv/archive/refs/tags/4.6.0.tar.gz URL_HASH SHA256=1ec1cba65f9f20fe5a41fda1586e01c70ea0c9a6d7b67c9e13edf0cfe2239277 PATCH_COMMAND git apply ${OpenCV_DIRECTORY_FLAG} --verbose --ignore-space-change --whitespace=fix ${CMAKE_CURRENT_LIST_DIR}/0001-vs2022.patch ${CMAKE_CURRENT_LIST_DIR}/0002-clang19-macos.patch + DEPENDS ${JPEG_PKG} CMAKE_ARGS -DBUILD_SHARED_LIBS=0 -DBUILD_PERE_TESTS=OFF @@ -20,7 +21,7 @@ orcaslicer_add_cmake_project(OpenCV -DBUILD_EXAMPLES=OFF -DBUILD_JASPER=OFF -DBUILD_JAVA=OFF - -DBUILD_JPEG=ON + -DBUILD_JPEG=OFF -DBUILD_APPS_LIST=version -DBUILD_opencv_apps=OFF -DBUILD_opencv_java=OFF From 5bc21568cfaafac49e6099f3ee5fa145440e89e6 Mon Sep 17 00:00:00 2001 From: hemai Date: Mon, 30 Jun 2025 17:17:27 +0800 Subject: [PATCH 06/27] ENH: advance part skip dialog Jira: [STUDIO-12687] Change-Id: Ie6805c57f478ae5a10f81b49dc5a4e45fb40dbc7 (cherry picked from commit 8ed17efc32f8c635dc50e27bf7146fd2eb70941d) --- localization/i18n/list.txt | 4 + src/slic3r/GUI/DeviceManager.cpp | 1 + src/slic3r/GUI/DeviceManager.hpp | 1 + src/slic3r/GUI/PartSkipDialog.cpp | 437 ++++++++++++------- src/slic3r/GUI/PartSkipDialog.hpp | 24 +- src/slic3r/GUI/Printer/PrinterFileSystem.cpp | 4 +- src/slic3r/GUI/SkipPartCanvas.cpp | 72 +-- src/slic3r/GUI/SkipPartCanvas.hpp | 10 +- src/slic3r/GUI/StatusPanel.cpp | 88 ++-- src/slic3r/GUI/Widgets/AnimaController.cpp | 6 +- 10 files changed, 356 insertions(+), 291 deletions(-) diff --git a/localization/i18n/list.txt b/localization/i18n/list.txt index f3792b3453..a2c2b64dce 100644 --- a/localization/i18n/list.txt +++ b/localization/i18n/list.txt @@ -177,3 +177,7 @@ src/slic3r/Utils/Flashforge.cpp src/slic3r/GUI/Jobs/OAuthJob.cpp src/slic3r/GUI/BackgroundSlicingProcess.cpp src/slic3r/GUI/Gizmos/GLGizmoBrimEars.cpp +src/slic3r/GUI/PartSkipDialog.cpp +src/slic3r/GUI/PartSkipDialog.hpp +src/slic3r/GUI/SkipCanvas.cpp +src/slic3r/GUI/SkipCanvas.cpp diff --git a/src/slic3r/GUI/DeviceManager.cpp b/src/slic3r/GUI/DeviceManager.cpp index ada83175d0..2e4ae75ada 100644 --- a/src/slic3r/GUI/DeviceManager.cpp +++ b/src/slic3r/GUI/DeviceManager.cpp @@ -5609,6 +5609,7 @@ void MachineObject::parse_new_info(json print) is_support_upgrade_kit = get_flag_bits(cfg, 14); is_support_command_homing = get_flag_bits(fun, 32); is_support_brtc = get_flag_bits(fun, 31); + is_support_partskip = get_flag_bits(fun, 49); } /*aux*/ diff --git a/src/slic3r/GUI/DeviceManager.hpp b/src/slic3r/GUI/DeviceManager.hpp index 7f66c30f25..ea42c723b2 100644 --- a/src/slic3r/GUI/DeviceManager.hpp +++ b/src/slic3r/GUI/DeviceManager.hpp @@ -876,6 +876,7 @@ public: bool is_support_upgrade_kit{false}; bool is_support_command_homing { false };// fun[32] bool is_support_brtc{false}; // fun[31], support tcp and upload protocol + bool is_support_partskip{false}; bool installed_upgrade_kit{false}; int nozzle_max_temperature = -1; diff --git a/src/slic3r/GUI/PartSkipDialog.cpp b/src/slic3r/GUI/PartSkipDialog.cpp index 2f72abdb0f..bfe98371d2 100644 --- a/src/slic3r/GUI/PartSkipDialog.cpp +++ b/src/slic3r/GUI/PartSkipDialog.cpp @@ -40,24 +40,25 @@ PartSkipDialog::PartSkipDialog(wxWindow* parent): DPIDialog(parent, wxID_ANY, _L std::stringstream buf; buf << put_time(std::localtime(&t), "%a_%b_%d_%H_%M_%S/"); m_timestamp = buf.str(); - + std::string icon_path = (boost::format("%1%/images/BambuStudioTitle.ico") % Slic3r::resources_dir()).str(); SetIcon(wxIcon(Slic3r::encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO)); SetBackgroundColour(*wxWHITE); - SetSizeHints( wxDefaultSize, wxDefaultSize ); - + m_sizer = new wxBoxSizer( wxVERTICAL ); - auto m_line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL); + + m_line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL); + m_line_top->SetMinSize(wxSize(-1, 1)); m_line_top->SetBackgroundColour(wxColour(0xA6, 0xa9, 0xAA)); - m_sizer->Add(m_line_top, 0, wxEXPAND, FromDIP(5)); - m_sizer->Add(0, 0, 0, wxTOP, FromDIP(5)); - + m_sizer->Add(m_line_top, 0, wxEXPAND | wxTOP, FromDIP(5)); + m_simplebook = new wxSimplebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0); - m_book_first_panel = new wxPanel( m_simplebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_simplebook->SetMinSize(wxSize(FromDIP(720), FromDIP(535))); + m_simplebook->SetMaxSize(wxSize(FromDIP(720), FromDIP(535))); + m_book_first_panel = new wxPanel(m_simplebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); m_book_third_panel = new wxPanel(m_simplebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); m_book_third_panel->SetBackgroundColour(*wxWHITE); - m_dlg_sizer = new wxBoxSizer( wxVERTICAL ); m_dlg_content_sizer = new wxBoxSizer( wxHORIZONTAL ); m_canvas_sizer = new wxBoxSizer( wxVERTICAL ); @@ -66,14 +67,13 @@ PartSkipDialog::PartSkipDialog(wxWindow* parent): DPIDialog(parent, wxID_ANY, _L wxGLAttributes canvasAttrs; canvasAttrs.PlatformDefaults().Defaults().Stencil(8).EndList(); m_canvas = new SkipPartCanvas(m_book_third_panel, canvasAttrs); - m_canvas->SetPosition(wxPoint(FromDIP(10), FromDIP(10))); m_canvas->SetMinSize( wxSize( FromDIP(400),FromDIP(400) ) ); + m_canvas->SetMaxSize( wxSize( FromDIP(400),FromDIP(400) ) ); - m_canvas_sizer->Add( m_canvas, 0, wxALL, FromDIP(10) ); m_canvas_btn_sizer = new wxBoxSizer( wxHORIZONTAL ); - m_canvas_btn_sizer->SetMinSize( wxSize( FromDIP(214),FromDIP(28) ) ); - + m_canvas_btn_sizer->SetMinSize( wxSize( -1,FromDIP(28))); + StateColor zoom_bg(std::pair(wxColour(255, 255, 255), StateColor::Disabled), std::pair(wxColour(206, 206, 206), StateColor::Pressed), std::pair(wxColour(238, 238, 238), StateColor::Hovered), std::pair(wxColour(255, 255, 255), StateColor::Enabled), std::pair(wxColour(255, 255, 255), StateColor::Normal)); @@ -81,15 +81,14 @@ PartSkipDialog::PartSkipDialog(wxWindow* parent): DPIDialog(parent, wxID_ANY, _L StateColor zoom_bd(std::pair(wxColour(144, 144, 144), StateColor::Disabled), std::pair(wxColour(38, 46, 48), StateColor::Enabled)); StateColor zoom_text(std::pair(wxColour(144, 144, 144), StateColor::Disabled), std::pair(wxColour(38, 46, 48), StateColor::Enabled)); - m_zoom_out_btn = new Button(m_book_third_panel, _L("")); + m_zoom_out_btn = new Button(m_book_third_panel, wxEmptyString); m_zoom_out_btn->SetIcon("canvas_zoom_out"); m_zoom_out_btn->SetToolTip(_L("Zoom Out")); m_zoom_out_btn->SetBackgroundColor(zoom_bg); m_zoom_out_btn->SetBorderColor(wxColour(238, 238, 238)); m_zoom_out_btn->SetCornerRadius(0); - m_zoom_out_btn->SetMinSize(wxSize(FromDIP(54), FromDIP(26))); + m_zoom_out_btn->SetMinSize(wxSize(FromDIP(56), FromDIP(28))); - m_canvas_btn_sizer->Add(m_zoom_out_btn, 0, wxEXPAND, 0); StateColor percent_bg(std::pair(wxColour(255, 255, 255), StateColor::Disabled), std::pair(wxColour(255, 255, 255), StateColor::Pressed), std::pair(wxColour(255, 255, 255), StateColor::Hovered), std::pair(wxColour(255, 255, 255), StateColor::Enabled), @@ -97,114 +96,134 @@ PartSkipDialog::PartSkipDialog(wxWindow* parent): DPIDialog(parent, wxID_ANY, _L m_percent_label = new Button(m_book_third_panel, _L("100 %")); m_percent_label->SetBackgroundColor(percent_bg); m_percent_label->SetBorderColor(wxColour(238, 238, 238)); - m_percent_label->SetMinSize(wxSize(FromDIP(54), FromDIP(26))); + m_percent_label->SetMinSize(wxSize(FromDIP(56), FromDIP(28))); m_percent_label->SetCornerRadius(0); - m_canvas_btn_sizer->Add( m_percent_label, 0, wxEXPAND, 0 ); - m_zoom_in_btn = new Button(m_book_third_panel, _L("")); + m_zoom_in_btn = new Button(m_book_third_panel, wxEmptyString); m_zoom_in_btn->SetIcon("canvas_zoom_in"); m_zoom_in_btn->SetToolTip(_L("Zoom In")); m_zoom_in_btn->SetBackgroundColor(zoom_bg); m_zoom_in_btn->SetBorderColor(wxColour(238, 238, 238)); - m_zoom_in_btn->SetMinSize(wxSize(FromDIP(54), FromDIP(26))); + m_zoom_in_btn->SetMinSize(wxSize(FromDIP(56), FromDIP(28))); m_zoom_in_btn->SetCornerRadius(0); - m_canvas_btn_sizer->Add(m_zoom_in_btn, 0, wxEXPAND, 0); - m_switch_drag_btn = new Button(m_book_third_panel, _L("")); + m_switch_drag_btn = new Button(m_book_third_panel, wxEmptyString); m_switch_drag_btn->SetIcon("canvas_drag"); m_switch_drag_btn->SetToolTip(_L("Drag")); m_switch_drag_btn->SetBackgroundColor(*wxWHITE); m_switch_drag_btn->SetBorderColor(wxColour(238, 238, 238)); - m_switch_drag_btn->SetMinSize(wxSize(FromDIP(54),FromDIP(26))); + m_switch_drag_btn->SetMinSize(wxSize(FromDIP(56),FromDIP(28))); m_switch_drag_btn->SetCornerRadius(0); - m_canvas_btn_sizer->Add( m_switch_drag_btn, 0, wxEXPAND, FromDIP(5) ); - m_canvas_sizer->Add(m_canvas_btn_sizer, 0, wxALIGN_CENTER_HORIZONTAL, FromDIP(10)); - - m_dlg_content_sizer->Add( m_canvas_sizer, 0, wxALL, FromDIP(10) ); + m_canvas_btn_sizer->Add(m_zoom_out_btn, 0, wxEXPAND | wxLEFT, FromDIP(105)); + m_canvas_btn_sizer->Add( m_percent_label, 0, wxEXPAND, 0 ); + m_canvas_btn_sizer->Add(m_zoom_in_btn, 0, wxEXPAND, 0); + m_canvas_btn_sizer->Add( m_switch_drag_btn, 0, wxEXPAND | wxRIGHT, FromDIP(88) ); m_list_sizer = new wxBoxSizer( wxVERTICAL ); m_list_sizer->SetMinSize( wxSize( FromDIP(267),FromDIP(422) ) ); auto all_checkbox_sizer = new wxBoxSizer(wxHORIZONTAL); - m_all_checkbox = new CheckBox(m_book_third_panel, wxID_ANY); + m_all_checkbox = new CheckBox(m_book_third_panel, wxID_ANY); m_all_checkbox->SetValue(false); + m_all_checkbox->SetMinSize(wxSize(FromDIP(18), FromDIP(18))); m_all_checkbox->SetBackgroundColour(*wxWHITE); m_all_label = new Label(m_book_third_panel, _L("Select All")); m_all_label->Wrap(-1); + m_all_label->SetMinSize(wxSize(-1, FromDIP(18))); m_all_label->SetBackgroundColour(*wxWHITE); - + m_all_label->SetMinSize(wxSize(267, -1)); m_all_label->SetMaxSize(wxSize(267, -1)); - all_checkbox_sizer->Add(m_all_checkbox, 0, wxALL | wxALIGN_CENTER_VERTICAL, 0); - all_checkbox_sizer->Add(m_all_label, 0, wxALL | wxALIGN_CENTER_VERTICAL, 0); - - m_list_sizer->Add(all_checkbox_sizer, 0, wxALL, FromDIP(5)); + all_checkbox_sizer->Add(m_all_checkbox, 0, wxALIGN_CENTER_VERTICAL, 0); + all_checkbox_sizer->Add(m_all_label, 0, wxLEFT| wxALIGN_CENTER_VERTICAL, FromDIP(8)); m_line = new wxPanel(m_book_third_panel, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(267), FromDIP(1)), wxTAB_TRAVERSAL); - m_line->SetBackgroundColour( wxColor(238, 238, 238) ); - - m_list_sizer->Add(m_line, 0, wxEXPAND, 0); + m_line->SetMinSize(wxSize(FromDIP(267), 1)); + m_line->SetMaxSize(wxSize(FromDIP(267), 1)); + m_line->SetBackgroundColour( wxColor(238, 238, 238) ); m_list_view = new wxScrolledWindow( m_book_third_panel, wxID_ANY, wxDefaultPosition, wxSize( 267,-1 ), wxHSCROLL|wxVSCROLL ); m_list_view->SetScrollRate( 5, 5 ); - m_list_view->SetMinSize( wxSize( FromDIP(267),FromDIP(410) ) ); - m_list_view->SetMaxSize(wxSize(FromDIP(267), FromDIP(410))); + m_list_view->SetMinSize( wxSize( FromDIP(267),FromDIP(378))); + m_list_view->SetMaxSize(wxSize(FromDIP(267), FromDIP(378))); m_list_view->SetBackgroundColour(*wxWHITE); m_scroll_sizer = new wxBoxSizer(wxVERTICAL); m_list_view->SetSizer(m_scroll_sizer); m_list_view->Layout(); - m_list_sizer->Add(m_list_view, 0, wxEXPAND, 0); - - m_dlg_content_sizer->Add( m_list_sizer, 0, wxALL, FromDIP(15) ); - - - m_dlg_sizer->Add( m_dlg_content_sizer, 0, wxEXPAND, FromDIP(5) ); - m_dlg_btn_sizer = new wxBoxSizer( wxHORIZONTAL ); - m_dlg_btn_sizer->SetMinSize(wxSize(FromDIP(720), FromDIP(54))); + m_dlg_btn_sizer->SetMinSize(wxSize(-1, FromDIP(54))); - m_cnt_label = new Label(m_book_third_panel, _L("cnt 1")); + m_cnt_label = new Label(m_book_third_panel, wxEmptyString); m_cnt_label->Wrap( -1 ); m_cnt_label->SetBackgroundColour(*wxWHITE); m_cnt_label->SetForegroundColour(wxColour(0, 174, 66)); - m_cnt_label->SetFont(Label::Head_15); - m_dlg_btn_sizer->Add(0, 0, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, FromDIP(5)); - m_dlg_btn_sizer->Add( m_cnt_label, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(0) ); + m_cnt_label->SetSize(wxSize(-1, FromDIP(20))); + m_cnt_label->SetMaxSize(wxSize(-1, FromDIP(20))); - m_tot_label = new Label(m_book_third_panel, _L("tot 15")); + m_tot_label = new Label(m_book_third_panel, wxEmptyString); m_tot_label->Wrap( -1 ); m_tot_label->SetBackgroundColour(*wxWHITE); - - m_dlg_btn_sizer->Add(m_tot_label, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(5)); - m_dlg_btn_sizer->Add(0, 0, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL, FromDIP(5)); + m_tot_label->SetMinSize(wxSize(FromDIP(200), FromDIP(20))); StateColor btn_bg_white(std::pair(wxColour(206, 206, 206), StateColor::Pressed), std::pair(wxColour(238, 238, 238), StateColor::Hovered), std::pair(*wxWHITE, StateColor::Normal)); - StateColor btn_bg_green(std::pair(wxColour(27, 136, 68), StateColor::Pressed), std::pair(wxColour(61, 203, 115), StateColor::Hovered), std::pair(wxColour(0, 177, 66), StateColor::Normal)); - - m_apply_btn = new Button(m_book_third_panel, wxString(_L("Skip"))); + + m_apply_btn = new Button(m_book_third_panel, _L("Skip")); m_apply_btn->SetBackgroundColor(btn_bg_green); m_apply_btn->SetTextColor(*wxWHITE); // m_apply_btn->SetBorderColor(wxColour(38, 46, 48)); m_apply_btn->SetFont(Label::Body_14); - m_apply_btn->SetSize(wxSize(-1, FromDIP(32))); + m_apply_btn->SetSize(wxSize(FromDIP(80), FromDIP(32))); m_apply_btn->SetMinSize(wxSize(FromDIP(80), FromDIP(32))); m_apply_btn->SetCornerRadius(FromDIP(16)); - m_dlg_btn_sizer->Add(m_apply_btn, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(15)); - m_dlg_sizer->Add( m_dlg_btn_sizer, 0, wxEXPAND, FromDIP(5) ); + + m_canvas_sizer->Add( m_canvas, 0, wxLEFT | wxTOP | wxEXPAND, FromDIP(17) ); + m_canvas_sizer->Add(m_canvas_btn_sizer, 0, wxTOP , FromDIP(8)); + m_list_sizer->Add(0, 0, 1, wxEXPAND, 0); + + m_list_sizer->Add(0, 0, 0, wxTOP , FromDIP(27)); + m_list_sizer->Add(all_checkbox_sizer, 0, wxLEFT , FromDIP(0)); + m_list_sizer->Add(m_line, 0, wxEXPAND | wxTOP | wxBOTTOM, FromDIP(12)); + m_list_sizer->Add(m_list_view, 0, wxEXPAND, 0); + m_list_sizer->Add(0, 0, 1, wxEXPAND, 0); + + m_dlg_content_sizer->Add( m_canvas_sizer, 0, wxEXPAND, FromDIP(0) ); + m_dlg_content_sizer->Add( 0, 0, 0, wxEXPAND | wxLEFT, FromDIP(22) ); + m_dlg_content_sizer->Add( m_list_sizer, 0, wxRIGHT | wxEXPAND, FromDIP(14) ); + + m_dlg_btn_sizer->Add(0, 0, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, FromDIP(21)); + m_dlg_btn_sizer->Add(m_cnt_label, 0, wxALIGN_CENTER_VERTICAL, FromDIP(0) ); + #ifdef __WXMSW__ + m_dlg_btn_sizer->Add(m_tot_label, 0, wxLEFT |wxTOP| wxALIGN_CENTER_VERTICAL, FromDIP(2)); + #else + m_dlg_btn_sizer->Add(m_tot_label, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 0); + #endif + m_dlg_btn_sizer->Add(0, 0, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL, FromDIP(0)); + m_dlg_btn_sizer->Add(m_apply_btn, 0, wxALIGN_CENTER_VERTICAL, FromDIP(0)); + m_dlg_btn_sizer->Add(0, 0, 0, wxLEFT | wxEXPAND| wxALIGN_CENTER_VERTICAL, FromDIP(24)); + + m_dlg_placeholder = new wxPanel(m_book_third_panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + m_dlg_placeholder->SetMinSize(wxSize(-1, FromDIP(15))); + m_dlg_placeholder->SetMaxSize(wxSize(-1, FromDIP(15))); + m_dlg_placeholder->SetBackgroundColour(*wxWHITE); + + m_dlg_sizer->Add( m_dlg_content_sizer, 1, wxEXPAND, FromDIP(0) ); + // m_dlg_sizer->Add( 0, 0, 1, wxEXPAND, FromDIP(0)); + m_dlg_sizer->Add( m_dlg_btn_sizer, 0, wxEXPAND, FromDIP(0)); + m_dlg_sizer->Add( m_dlg_placeholder, 0, 0, wxEXPAND, 0); m_book_third_panel->SetSizer( m_dlg_sizer ); m_book_third_panel->Layout(); m_dlg_sizer->Fit( m_book_third_panel ); - + // page 2 m_book_second_panel = new wxPanel( m_simplebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); m_book_second_panel->SetBackgroundColour(*wxWHITE); @@ -237,35 +256,34 @@ PartSkipDialog::PartSkipDialog(wxWindow* parent): DPIDialog(parent, wxID_ANY, _L // page 1 m_book_first_panel = new wxPanel( m_simplebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); m_book_first_sizer = new wxBoxSizer( wxVERTICAL ); - m_book_first_sizer->SetMinSize(wxSize(FromDIP(720), FromDIP(500))); + // m_book_first_sizer->SetMinSize(wxSize(FromDIP(720), FromDIP(500))); - // auto m_loading_sizer = new wxBoxSizer(wxHORIZONTAL); - // std::vector list{"ams_rfid_1", "ams_rfid_2", "ams_rfid_3", "ams_rfid_4"}; - // m_loading_icon = new AnimaIcon(m_book_first_panel, wxID_ANY, list, "refresh_printer", 100); + auto m_loading_sizer = new wxBoxSizer(wxHORIZONTAL); + std::vector list{"ams_rfid_1", "ams_rfid_2", "ams_rfid_3", "ams_rfid_4"}; + m_loading_icon = new AnimaIcon(m_book_first_panel, wxID_ANY, list, "refresh_printer", 100); + m_loading_icon->SetMinSize(wxSize(FromDIP(25), FromDIP(25))); - m_loading_label = new Label( m_book_first_panel, wxString(_L("Skipping Objects Information Loading ..."))); + m_loading_label = new Label( m_book_first_panel, _L("Loading ...")); m_loading_label->Wrap( -1 ); m_loading_label->SetBackgroundColour(*wxWHITE); - // m_loading_sizer->Add(0, 0, 1, wxEXPAND, 0); - // m_loading_sizer->Add(m_loading_icon, 0, wxALL, FromDIP(5)); - // m_loading_sizer->Add(m_loading_label, 0, wxALL, FromDIP(5)); - // m_loading_sizer->Add(0, 0, 1, wxEXPAND, 0); + m_loading_sizer->Add(m_loading_icon, 0, wxALL, FromDIP(5)); + m_loading_sizer->Add(m_loading_label, 0, wxALL, FromDIP(5)); m_book_first_sizer->Add(0, 0, 1, wxEXPAND, 0); - m_book_first_sizer->Add( m_loading_label, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 0); + m_book_first_sizer->Add( m_loading_sizer, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 0); m_book_first_sizer->Add(0, 0, 1, wxEXPAND, 0); m_book_first_panel->SetSizer( m_book_first_sizer ); m_book_first_panel->Layout(); m_book_first_sizer->Fit( m_book_first_panel ); - + m_simplebook->AddPage( m_book_first_panel, _("loading page"), false ); m_simplebook->AddPage( m_book_second_panel, _("retry page"), false ); m_simplebook->AddPage( m_book_third_panel, _("dialog page"), false ); m_sizer->Add( m_simplebook, 1, wxEXPAND | wxALL, 5 ); - + SetSizer( m_sizer ); m_zoom_in_btn->Bind(wxEVT_BUTTON, &PartSkipDialog::OnZoomIn, this); m_zoom_out_btn->Bind(wxEVT_BUTTON, &PartSkipDialog::OnZoomOut, this); @@ -275,7 +293,7 @@ PartSkipDialog::PartSkipDialog(wxWindow* parent): DPIDialog(parent, wxID_ANY, _L m_apply_btn->Bind(wxEVT_BUTTON, &PartSkipDialog::OnApplyDialog, this); m_all_checkbox->Bind(wxEVT_TOGGLEBUTTON, &PartSkipDialog::OnAllCheckbox, this); - + Fit(); Layout(); CentreOnParent(); @@ -283,7 +301,74 @@ PartSkipDialog::PartSkipDialog(wxWindow* parent): DPIDialog(parent, wxID_ANY, _L PartSkipDialog::~PartSkipDialog() {} -void PartSkipDialog::on_dpi_changed(const wxRect& suggested_rect) { Fit(); } +void PartSkipDialog::on_dpi_changed(const wxRect& suggested_rect) { + m_canvas->LoadPickImage(m_local_paths[0]); + + m_loading_icon->SetMinSize(wxSize(FromDIP(25), FromDIP(25))); + + m_percent_label->SetMinSize(wxSize(FromDIP(56), FromDIP(28))); + m_percent_label->SetMaxSize(wxSize(FromDIP(56), FromDIP(28))); + m_zoom_out_btn->SetMinSize(wxSize(FromDIP(56), FromDIP(28))); + m_zoom_out_btn->SetMaxSize(wxSize(FromDIP(56), FromDIP(28))); + m_zoom_in_btn->SetMinSize(wxSize(FromDIP(56), FromDIP(28))); + m_zoom_in_btn->SetMaxSize(wxSize(FromDIP(56), FromDIP(28))); + m_switch_drag_btn->SetMinSize(wxSize(FromDIP(56), FromDIP(28))); + m_switch_drag_btn->SetMaxSize(wxSize(FromDIP(56), FromDIP(28))); + m_percent_label->Rescale(); + m_zoom_out_btn->Rescale(); + m_zoom_in_btn->Rescale(); + m_switch_drag_btn->Rescale(); + + m_cnt_label->SetMaxSize(wxSize(-1, FromDIP(20))); + + m_tot_label->SetMinSize(wxSize(FromDIP(200), FromDIP(20))); + m_tot_label->SetLabel(m_tot_label->GetLabel()); + m_tot_label->Refresh(); + + m_line_top->SetMinSize(wxSize(-1, 1)); + m_line->SetMinSize(wxSize(FromDIP(267), 1)); + m_line->SetMaxSize(wxSize(FromDIP(267), 1)); + + + m_apply_btn->SetMinSize(wxSize(FromDIP(80), FromDIP(32))); + m_apply_btn->SetCornerRadius(FromDIP(16)); + m_apply_btn->Rescale(); + + m_dlg_placeholder->SetMinSize(wxSize(-1, FromDIP(15))); + m_dlg_placeholder->SetMaxSize(wxSize(-1, FromDIP(15))); + + // m_second_retry_btn->SetSize(wxSize(-1, FromDIP(32))); + m_second_retry_btn->SetMinSize(wxSize(FromDIP(80), FromDIP(32))); + m_second_retry_btn->SetCornerRadius(FromDIP(16)); + m_second_retry_btn->Rescale(); + + + m_all_checkbox->SetMinSize(wxSize(FromDIP(18), FromDIP(18))); + m_all_checkbox->Rescale(); + + for (auto it = m_scroll_sizer->GetChildren().begin(); it != m_scroll_sizer->GetChildren().end(); ++it) { + wxSizerItem *item = *it; + if (item && item->IsSizer()) { + wxSizer* sizer = item->GetSizer(); + auto check_item = sizer->GetItem((size_t)0); + if (check_item && check_item->IsWindow()) + { + wxWindow *window = check_item->GetWindow(); + CheckBox *checkbox = dynamic_cast(window); + checkbox->SetMinSize(wxSize(FromDIP(18), FromDIP(18))); + checkbox->Rescale(); + } + } + } + m_canvas_btn_sizer->SetMinSize(wxSize(-1, FromDIP(28))); + m_canvas_btn_sizer->Layout(); + m_canvas_sizer->Layout(); + m_dlg_content_sizer->Layout(); + m_dlg_sizer->Layout(); + + Fit(); + Layout(); +} std::string PartSkipDialog::create_tmp_path(){ @@ -307,10 +392,10 @@ std::string PartSkipDialog::create_tmp_path(){ } -bool PartSkipDialog::is_local_file_existed(const std::vector &local_paths) -{ - for (auto path : local_paths) { - if (!std::filesystem::exists(path)) { +bool PartSkipDialog::is_local_file_existed(const std::vector &local_paths) +{ + for (auto path : local_paths) { + if (!std::filesystem::exists(path)) { return false; } } @@ -323,7 +408,7 @@ void PartSkipDialog::DownloadPartsFile() m_local_paths.clear(); m_target_paths.clear(); - + int plate_idx = m_obj ? m_obj->m_plate_index : 1; m_local_paths.push_back(m_tmp_path + "Metadata/pick_" + std::to_string(plate_idx) + ".png"); @@ -333,17 +418,22 @@ void PartSkipDialog::DownloadPartsFile() m_target_paths.push_back("Metadata/pick_" + std::to_string(plate_idx) + ".png"); m_target_paths.push_back("Metadata/model_settings.config"); m_target_paths.push_back("Metadata/slice_info.config"); - + if (!is_local_file_existed(m_local_paths)) { - m_file_sys = boost::make_shared(); - m_file_sys->Attached(); - m_file_sys->Bind(EVT_STATUS_CHANGED, &PartSkipDialog::OnFileSystemEvent, this); - m_file_sys->Bind(EVT_RAMDOWNLOAD, &PartSkipDialog::OnFileSystemResult, this); - m_file_sys->Start(); + if(!m_file_sys){ + m_file_sys = boost::make_shared(); + m_file_sys->Attached(); + m_file_sys->Bind(EVT_STATUS_CHANGED, &PartSkipDialog::OnFileSystemEvent, this); + m_file_sys->Bind(EVT_RAMDOWNLOAD, &PartSkipDialog::OnFileSystemResult, this); + m_file_sys->Start(); + }else{ + m_file_sys->Retry(); + } } else { m_file_sys->SendExistedFile(); } } +// actor void PartSkipDialog::fetchUrl(boost::weak_ptr wfs) { boost::shared_ptr fs(wfs.lock()); @@ -358,13 +448,18 @@ void PartSkipDialog::fetchUrl(boost::weak_ptr wfs) } std::string dev_ver = obj->get_ota_version(); std::string dev_id = obj->dev_id; - int remote_proto = obj->get_file_remote(); + // int remote_proto = obj->get_file_remote(); NetworkAgent *agent = wxGetApp().getAgent(); std::string agent_version = agent ? agent->get_version() : ""; + auto url_state = m_url_state; + if(obj->is_lan_mode_printer()){ + url_state = URL_TCP; + } + if (agent) { - switch (m_url_state) { + switch (url_state) { case URL_TCP: { std::string devIP = obj->dev_ip; std::string accessCode = obj->get_access_code(); @@ -379,10 +474,10 @@ void PartSkipDialog::fetchUrl(boost::weak_ptr wfs) } }); break; - } + } case URL_TUTK: { std::string protocols[] = {"", "\"tutk\"", "\"agora\"", "\"tutk\",\"agora\""}; - agent->get_camera_url(obj->dev_id + "|" + dev_ver + "|" + protocols[1], [this, wfs, m = dev_id, v = agent->get_version(), dv = dev_ver](std::string url) { + agent->get_camera_url(obj->dev_id + "|" + dev_ver + "|" + protocols[3], [this, wfs, m = dev_id, v = agent->get_version(), dv = dev_ver](std::string url) { if (boost::algorithm::starts_with(url, "bambu:///")) { url += "&device=" + m; url += "&net_ver=" + v; @@ -391,8 +486,6 @@ void PartSkipDialog::fetchUrl(boost::weak_ptr wfs) url += "&cli_id=" + wxGetApp().app_config->get("slicer_uuid"); url += "&cli_ver=" + std::string(SLIC3R_VERSION); } - BOOST_LOG_TRIVIAL(info) << "SendToPrinter::fetchUrl: camera_url: " << hide_passwd(url, {"?uid=", "authkey=", "passwd="}); - std::cout << "SendToPrinter::fetchUrl: camera_url: " << hide_passwd(url, {"?uid=", "authkey=", "passwd="}); CallAfter([=] { boost::shared_ptr fs(wfs.lock()); if (!fs) return; @@ -402,14 +495,14 @@ void PartSkipDialog::fetchUrl(boost::weak_ptr wfs) fs->SetUrl("3"); } }); - }); + }); break; } default: break; } } } - +// controller void PartSkipDialog::OnFileSystemEvent(wxCommandEvent &e) { e.Skip(); @@ -419,20 +512,26 @@ void PartSkipDialog::OnFileSystemEvent(wxCommandEvent &e) wxString msg; int status = e.GetInt(); - int extra = e.GetExtraLong(); + // int extra = e.GetExtraLong(); switch (status) { - case PrinterFileSystem::Initializing: - case PrinterFileSystem::Connecting: break; - case PrinterFileSystem::ListSyncing: { - m_file_sys->GetPickImages(m_local_paths, m_target_paths); - break; - } - case PrinterFileSystem::Failed: { - m_file_sys->SendConnectFail(); - break; - } - case PrinterFileSystem::Reconnecting: break; + case PrinterFileSystem::Initializing: + case PrinterFileSystem::Connecting: break; + case PrinterFileSystem::ListSyncing: { + m_file_sys->GetPickImages(m_local_paths, m_target_paths); + break; + } + case PrinterFileSystem::Failed: { + m_file_sys->Stop(); + if( m_url_state == URL_TCP){ + m_url_state = URL_TUTK; + m_file_sys->Retry(); + }else{ + m_file_sys->SendConnectFail(); + } + break; + } + case PrinterFileSystem::Reconnecting: break; } if (e.GetInt() == PrinterFileSystem::Initializing) { CallAfter([=] { @@ -442,33 +541,26 @@ void PartSkipDialog::OnFileSystemEvent(wxCommandEvent &e) }); } } + +// reseter: [TCP -> TUTK(TCP)] -> [TCP -> TUTK(TCP)] void PartSkipDialog::OnFileSystemResult(wxCommandEvent &event){ int result = event.GetInt(); + m_loading_icon->Stop(); if (result == 0) { InitDialogUI(); SetSimplebookPage(2); + m_file_sys->Stop(); }else{ - switch (m_url_state) { - case URL_TCP: { - m_url_state = URL_TUTK; - break; - } - case URL_TUTK: { - m_url_state = URL_TCP; - break; - } - } + m_url_state = URL_TCP; SetSimplebookPage(1); + m_file_sys->Stop(); } - // wxMilliSleep(1000); - // m_loading_icon->Stop(); - Refresh(); } void PartSkipDialog::InitSchedule(MachineObject *obj){ m_obj = obj; SetSimplebookPage(0); - // m_loading_icon->Play(); + m_loading_icon->Play(); DownloadPartsFile(); } @@ -485,9 +577,9 @@ bool PartSkipDialog::is_drag_mode(){ PartsInfo PartSkipDialog::GetPartsInfo(){ PartsInfo parts_info; - for (auto [part_id, part_state] : this->m_parts_state) - { - parts_info.push_back(std::pair(part_id, part_state)); + for (auto [part_id, part_state] : this->m_parts_state) + { + parts_info.push_back(std::pair(part_id, part_state)); } return parts_info; } @@ -497,7 +589,7 @@ void PartSkipDialog::OnZoomIn(wxCommandEvent &event){ UpdateZoomPercent(); } -void PartSkipDialog::OnZoomOut(wxCommandEvent &event){ +void PartSkipDialog::OnZoomOut(wxCommandEvent &event){ m_canvas->ZoomOut(20); UpdateZoomPercent(); } @@ -512,7 +604,7 @@ void PartSkipDialog::OnSwitchDrag(wxCommandEvent& event){ m_switch_drag_btn->SetBackgroundColor(wxColour(0, 174, 66)); m_switch_drag_btn->SetIcon("canvas_drag_active"); } - m_canvas->SwitchDrag(m_is_drag); + m_canvas->SwitchDrag(m_is_drag); } void PartSkipDialog::OnZoomPercent(wxCommandEvent &event) { @@ -549,14 +641,14 @@ void PartSkipDialog::UpdatePartsStateFromCanvas(wxCommandEvent &event) { UpdateDialogUI(); } -void PartSkipDialog::UpdateZoomPercent() { - m_percent_label->SetLabel(wxString::Format(_L("%d%%"), m_zoom_percent)); +void PartSkipDialog::UpdateZoomPercent() { + m_percent_label->SetLabel(wxString::Format(_L("%d%%"), m_zoom_percent)); } void PartSkipDialog::UpdateCountLabel() { int check_cnt = 0; int tot_cnt = 0; - for (auto [part_id, part_state] : m_parts_state) { + for (auto [part_id, part_state] : m_parts_state) { if (part_state == PartState::psChecked) check_cnt++; if (part_state != PartState::psSkipped) tot_cnt++; } @@ -584,7 +676,7 @@ void PartSkipDialog::InitDialogUI() { m_parts_name.clear(); string pick_img = m_local_paths[0]; - string model_settings = m_local_paths[1]; + string slice_info = m_local_paths[2]; m_switch_drag_btn->SetIcon("canvas_drag"); m_switch_drag_btn->SetBackgroundColor(*wxWHITE); @@ -593,7 +685,7 @@ void PartSkipDialog::InitDialogUI() { m_canvas->SetZoomPercent(100); m_canvas->SetOffset(wxPoint(0, 0)); m_canvas->LoadPickImage(pick_img); - ModelSettingHelper helper(model_settings); + ModelSettingHelper helper(slice_info); if (helper.Parse()) { auto parse_result = helper.GetResults(); @@ -607,13 +699,13 @@ void PartSkipDialog::InitDialogUI() { m_parts_state[part_id] = PartState::psSkipped; } } - + for(const auto& [part_id, part_state] : m_parts_state) { auto line_sizer = new wxBoxSizer(wxHORIZONTAL); auto checkbox = new CheckBox(m_list_view); - auto label = new Label(m_list_view, _L("file 0")); - - checkbox->Bind(wxEVT_TOGGLEBUTTON, [this, part_id=part_id](wxCommandEvent& event) { + auto label = new Label(m_list_view, wxEmptyString); + + checkbox->Bind(wxEVT_TOGGLEBUTTON, [this, part_id=part_id](wxCommandEvent& event) { m_parts_state[part_id] = event.IsChecked() ? PartState::psChecked : PartState::psUnCheck; if(!event.IsChecked()){ m_all_checkbox->SetValue(false); @@ -624,7 +716,7 @@ void PartSkipDialog::InitDialogUI() { UpdateCountLabel(); event.Skip(); }, checkbox->GetId()); - + if (part_state == PartState::psChecked) { checkbox->SetValue(true); checkbox->Enable(true); @@ -639,15 +731,18 @@ void PartSkipDialog::InitDialogUI() { label->SetBackgroundColour(*wxWHITE); label->SetForegroundColour(wxColor(107,107,107)); label->Wrap(-1); + label->SetMinSize(wxSize(-1, FromDIP(18))); checkbox->SetBackgroundColour(*wxWHITE); - - line_sizer->Add(checkbox, 0, wxALL | wxALIGN_CENTER_VERTICAL, 0); - line_sizer->Add(label, 1, wxALL | wxALIGN_CENTER_VERTICAL, 0); - m_scroll_sizer->Add(line_sizer, 0, wxALL, FromDIP(5)); + checkbox->SetMinSize(wxSize(FromDIP(18), FromDIP(18))); + checkbox->SetMaxSize(wxSize(FromDIP(18), FromDIP(18))); + + line_sizer->Add(checkbox, 0, wxALIGN_CENTER_VERTICAL, 0); + line_sizer->Add(label, 1, wxLEFT | wxALIGN_CENTER_VERTICAL, FromDIP(8)); + m_scroll_sizer->Add(line_sizer, 0, wxBOTTOM | wxEXPAND, FromDIP(12)); } m_canvas->UpdatePartsInfo(GetPartsInfo()); } - + m_scroll_sizer->Layout(); UpdateCountLabel(); Refresh(); @@ -674,26 +769,25 @@ void PartSkipDialog::UpdateDialogUI(){ return; } - for (auto it = m_parts_state.begin(); it != m_parts_state.end(); ++it) { - int idx = std::distance(m_parts_state.begin(), it); - auto part_id = it->first; + for (auto it = m_parts_state.begin(); it != m_parts_state.end(); ++it) { + int idx = std::distance(m_parts_state.begin(), it); auto part_state = it->second; wxSizerItem *item = m_scroll_sizer->GetItem(idx); if (item && item->IsSizer()) { wxSizer* sizer = item->GetSizer(); auto check_item = sizer->GetItem((size_t)0); - - if (check_item && check_item->IsWindow()) - { - wxWindow *window = check_item->GetWindow(); + + if (check_item && check_item->IsWindow()) + { + wxWindow *window = check_item->GetWindow(); CheckBox *checkbox = dynamic_cast(window); - if (part_state == PartState::psChecked) { + if (part_state == PartState::psChecked) { checkbox->SetValue(true); } else if (part_state == PartState::psUnCheck) { - checkbox->SetValue(false); + checkbox->SetValue(false); } else { - checkbox->SetValue(true); + checkbox->SetValue(true); checkbox->Enable(false); } } @@ -721,13 +815,13 @@ void PartSkipDialog::OnAllCheckbox(wxCommandEvent &event) if (m_all_checkbox->GetValue()) { for (auto& [part_id, part_state] : m_parts_state) { if (part_state == PartState::psUnCheck) - part_state = PartState::psChecked; + part_state = PartState::psChecked; } } else{ for (auto& [part_id, part_state] : m_parts_state) { if (part_state == PartState::psChecked) - part_state = PartState::psUnCheck; + part_state = PartState::psUnCheck; } } m_canvas->UpdatePartsInfo(GetPartsInfo()); @@ -750,15 +844,15 @@ void PartSkipDialog::OnApplyDialog(wxCommandEvent &event) if(confirm_dialog.ShowModal() == wxID_OK){ if (m_obj) { - BOOST_LOG_TRIVIAL(info) << "monitor: skipping "<< m_partskip_ids.size() <<" parts dev_id =" << m_obj->dev_id; - + BOOST_LOG_TRIVIAL(info) << "monitor: skipping "<< m_partskip_ids.size() <<" objects."; + bool all_skipped = true; - for (auto [part_id, part_state] : m_parts_state) { + for (auto [part_id, part_state] : m_parts_state) { if (part_state == PartState::psUnCheck) all_skipped = false; } - if (all_skipped) { - m_obj->command_task_abort(); + if (all_skipped) { + m_obj->command_task_abort(); } else { m_obj->command_task_partskip(m_partskip_ids); } @@ -775,27 +869,33 @@ PartSkipConfirmDialog::PartSkipConfirmDialog(wxWindow *parent) : DPIDialog(paren std::string icon_path = (boost::format("%1%/images/BambuStudioTitle.ico") % Slic3r::resources_dir()).str(); SetIcon(wxIcon(Slic3r::encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO)); SetBackgroundColour(*wxWHITE); + SetMinSize(wxSize(FromDIP(480), FromDIP(215))); SetSizeHints(wxDefaultSize, wxDefaultSize); wxBoxSizer* m_sizer; m_sizer = new wxBoxSizer( wxVERTICAL ); auto m_line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL); + m_line_top->SetMinSize(wxSize(FromDIP(480), 1)); + m_line_top->SetMaxSize(wxSize(FromDIP(480), 1)); m_line_top->SetBackgroundColour(wxColour(0xA6, 0xa9, 0xAA)); m_sizer->Add(m_line_top, 0, wxEXPAND, 0); m_sizer->Add(0, 0, 0, wxALL, FromDIP(15)); - m_msg_label = new Label( this, wxString(_L("Skipping objects."))); + m_msg_label = new Label( this, _L("Skipping objects.")); m_msg_label->Wrap( -1 ); m_msg_label->SetBackgroundColour(*wxWHITE); - m_sizer->Add(m_msg_label, 0, wxLEFT, FromDIP(15)); - auto m_tip_label = new Label(this, wxString(_("This action cannot be undone. Continue?"))); + + auto m_tip_label = new Label(this, _L("This action cannot be undone. Continue?")); m_tip_label->Wrap(-1); m_tip_label->SetBackgroundColour(*wxWHITE); m_tip_label->SetForegroundColour(wxColor(92,92,92)); - m_sizer->Add(m_tip_label, 0, wxLEFT | wxTOP, FromDIP(15)); + + m_sizer->Add(m_msg_label, 0, wxLEFT, FromDIP(29)); + m_sizer->Add(0, 0, 0, wxTOP, FromDIP(9)); + m_sizer->Add(m_tip_label, 0, wxLEFT, FromDIP(29)); wxBoxSizer* m_button_sizer; m_button_sizer = new wxBoxSizer(wxHORIZONTAL); @@ -809,7 +909,7 @@ PartSkipConfirmDialog::PartSkipConfirmDialog(wxWindow *parent) : DPIDialog(paren StateColor btn_bg_green(std::pair(wxColour(27, 136, 68), StateColor::Pressed), std::pair(wxColour(61, 203, 115), StateColor::Hovered), std::pair(wxColour(0, 177, 66), StateColor::Normal)); - m_apply_button = new Button(this, wxString(_L("Continue"))); + m_apply_button = new Button(this, _L("Continue")); m_apply_button->SetBackgroundColor(btn_bg_green); m_apply_button->SetTextColor(*wxWHITE); // m_apply_button->SetBorderColor(wxColour(38, 46, 48)); @@ -822,7 +922,7 @@ PartSkipConfirmDialog::PartSkipConfirmDialog(wxWindow *parent) : DPIDialog(paren e.Skip(); }); - m_button_sizer->Add(m_apply_button, 0, wxALL, FromDIP(15)); + m_button_sizer->Add(m_apply_button, 0, wxRIGHT | wxBOTTOM, FromDIP(24)); m_sizer->Add(m_button_sizer, 1, wxEXPAND, FromDIP(5)); m_sizer->Fit( this ); @@ -847,7 +947,13 @@ bool PartSkipConfirmDialog::Show(bool show) return DPIDialog::Show(show); } -void PartSkipConfirmDialog::on_dpi_changed(const wxRect& suggested_rect) { Fit(); } +void PartSkipConfirmDialog::on_dpi_changed(const wxRect& suggested_rect) { + m_apply_button->SetMinSize(wxSize(FromDIP(80), FromDIP(32))); + m_apply_button->SetCornerRadius(FromDIP(16)); + m_apply_button->Rescale(); + Layout(); + Fit(); +} Button* PartSkipConfirmDialog::GetConfirmButton() @@ -860,4 +966,5 @@ void PartSkipConfirmDialog::SetMsgLabel(wxString msg){ } -}} // namespace Slic3r::GUI +} +} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/PartSkipDialog.hpp b/src/slic3r/GUI/PartSkipDialog.hpp index cd8fca3be7..1428f15ee8 100644 --- a/src/slic3r/GUI/PartSkipDialog.hpp +++ b/src/slic3r/GUI/PartSkipDialog.hpp @@ -29,8 +29,8 @@ namespace Slic3r { namespace GUI { class SkipPartCanvas; -enum URL_STATE { - URL_TCP, +enum URL_STATE { + URL_TCP, URL_TUTK, }; @@ -78,13 +78,15 @@ public: Button* m_switch_drag_btn; CheckBox* m_all_checkbox; Button* m_percent_label; - Label * m_all_label; + Label* m_all_label; wxPanel* m_line; + wxPanel* m_line_top; wxScrolledWindow* m_list_view; - + + wxPanel* m_dlg_placeholder; Label* m_cnt_label; Label* m_tot_label; - + Button* m_apply_btn; Label* m_loading_label; @@ -108,7 +110,7 @@ private: int m_zoom_percent{100}; bool m_is_drag{false}; bool m_print_lock{true}; - + std::map m_parts_state; std::map m_parts_name; std::vector m_partskip_ids; @@ -117,7 +119,7 @@ private: PartsInfo GetPartsInfo(); bool is_drag_mode(); - + boost::shared_ptr m_file_sys; bool m_file_sys_result{false}; std::string m_timestamp; @@ -127,19 +129,19 @@ private: std::string create_tmp_path(); bool is_local_file_existed(const std::vector &local_paths); - + void DownloadPartsFile(); void OnFileSystemEvent(wxCommandEvent &event); void OnFileSystemResult(wxCommandEvent &event); void fetchUrl(boost::weak_ptr wfs); - + void OnZoomIn(wxCommandEvent &event); void OnZoomOut(wxCommandEvent &event); - void OnSwitchDrag(wxCommandEvent &event); + void OnSwitchDrag(wxCommandEvent &event); void OnZoomPercent(wxCommandEvent &event); void UpdatePartsStateFromCanvas(wxCommandEvent &event); - + void UpdateZoomPercent(); void UpdateCountLabel(); void UpdateDialogUI(); diff --git a/src/slic3r/GUI/Printer/PrinterFileSystem.cpp b/src/slic3r/GUI/Printer/PrinterFileSystem.cpp index 4385cbfb52..a48310254d 100644 --- a/src/slic3r/GUI/Printer/PrinterFileSystem.cpp +++ b/src/slic3r/GUI/Printer/PrinterFileSystem.cpp @@ -263,7 +263,7 @@ void PrinterFileSystem::GetPickImage(int id, const std::string &local_path, cons { json j; - j["sequence_id"] = id; + j["sequence_id"] = id; j["version"] = 1; j["peer_host"] = "studio"; j["command"] = "get_project_file"; @@ -836,7 +836,7 @@ void PrinterFileSystem::DownloadNextFile() file.download.reset(), file.flags &= ~FF_DOWNLOAD; else // FAILED file.download.reset(); - if (&file_index.first == &m_file_list) + if (&file_index.first == &m_file_list) SendChangedEvent(EVT_DOWNLOAD, download->index, result ? download->error : file.local_path, result); } } diff --git a/src/slic3r/GUI/SkipPartCanvas.cpp b/src/slic3r/GUI/SkipPartCanvas.cpp index e9218aa714..09e6987a7a 100644 --- a/src/slic3r/GUI/SkipPartCanvas.cpp +++ b/src/slic3r/GUI/SkipPartCanvas.cpp @@ -33,8 +33,11 @@ SkipPartCanvas::SkipPartCanvas(wxWindow *parent, const wxGLAttributes& dispAttrs void SkipPartCanvas::LoadPickImage(const std::string & path) { parts_state_.clear(); + pick_parts_.clear(); + parts_triangles_.clear(); int preffered_w{FromDIP(400)}, preffered_h{FromDIP(400)}; cv::Mat src_image = cv::imread(path, cv::IMREAD_UNCHANGED); + cv::cvtColor(src_image, src_image, cv::COLOR_BGRA2BGR); // remove alpha float zoom_x{static_cast(preffered_w) / src_image.cols}; float zoom_y{static_cast(preffered_h) / src_image.rows}; float image_scale{0}; @@ -42,12 +45,12 @@ void SkipPartCanvas::LoadPickImage(const std::string & path) image_scale = zoom_x; else image_scale = zoom_y; - cv::resize(src_image, pick_image_, cv::Size(), image_scale, image_scale); + cv::resize(src_image, pick_image_, cv::Size(), image_scale, image_scale, cv::INTER_NEAREST); std::vector channels; - cv::split(pick_image_, channels); // channels[3] 是 Alpha - cv::Mat alpha = channels[3]; - cv::Mat mask; - cv::threshold(alpha, mask, 0, 255, cv::THRESH_BINARY); + cv::Mat gray; // convert to gray + cv::cvtColor(pick_image_, gray, cv::COLOR_BGR2GRAY); + cv::Mat mask; // convery to binary + cv::threshold(gray, mask, 0, 255, cv::THRESH_BINARY); std::vector> pick_counters; cv::findContours(mask, pick_counters, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_TC89_KCOS); std::vector> polygon; @@ -328,7 +331,7 @@ uint32_t SkipPartCanvas::GetIdAtImagePt(const wxPoint& image_pt) const if (image_pt.x >= 0 && image_pt.x < pick_image_.cols && image_pt.y >= 0 && image_pt.y < pick_image_.rows) { // at(row, col)=>at(y, x) - cv::Vec4b bgr = pick_image_.at(image_pt.y, image_pt.x); + cv::Vec3b bgr = pick_image_.at(image_pt.y, image_pt.x); SkipIdHelper helper{bgr[2], bgr[1], bgr[0]}; helper.reverse(); return helper.value; @@ -392,7 +395,7 @@ void SkipPartCanvas::ProcessHover(const wxPoint& mouse_pt) int new_hover_id { -1 }; auto part_state = parts_state_.find(id_at_mouse); if (part_state != parts_state_.end() && part_state->second == psUnCheck) { - new_hover_id = id_at_mouse; + new_hover_id = id_at_mouse; }; if (new_hover_id != this->hover_id_) { this->hover_id_ = new_hover_id; @@ -457,9 +460,9 @@ void SkipPartCanvas::OnMouseLeftUp(wxMouseEvent& event) left_down_ = false; if (fixed_draging_) EndDrag(); - else { + else { Refresh(); - } + } } void SkipPartCanvas::OnMouseRightDown(wxMouseEvent& event) @@ -556,44 +559,19 @@ bool ModelSettingHelper::Parse() return true; } -std::vector ModelSettingHelper::GetResults() { return context_.objects; } - +std::vector ModelSettingHelper::GetResults() { + return context_.objects; +} void XMLCALL ModelSettingHelper::StartElementHandler(void *userData, const XML_Char *name, const XML_Char **atts) { ModelSettingHelper* self = static_cast(userData); if (strcmp(name, "object") == 0) { for (int i = 0; atts[i]; i += 2) { - if (strcmp(atts[i], "id") == 0) { - self->context_.current_object_id = atoi(atts[i+1]); - } - } - } else if ((self->context_.current_object_id != -1) - && (strcmp(name, "metadata") == 0) - && (self->context_.node_name.top() == "object")) { - const XML_Char *key{nullptr}, *value{nullptr}; - for (int i = 0; atts[i]; i += 2) { - if (strcmp(atts[i], "key") == 0) key = atts[i+1]; - if (strcmp(atts[i], "value") == 0) value = atts[i+1]; - } - if (key && value && (strcmp(key, "name") == 0)) { - self->context_.temp_object.id = self->context_.current_object_id; - self->context_.temp_object.name = std::string(value); - } - } else if ((strcmp(name, "metadata") == 0) - && (self->context_.node_name.top() == "model_instance")) { - const XML_Char *key{nullptr}, *value{nullptr}; - for (int i = 0; atts[i]; i += 2) { - if (strcmp(atts[i], "key") == 0) key = atts[i+1]; - if (strcmp(atts[i], "value") == 0) value = atts[i+1]; - } - if (key && value && (strcmp(key, "object_id") == 0)) { - self->context_.current_instance_object_id = atoi(value); - } else if (key && value && (strcmp(key, "identify_id") == 0)) { - self->context_.current_instance_identify_id = atoi(value); + if (strcmp(atts[i], "identify_id") == 0) { self->context_.temp_object.identify_id = atoi(atts[i + 1]); } + if (strcmp(atts[i], "name") == 0) { self->context_.temp_object.name = std::string(atts[i + 1]); } } } - self->context_.node_name.push(std::string(name)); } void XMLCALL ModelSettingHelper::EndElementHandler(void *userData, const XML_Char *name) @@ -601,23 +579,7 @@ void XMLCALL ModelSettingHelper::EndElementHandler(void *userData, const XML_Cha ModelSettingHelper* self = static_cast(userData); if (strcmp(name, "object") == 0) { self->context_.objects.push_back(self->context_.temp_object); - self->context_.current_object_id = -1; } - else if ((strcmp(name, "model_instance") == 0) - && (self->context_.current_instance_identify_id != -1) - && (self->context_.current_instance_object_id != -1)) { - for (auto& obj : self->context_.objects) { - if (obj.id == self->context_.current_instance_object_id) { - obj.identify_id = self->context_.current_instance_identify_id; - break; - } - } - self->context_.current_instance_identify_id = -1; - self->context_.current_instance_object_id = -1; - } else if (strcmp(name, "object") == 0) { - self->context_.current_object_id = -1; - } - self->context_.node_name.pop(); } void ModelSettingHelper::DataHandler(const XML_Char *s, int len) diff --git a/src/slic3r/GUI/SkipPartCanvas.hpp b/src/slic3r/GUI/SkipPartCanvas.hpp index b4a6ad3d2c..6f02510872 100644 --- a/src/slic3r/GUI/SkipPartCanvas.hpp +++ b/src/slic3r/GUI/SkipPartCanvas.hpp @@ -23,7 +23,6 @@ using FloatPoint = std::array; struct ObjectInfo { - int id{-1}; std::string name{""}; int identify_id{-1}; PartState state{psUnCheck}; @@ -107,7 +106,7 @@ private: inline wxPoint ViewPtToImagePt(const wxPoint& view_pt) const; uint32_t GetIdAtImagePt(const wxPoint& image_pt) const; inline uint32_t GetIdAtViewPt(const wxPoint& view_pt) const; - + void ProcessHover(const wxPoint& mouse_pt); void AutoSetCursor(); void StartDrag(const wxPoint& mouse_pt); @@ -136,16 +135,11 @@ class ModelSettingHelper : public _BBS_3MF_Base { struct ParseContext{ std::vector objects; ObjectInfo temp_object; - - std::stack node_name; - int current_object_id{-1}; - int current_instance_identify_id{-1}; - int current_instance_object_id{-1}; }; public: ModelSettingHelper(const std::string& path); - + bool Parse(); std::vector GetResults(); diff --git a/src/slic3r/GUI/StatusPanel.cpp b/src/slic3r/GUI/StatusPanel.cpp index 25138c0168..7763563b5c 100644 --- a/src/slic3r/GUI/StatusPanel.cpp +++ b/src/slic3r/GUI/StatusPanel.cpp @@ -233,12 +233,6 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) bSizer_task_name->Add(task_name_panel, 0, wxEXPAND, FromDIP(5)); - - /* wxFlexGridSizer *fgSizer_task = new wxFlexGridSizer(2, 2, 0, 0); - fgSizer_task->AddGrowableCol(0); - fgSizer_task->SetFlexibleDirection(wxVERTICAL); - fgSizer_task->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);*/ - m_printing_stage_value = new wxStaticText(parent, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT | wxST_ELLIPSIZE_END); m_printing_stage_value->Wrap(-1); m_printing_stage_value->SetMaxSize(wxSize(FromDIP(800),-1)); @@ -261,18 +255,12 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) m_staticText_profile_value->SetForegroundColour(0x6B6B6B); + auto progress_lr_panel = new wxPanel(parent, wxID_ANY); + progress_lr_panel->SetBackgroundColour(*wxWHITE); - auto m_panel_progress = new wxPanel(parent, wxID_ANY); - m_panel_progress->SetBackgroundColour(*wxWHITE); - auto m_sizer_progressbar = new wxBoxSizer(wxHORIZONTAL); - m_gauge_progress = new ProgressBar(m_panel_progress, wxID_ANY, 100, wxDefaultPosition, wxDefaultSize); + m_gauge_progress = new ProgressBar(progress_lr_panel, wxID_ANY, 100, wxDefaultPosition, wxDefaultSize); m_gauge_progress->SetValue(0); m_gauge_progress->SetHeight(PROGRESSBAR_HEIGHT); - m_gauge_progress->SetMaxSize(wxSize(FromDIP(600), -1)); - m_panel_progress->SetSizer(m_sizer_progressbar); - m_panel_progress->Layout(); - m_panel_progress->SetSize(wxSize(-1, FromDIP(24))); - m_panel_progress->SetMaxSize(wxSize(-1, FromDIP(24))); wxBoxSizer *bSizer_task_btn = new wxBoxSizer(wxHORIZONTAL); @@ -281,8 +269,9 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) StateColor white_bg(std::pair(wxColour(255, 255, 255), StateColor::Disabled), std::pair(wxColour(255, 255, 255), StateColor::Pressed), std::pair(wxColour(255, 255, 255), StateColor::Hovered), std::pair(wxColour(255, 255, 255), StateColor::Enabled), std::pair(wxColour(255, 255, 255), StateColor::Normal)); - m_button_partskip = new Button(m_panel_progress, ""); + m_button_partskip = new Button(progress_lr_panel, ""); m_button_partskip->Enable(false); + m_button_partskip->Hide(); m_button_partskip->SetBackgroundColor(white_bg); m_button_partskip->SetIcon("print_control_partskip_disable"); m_button_partskip->SetBorderColor(*wxWHITE); @@ -292,7 +281,7 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) m_button_partskip->Bind(wxEVT_ENTER_WINDOW, [this](auto &e) { m_button_partskip->SetIcon("print_control_partskip"); }); m_button_partskip->Bind(wxEVT_LEAVE_WINDOW, [this](auto &e) { m_button_partskip->SetIcon("print_control_partskip"); }); - m_button_pause_resume = new ScalableButton(m_panel_progress, wxID_ANY, "print_control_pause", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER,true); + m_button_pause_resume = new ScalableButton(progress_lr_panel, wxID_ANY, "print_control_pause", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER,true); m_button_pause_resume->Bind(wxEVT_ENTER_WINDOW, [this](auto &e) { if (m_button_pause_resume->GetToolTipText() == _L("Pause")) { @@ -315,7 +304,7 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) } }); - m_button_abort = new ScalableButton(m_panel_progress, wxID_ANY, "print_control_stop", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, true); + m_button_abort = new ScalableButton(progress_lr_panel, wxID_ANY, "print_control_stop", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, true); m_button_abort->SetToolTip(_L("Stop")); m_button_abort->Bind(wxEVT_ENTER_WINDOW, [this](auto &e) { @@ -326,14 +315,6 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) m_button_abort->SetBitmap_("print_control_stop"); } ); - m_sizer_progressbar->Add(m_gauge_progress, 1, wxALIGN_CENTER_VERTICAL, 0); - m_sizer_progressbar->Add(0, 0, 0, wxEXPAND|wxLEFT, FromDIP(18)); - m_sizer_progressbar->Add(m_button_partskip, 0, wxALL, FromDIP(5)); - m_sizer_progressbar->Add(0, 0, 0, wxEXPAND | wxLEFT, FromDIP(18)); - m_sizer_progressbar->Add(m_button_pause_resume, 0, wxALL, FromDIP(5)); - m_sizer_progressbar->Add(0, 0, 0, wxEXPAND|wxLEFT, FromDIP(18)); - m_sizer_progressbar->Add(m_button_abort, 0, wxALL, FromDIP(5)); - wxBoxSizer *bSizer_buttons = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer *bSizer_text = new wxBoxSizer(wxHORIZONTAL); wxPanel* penel_bottons = new wxPanel(parent); @@ -380,48 +361,55 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxT("HarmonyOS Sans SC"))); m_staticText_progress_end->SetForegroundColour(wxColour(146, 146, 146)); - //fgSizer_task->Add(bSizer_buttons, 0, wxEXPAND, 0); - //fgSizer_task->Add(0, 0, 0, wxEXPAND, FromDIP(5)); - - wxPanel* panel_button_block = new wxPanel(penel_bottons, wxID_ANY); - panel_button_block->SetMinSize(wxSize(TASK_BUTTON_SIZE.x * 2 + FromDIP(5) * 4, -1)); - panel_button_block->SetMinSize(wxSize(TASK_BUTTON_SIZE.x * 2 + FromDIP(5) * 4, -1)); - panel_button_block->SetSize(wxSize(TASK_BUTTON_SIZE.x * 2 + FromDIP(5) * 2, -1)); - panel_button_block->SetBackgroundColour(*wxWHITE); - m_staticText_layers = new wxStaticText(penel_text, wxID_ANY, _L("Layer: N/A")); m_staticText_layers->SetFont(wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxT("HarmonyOS Sans SC"))); m_staticText_layers->SetForegroundColour(wxColour(146, 146, 146)); m_staticText_layers->Hide(); - //bSizer_text->Add(m_staticText_progress_percent, 0, wxALL, 0); bSizer_text->Add(sizer_percent, 0, wxEXPAND, 0); bSizer_text->Add(sizer_percent_icon, 0, wxEXPAND, 0); bSizer_text->Add(0, 0, 1, wxEXPAND, 0); - bSizer_text->Add(m_staticText_layers, 0, wxALIGN_CENTER | wxALL, 0); + bSizer_text->Add(m_staticText_layers, 0, wxALIGN_CENTER_VERTICAL | wxALL, 0); bSizer_text->Add(0, 0, 0, wxLEFT, FromDIP(20)); - bSizer_text->Add(m_staticText_progress_left, 0, wxALIGN_CENTER | wxALL, 0); + bSizer_text->Add(m_staticText_progress_left, 0, wxALIGN_CENTER_VERTICAL | wxALL, 0); // Orca: display the end time of the print bSizer_text->Add(0, 0, 0, wxLEFT, FromDIP(8)); bSizer_text->Add(m_staticText_progress_end, 0, wxALIGN_CENTER | wxALL, 0); - penel_text->SetMaxSize(wxSize(FromDIP(600), -1)); + // penel_text->SetMaxSize(wxSize(FromDIP(600), -1)); penel_text->SetSizer(bSizer_text); penel_text->Layout(); - bSizer_buttons->Add(penel_text, 1, wxEXPAND | wxALL, 0); - bSizer_buttons->Add(panel_button_block, 0, wxALIGN_CENTER | wxALL, 0); + auto progress_lr_sizer = new wxBoxSizer(wxHORIZONTAL); + auto progress_left_sizer = new wxBoxSizer(wxVERTICAL); + auto progress_right_sizer = new wxBoxSizer(wxHORIZONTAL); - penel_bottons->SetSizer(bSizer_buttons); - penel_bottons->Layout(); + progress_left_sizer->Add(penel_text, 0, wxEXPAND | wxALL, 0); + progress_left_sizer->Add(m_gauge_progress, 0, wxEXPAND | wxALL, 0); + // progress_left_sizer->SetMaxSize(wxSize(FromDIP(600), -1)); + + progress_right_sizer->Add(0, 0, 0, wxEXPAND | wxLEFT, FromDIP(18)); + progress_right_sizer->Add(m_button_partskip, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(0));//5 + progress_right_sizer->Add(0, 0, 0, wxEXPAND | wxLEFT, FromDIP(18)); + progress_right_sizer->Add(m_button_pause_resume, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(0)); + progress_right_sizer->Add(0, 0, 0, wxEXPAND | wxLEFT, FromDIP(18)); + progress_right_sizer->Add(m_button_abort, 0, wxALL | wxALIGN_CENTER_VERTICAL, FromDIP(0)); + progress_right_sizer->Add(0, 0, 0, wxEXPAND | wxLEFT, FromDIP(18)); + + progress_lr_sizer->Add(progress_left_sizer, 1, wxEXPAND | wxALL, 0); + progress_lr_sizer->Add(progress_right_sizer, 0, wxEXPAND | wxALL , 0); + + progress_lr_panel->SetSizer(progress_lr_sizer); + progress_lr_panel->SetMaxSize(wxSize(FromDIP(720), -1)); + + progress_lr_panel->Layout(); + progress_lr_panel->Fit(); bSizer_subtask_info->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(14)); bSizer_subtask_info->Add(bSizer_task_name, 0, wxEXPAND|wxRIGHT, FromDIP(18)); bSizer_subtask_info->Add(m_staticText_profile_value, 0, wxEXPAND | wxTOP, FromDIP(5)); bSizer_subtask_info->Add(m_printing_stage_value, 0, wxEXPAND | wxTOP, FromDIP(5)); - bSizer_subtask_info->Add(penel_bottons, 0, wxEXPAND | wxTOP, FromDIP(10)); - bSizer_subtask_info->Add(m_panel_progress, 0, wxEXPAND|wxRIGHT, FromDIP(25)); - + bSizer_subtask_info->Add(progress_lr_panel, 0, wxEXPAND | wxTOP, FromDIP(5)); m_printing_sizer = new wxBoxSizer(wxHORIZONTAL); m_printing_sizer->SetMinSize(wxSize(PAGE_MIN_WIDTH, -1)); @@ -660,6 +648,14 @@ void PrintingTaskPanel::update_machine_object(MachineObject* obj){ void PrintingTaskPanel::enable_partskip_button(bool enable) { + if(m_obj){ + if( m_obj->is_support_partskip ){ + m_button_partskip->Show(); + }else{ + m_button_partskip->Hide(); + } + } + if (!enable) { m_button_partskip->Enable(false); m_button_partskip->SetLabel(""); diff --git a/src/slic3r/GUI/Widgets/AnimaController.cpp b/src/slic3r/GUI/Widgets/AnimaController.cpp index 10f0061dbf..d034fe9fd8 100644 --- a/src/slic3r/GUI/Widgets/AnimaController.cpp +++ b/src/slic3r/GUI/Widgets/AnimaController.cpp @@ -13,8 +13,8 @@ AnimaIcon::AnimaIcon(wxWindow *parent, wxWindowID id, std::vector i m_size = 20; //add ScalableBitmap - for (const auto &filename : img_list) m_images.emplace_back(create_scaled_bitmap(filename, this, m_size)); - m_image_enable = create_scaled_bitmap(img_enable, this, m_size-8); + for (const auto &filename : img_list) m_images.emplace_back(create_scaled_bitmap(filename, this, FromDIP(m_size))); + m_image_enable = create_scaled_bitmap(img_enable, this, FromDIP(m_size-8)); // show first wxStaticBitmap if (!m_images.empty()) m_bitmap = new wxStaticBitmap(this, wxID_ANY, m_images[0], wxDefaultPosition, wxSize(FromDIP(m_size), FromDIP(m_size))); @@ -73,5 +73,3 @@ void AnimaIcon::Enable() { if (m_bitmap) { m_bitmap->SetBitmap(m_image_enable); } } - - From 17ed76a033597352597c8729b017c018ecfec5f2 Mon Sep 17 00:00:00 2001 From: hemai Date: Fri, 4 Jul 2025 10:55:07 +0800 Subject: [PATCH 07/27] FIX: update SkipCanvas to SkipPartCanvas Jira: [STUDIO-12687] Change-Id: Ib50b7711ced0c3261586faae37ecc9054ea81183 (cherry picked from commit 7b4792d823a1b29ca3fe7f500ed30bd4211770d6) --- localization/i18n/list.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/localization/i18n/list.txt b/localization/i18n/list.txt index a2c2b64dce..a4a37e55b4 100644 --- a/localization/i18n/list.txt +++ b/localization/i18n/list.txt @@ -179,5 +179,5 @@ src/slic3r/GUI/BackgroundSlicingProcess.cpp src/slic3r/GUI/Gizmos/GLGizmoBrimEars.cpp src/slic3r/GUI/PartSkipDialog.cpp src/slic3r/GUI/PartSkipDialog.hpp -src/slic3r/GUI/SkipCanvas.cpp -src/slic3r/GUI/SkipCanvas.cpp +src/slic3r/GUI/SkipPartCanvas.cpp +src/slic3r/GUI/SkipPartCanvas.hpp From 1928add5a1cebd96702c4d804e3477314b3415bc Mon Sep 17 00:00:00 2001 From: hemai Date: Mon, 7 Jul 2025 12:04:35 +0800 Subject: [PATCH 08/27] FIX: partskip button display bug && cavas update bug Jira: [STUDIO-12687] Change-Id: Ibb42dab945be9b9b4b8b8ac8f2c268960d553325 (cherry picked from commit 15d8fb70c93d243a1592c050302e76867ed6582c) --- resources/images/partskip_retry.svg | 26 +++ .../images/print_control_partskip_hover.svg | 4 + src/slic3r/GUI/PartSkipDialog.cpp | 83 ++++++-- src/slic3r/GUI/PartSkipDialog.hpp | 6 +- src/slic3r/GUI/SkipPartCanvas.cpp | 183 +++++++++++++----- src/slic3r/GUI/SkipPartCanvas.hpp | 37 ++-- src/slic3r/GUI/StatusPanel.cpp | 51 +++-- src/slic3r/GUI/StatusPanel.hpp | 11 ++ 8 files changed, 303 insertions(+), 98 deletions(-) create mode 100644 resources/images/partskip_retry.svg create mode 100644 resources/images/print_control_partskip_hover.svg diff --git a/resources/images/partskip_retry.svg b/resources/images/partskip_retry.svg new file mode 100644 index 0000000000..ff104e9951 --- /dev/null +++ b/resources/images/partskip_retry.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/images/print_control_partskip_hover.svg b/resources/images/print_control_partskip_hover.svg new file mode 100644 index 0000000000..b41ffcc516 --- /dev/null +++ b/resources/images/print_control_partskip_hover.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/slic3r/GUI/PartSkipDialog.cpp b/src/slic3r/GUI/PartSkipDialog.cpp index bfe98371d2..2b847fd0a0 100644 --- a/src/slic3r/GUI/PartSkipDialog.cpp +++ b/src/slic3r/GUI/PartSkipDialog.cpp @@ -230,11 +230,13 @@ PartSkipDialog::PartSkipDialog(wxWindow* parent): DPIDialog(parent, wxID_ANY, _L m_book_second_sizer = new wxBoxSizer( wxVERTICAL ); m_book_second_btn_sizer = new wxBoxSizer( wxHORIZONTAL ); - m_retry_label = new Label( m_book_second_panel, _L("Load Skipping Objects Information Failed. \nPlease try again.")); + m_retry_bitmap = new wxStaticBitmap(m_book_second_panel, -1, create_scaled_bitmap("partskip_retry", m_book_second_panel, 200), wxDefaultPosition, wxDefaultSize); + m_retry_label = new Label( m_book_second_panel, _L("Load skipping objects information failed. Please try again.")); m_retry_label->Wrap( -1 ); m_retry_label->SetBackgroundColour(*wxWHITE); m_book_second_sizer->Add(0, 0, 1, wxEXPAND, 0); - m_book_second_sizer->Add(m_retry_label, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_CENTER_HORIZONTAL, 0); + m_book_second_sizer->Add(m_retry_bitmap, 0, wxALIGN_CENTER_HORIZONTAL, 0); + m_book_second_sizer->Add(m_retry_label, 0, wxALIGN_CENTER_HORIZONTAL, 0); m_book_second_sizer->Add(0, 0, 1, wxEXPAND, 0); m_second_retry_btn = new Button(m_book_second_panel, _L("Retry")); @@ -305,6 +307,7 @@ void PartSkipDialog::on_dpi_changed(const wxRect& suggested_rect) { m_canvas->LoadPickImage(m_local_paths[0]); m_loading_icon->SetMinSize(wxSize(FromDIP(25), FromDIP(25))); + m_retry_bitmap->SetBitmap(create_scaled_bitmap("partskip_retry", m_book_second_panel, 200)); m_percent_label->SetMinSize(wxSize(FromDIP(56), FromDIP(28))); m_percent_label->SetMaxSize(wxSize(FromDIP(56), FromDIP(28))); @@ -426,8 +429,10 @@ void PartSkipDialog::DownloadPartsFile() m_file_sys->Bind(EVT_STATUS_CHANGED, &PartSkipDialog::OnFileSystemEvent, this); m_file_sys->Bind(EVT_RAMDOWNLOAD, &PartSkipDialog::OnFileSystemResult, this); m_file_sys->Start(); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ <<"part skip: print file system start."; }else{ m_file_sys->Retry(); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ <<"part skip: print file system retry."; } } else { m_file_sys->SendExistedFile(); @@ -526,8 +531,10 @@ void PartSkipDialog::OnFileSystemEvent(wxCommandEvent &e) if( m_url_state == URL_TCP){ m_url_state = URL_TUTK; m_file_sys->Retry(); + BOOST_LOG_TRIVIAL(info) << "part skip: print file system connnect failed first."; }else{ m_file_sys->SendConnectFail(); + BOOST_LOG_TRIVIAL(info) << "part skip: print file system connnect failed second."; } break; } @@ -538,6 +545,7 @@ void PartSkipDialog::OnFileSystemEvent(wxCommandEvent &e) boost::shared_ptr fs(wfs.lock()); if (!fs) return; fetchUrl(boost::weak_ptr(fs)); + BOOST_LOG_TRIVIAL(info) << "part skip: fetch url, get parts info files from printer."; }); } } @@ -550,10 +558,12 @@ void PartSkipDialog::OnFileSystemResult(wxCommandEvent &event){ InitDialogUI(); SetSimplebookPage(2); m_file_sys->Stop(); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ <<"part skip: on file system result success."; }else{ m_url_state = URL_TCP; SetSimplebookPage(1); m_file_sys->Stop(); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ <<"part skip: on file system result failed."; } } @@ -670,6 +680,7 @@ bool PartSkipDialog::Show(bool show) void PartSkipDialog::InitDialogUI() { m_print_lock = true; + BOOST_LOG_TRIVIAL(info) << "part skip: lock parts info from printer."; m_scroll_sizer->Clear(true); m_all_checkbox->SetValue(false); m_parts_state.clear(); @@ -684,11 +695,15 @@ void PartSkipDialog::InitDialogUI() { m_canvas->SwitchDrag(false); m_canvas->SetZoomPercent(100); m_canvas->SetOffset(wxPoint(0, 0)); + + BOOST_LOG_TRIVIAL(info) << "part skip: load canvas pick image begin."; m_canvas->LoadPickImage(pick_img); + BOOST_LOG_TRIVIAL(info) << "part skip: load canvas pick image end."; ModelSettingHelper helper(slice_info); if (helper.Parse()) { - auto parse_result = helper.GetResults(); + int plate_idx = m_obj ? m_obj->m_plate_index : 0; + auto parse_result = helper.GetPlates()[plate_idx - 1].objects; for (const auto& part : parse_result) { m_parts_state[part.identify_id] = part.state; m_parts_name[part.identify_id] = part.name; @@ -741,31 +756,42 @@ void PartSkipDialog::InitDialogUI() { m_scroll_sizer->Add(line_sizer, 0, wxBOTTOM | wxEXPAND, FromDIP(12)); } m_canvas->UpdatePartsInfo(GetPartsInfo()); + BOOST_LOG_TRIVIAL(info) << "part skip: update canvas parts info."; } m_scroll_sizer->Layout(); UpdateCountLabel(); Refresh(); m_print_lock = false; + BOOST_LOG_TRIVIAL(info) << "part skip: unlock parts info from printer."; } void PartSkipDialog::UpdatePartsStateFromPrinter(MachineObject *obj) { - if (m_print_lock) return; + if (m_print_lock) { + BOOST_LOG_TRIVIAL(info) << "part skip: parts info from printer is locked."; + return; + } m_obj = obj; if (m_obj) { + bool update_flag = false; std::vector partskip_ids = m_obj->m_partskip_ids; for(auto part_id : partskip_ids) { - m_parts_state[part_id] = PartState::psSkipped; + if(m_parts_state[part_id] != PartState::psSkipped){ + m_parts_state[part_id] = PartState::psSkipped; + update_flag = true; + } + } + if(update_flag){ + m_canvas->UpdatePartsInfo(GetPartsInfo()); + UpdateDialogUI(); } - m_canvas->UpdatePartsInfo(GetPartsInfo()); - UpdateDialogUI(); } } void PartSkipDialog::UpdateDialogUI(){ if(m_parts_state.size() != m_scroll_sizer->GetItemCount()){ - BOOST_LOG_TRIVIAL(warning) << "m_parts_state and m_scroll_sizer mismatch."; + BOOST_LOG_TRIVIAL(warning) << "part skip: m_parts_state and m_scroll_sizer mismatch."; return; } @@ -839,30 +865,47 @@ void PartSkipDialog::OnApplyDialog(wxCommandEvent &event) m_partskip_ids.push_back(part_id); } } + + bool all_skipped = true; + for (auto [part_id, part_state] : m_parts_state) { + if (part_state == PartState::psUnCheck) all_skipped = false; + } + PartSkipConfirmDialog confirm_dialog(this); - confirm_dialog.SetMsgLabel(wxString::Format(_L("Skipping %d objects."), m_partskip_ids.size())); + if (all_skipped){ + confirm_dialog.SetMsgLabel(_L("Skipping all objects.")); + confirm_dialog.SetTipLabel(_L("The printing job will be stopped. Continue?")); + }else{ + confirm_dialog.SetMsgLabel(wxString::Format(_L("Skipping %d objects."), m_partskip_ids.size())); + confirm_dialog.SetTipLabel(_L("This action cannot be undone. Continue?")); + } if(confirm_dialog.ShowModal() == wxID_OK){ if (m_obj) { - BOOST_LOG_TRIVIAL(info) << "monitor: skipping "<< m_partskip_ids.size() <<" objects."; - - bool all_skipped = true; - for (auto [part_id, part_state] : m_parts_state) { - if (part_state == PartState::psUnCheck) all_skipped = false; - } + BOOST_LOG_TRIVIAL(info) << "part skip: skipping "<< m_partskip_ids.size() <<" objects."; if (all_skipped) { m_obj->command_task_abort(); + BOOST_LOG_TRIVIAL(info) << "part skip: command skip all parts, abort task."; } else { m_obj->command_task_partskip(m_partskip_ids); + BOOST_LOG_TRIVIAL(info) << "part skip: command skip " << m_partskip_ids.size() << " parts."; } EndModal(wxID_OK); } else { - BOOST_LOG_TRIVIAL(warning) << "machine object is null."; + BOOST_LOG_TRIVIAL(warning) << "part skip: machine object is null."; } } } +int PartSkipDialog::GetAllSkippedPartsNum() { + int skipped_cnt = 0; + for (auto& [part_id, part_state] : m_parts_state) { + if (part_state == PartState::psSkipped || part_state == PartState::psChecked) + skipped_cnt++; + } + return skipped_cnt; +} PartSkipConfirmDialog::PartSkipConfirmDialog(wxWindow *parent) : DPIDialog(parent, wxID_ANY, _L("Skip Objects"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX) { @@ -886,9 +929,7 @@ PartSkipConfirmDialog::PartSkipConfirmDialog(wxWindow *parent) : DPIDialog(paren m_msg_label->Wrap( -1 ); m_msg_label->SetBackgroundColour(*wxWHITE); - - - auto m_tip_label = new Label(this, _L("This action cannot be undone. Continue?")); + m_tip_label = new Label(this, _L("This action cannot be undone. Continue?")); m_tip_label->Wrap(-1); m_tip_label->SetBackgroundColour(*wxWHITE); m_tip_label->SetForegroundColour(wxColor(92,92,92)); @@ -965,6 +1006,10 @@ void PartSkipConfirmDialog::SetMsgLabel(wxString msg){ m_msg_label->SetLabel(msg); } +void PartSkipConfirmDialog::SetTipLabel(wxString msg){ + m_tip_label->SetLabel(msg); +} + } } // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/PartSkipDialog.hpp b/src/slic3r/GUI/PartSkipDialog.hpp index 1428f15ee8..ef5cb743af 100644 --- a/src/slic3r/GUI/PartSkipDialog.hpp +++ b/src/slic3r/GUI/PartSkipDialog.hpp @@ -39,6 +39,7 @@ class PartSkipConfirmDialog : public DPIDialog private: protected: Label *m_msg_label; + Label *m_tip_label; Button *m_apply_button; public: @@ -48,6 +49,7 @@ public: void on_dpi_changed(const wxRect &suggested_rect); Button* GetConfirmButton(); void SetMsgLabel(wxString msg); + void SetTipLabel(wxString msg); bool Show(bool show); }; @@ -63,7 +65,7 @@ public: void SetSimplebookPage(int page); void InitSchedule(MachineObject *obj_); void InitDialogUI(); - + int GetAllSkippedPartsNum(); MachineObject *m_obj{nullptr}; @@ -91,6 +93,8 @@ public: Label* m_loading_label; Label* m_retry_label; + ScalableBitmap* m_retry_icon; + wxStaticBitmap* m_retry_bitmap; wxBoxSizer* m_sizer; wxBoxSizer* m_dlg_sizer; diff --git a/src/slic3r/GUI/SkipPartCanvas.cpp b/src/slic3r/GUI/SkipPartCanvas.cpp index 09e6987a7a..85c2cc6c6d 100644 --- a/src/slic3r/GUI/SkipPartCanvas.cpp +++ b/src/slic3r/GUI/SkipPartCanvas.cpp @@ -32,9 +32,47 @@ SkipPartCanvas::SkipPartCanvas(wxWindow *parent, const wxGLAttributes& dispAttrs void SkipPartCanvas::LoadPickImage(const std::string & path) { + auto ParseShapeId = [](cv::Mat image, const std::vector> &contours, const std::vector &hierarchy, int root_idx) -> uint32_t { + cv::Mat mask = cv::Mat::zeros(image.size(), CV_8UC1); + + cv::drawContours(mask, contours, root_idx, 255, cv::FILLED); + + int child = hierarchy[root_idx][2]; + while (child != -1) { + cv::drawContours(mask, contours, child, 0, cv::FILLED); + child = hierarchy[child][0]; + } + std::vector pixels; + for (int y = 0; y < image.rows; ++y) { + for (int x = 0; x < image.cols; ++x) { + if (mask.at(y, x)) { pixels.push_back(image.at(y, x)); } + } + } + + std::map> colorCount( + [](const cv::Vec3b &a, const cv::Vec3b &b) { return std::lexicographical_compare(a.val, a.val + 3, b.val, b.val + 3); }); + + for (auto &c : pixels) colorCount[c]++; + + cv::Vec3b main_color; + int max_count = 0; + int total_count = 0; + for (const auto &kv : colorCount) { + if (kv.second > max_count) { + max_count = kv.second; + main_color = kv.first; + } + total_count += kv.second; + } + + SkipIdHelper helper{main_color[2], main_color[1], main_color[0]}; + helper.reverse(); + return (max_count * 2 > total_count) ? helper.value : 0; + }; + parts_state_.clear(); - pick_parts_.clear(); parts_triangles_.clear(); + pick_parts_.clear(); int preffered_w{FromDIP(400)}, preffered_h{FromDIP(400)}; cv::Mat src_image = cv::imread(path, cv::IMREAD_UNCHANGED); cv::cvtColor(src_image, src_image, cv::COLOR_BGRA2BGR); // remove alpha @@ -52,35 +90,64 @@ void SkipPartCanvas::LoadPickImage(const std::string & path) cv::Mat mask; // convery to binary cv::threshold(gray, mask, 0, 255, cv::THRESH_BINARY); std::vector> pick_counters; - cv::findContours(mask, pick_counters, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_TC89_KCOS); - std::vector> polygon; - for (const auto& counter : pick_counters) { - cv::Point center_pos{0,0}; - for (const auto& pt : counter) { - center_pos += pt; + std::vector hierarchy; + cv::findContours(mask, pick_counters, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_TC89_KCOS); + auto compute_depth = [&](int idx) { + int depth = 0; + while (hierarchy[idx][3] != -1) { + depth++; + idx = hierarchy[idx][3]; } - center_pos = center_pos / static_cast(counter.size()); - auto id = GetIdAtImagePt(wxPoint(center_pos.x, center_pos.y)); + return depth; + }; + for (int i = 0; i < pick_counters.size(); ++i) { + int depth = compute_depth(i); + int parent = hierarchy[i][3]; + if (parent != -1) continue; + + auto id = ParseShapeId(pick_image_, pick_counters, hierarchy, i); if (id > 0) { - polygon.clear(); - polygon.emplace_back(std::vector{}); - for (const auto& pt : counter) { - polygon[0].push_back(FloatPoint{pt.x * 1.0f, pt.y * 1.0f}); + std::vector flat_points; + std::vector> polygon; + + // part body + { + polygon.emplace_back(); + for (const auto &pt : pick_counters[i]) { + FloatPoint fp{pt.x * 1.0f, pt.y * 1.0f}; + polygon.back().push_back(fp); + flat_points.push_back(fp); + } + int child = hierarchy[i][2]; + while (child != -1) { + polygon.emplace_back(); + for (const auto &pt : pick_counters[child]) { + FloatPoint fp{pt.x * 1.0f, pt.y * 1.0f}; + polygon.back().push_back(fp); + flat_points.push_back(fp); + } + child = hierarchy[child][0]; + } + std::vector indices = mapbox::earcut(polygon); + std::vector final_counter; + for (size_t j = 0; j < indices.size(); j += 3) { + final_counter.push_back(flat_points[indices[j]]); + final_counter.push_back(flat_points[indices[j + 1]]); + final_counter.push_back(flat_points[indices[j + 2]]); + } + + parts_triangles_[id].emplace_back(final_counter); } - std::vector indices = mapbox::earcut(polygon); - std::vector final_counter{}; - for (size_t i = 0; i < indices.size(); i += 3) { - FloatPoint a = polygon[0][indices[i]]; - FloatPoint b = polygon[0][indices[i+1]]; - FloatPoint c = polygon[0][indices[i+2]]; - final_counter.push_back(FloatPoint{a[0], a[1]}); - final_counter.push_back(FloatPoint{b[0], b[1]}); - final_counter.push_back(FloatPoint{c[0], c[1]}); + // part outlines + { + pick_parts_[id].emplace_back(pick_counters[i]); + int child = hierarchy[i][2]; + while (child != -1) { + pick_parts_[id].emplace_back(pick_counters[child]); + child = hierarchy[child][0]; + } } - parts_triangles_.emplace(id, final_counter); - pick_parts_.emplace(id, counter); - if (parts_state_.find(id) == parts_state_.end()) - parts_state_.emplace(id, psUnCheck); + if (parts_state_.find(id) == parts_state_.end()) parts_state_.emplace(id, psUnCheck); } } } @@ -220,13 +287,15 @@ void SkipPartCanvas::Render() if (part_info == parts_state_.end() || part_info->second != part_type) continue; glColor3f(1, 1, 1); - glBegin(GL_TRIANGLES); - for (size_t i = 0; i < contour.second.size(); i += 3) { - glVertex2f(contour.second[i][0], contour.second[i][1]); - glVertex2f(contour.second[i+1][0], contour.second[i+1][1]); - glVertex2f(contour.second[i+2][0], contour.second[i+2][1]); + for (const auto &contour_item : contour.second) { + glBegin(GL_TRIANGLES); + for (size_t i = 0; i < contour_item.size(); i += 3) { + glVertex2f(contour_item[i][0], contour_item[i][1]); + glVertex2f(contour_item[i + 1][0], contour_item[i + 1][1]); + glVertex2f(contour_item[i + 2][0], contour_item[i + 2][1]); + } + glEnd(); } - glEnd(); } for (const auto& contour : pick_parts_) { @@ -237,11 +306,11 @@ void SkipPartCanvas::Render() glColor3f(rgb.r(), rgb.g(), rgb.b()); glLineWidth(border_w); - glBegin(GL_LINE_LOOP); - for (const auto& pt : contour.second) { - glVertex2f(pt.x, pt.y); + for (const auto &contour_item : contour.second) { + glBegin(GL_LINE_LOOP); + for (const auto &pt : contour_item) { glVertex2f(pt.x, pt.y); } + glEnd(); } - glEnd(); } }; // draw unchecked shapes @@ -277,11 +346,11 @@ void SkipPartCanvas::Render() continue; glColor3f(bound.r(), bound.g(), bound.b()); glLineWidth(border_w); - glBegin(GL_LINE_LOOP); - for (const auto& pt : contour.second) { - glVertex2f(pt.x, pt.y); + for (const auto &contour_item : contour.second) { + glBegin(GL_LINE_LOOP); + for (const auto &pt : contour_item) { glVertex2f(pt.x, pt.y); } + glEnd(); } - glEnd(); } }; @@ -559,29 +628,41 @@ bool ModelSettingHelper::Parse() return true; } -std::vector ModelSettingHelper::GetResults() { - return context_.objects; -} - void XMLCALL ModelSettingHelper::StartElementHandler(void *userData, const XML_Char *name, const XML_Char **atts) { - ModelSettingHelper* self = static_cast(userData); - if (strcmp(name, "object") == 0) { + ModelSettingHelper *self = static_cast(userData); + if (strcmp(name, "plate") == 0) { + self->context_.current_plate = PlateInfo(); // start a new plate + self->context_.in_plate = true; + } else if (strcmp(name, "metadata") == 0 && self->context_.in_plate) { + std::string key, value; for (int i = 0; atts[i]; i += 2) { - if (strcmp(atts[i], "identify_id") == 0) { self->context_.temp_object.identify_id = atoi(atts[i + 1]); } - if (strcmp(atts[i], "name") == 0) { self->context_.temp_object.name = std::string(atts[i + 1]); } + if (strcmp(atts[i], "key") == 0) key = atts[i + 1]; + if (strcmp(atts[i], "value") == 0) value = atts[i + 1]; } + if (key == "index") { self->context_.current_plate.index = std::stoi(value); } + } else if (strcmp(name, "object") == 0 && self->context_.in_plate) { + ObjectInfo obj; + for (int i = 0; atts[i]; i += 2) { + if (strcmp(atts[i], "identify_id") == 0) obj.identify_id = atoi(atts[i + 1]); + if (strcmp(atts[i], "name") == 0) obj.name = atts[i + 1]; + } + self->context_.current_plate.objects.push_back(obj); } } void XMLCALL ModelSettingHelper::EndElementHandler(void *userData, const XML_Char *name) { - ModelSettingHelper* self = static_cast(userData); - if (strcmp(name, "object") == 0) { - self->context_.objects.push_back(self->context_.temp_object); + ModelSettingHelper *self = static_cast(userData); + if (strcmp(name, "plate") == 0 && self->context_.in_plate) { + self->context_.plates.push_back(self->context_.current_plate); + self->context_.current_plate = PlateInfo(); // reset + self->context_.in_plate = false; } } +std::vector ModelSettingHelper::GetPlates() { return context_.plates; } + void ModelSettingHelper::DataHandler(const XML_Char *s, int len) { // do nothing diff --git a/src/slic3r/GUI/SkipPartCanvas.hpp b/src/slic3r/GUI/SkipPartCanvas.hpp index 6f02510872..77dd4ffa42 100644 --- a/src/slic3r/GUI/SkipPartCanvas.hpp +++ b/src/slic3r/GUI/SkipPartCanvas.hpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include @@ -85,8 +84,8 @@ protected: private: wxGLContext* context_; cv::Mat pick_image_; - std::unordered_map> parts_triangles_; - std::unordered_map> pick_parts_; + std::unordered_map < uint32_t, std::vector>> parts_triangles_; + std::unordered_map < uint32_t, std::vector>> pick_parts_; std::unordered_map parts_state_; bool gl_inited_{false}; int zoom_percent_{100}; @@ -131,25 +130,35 @@ public: void log_errors(); }; -class ModelSettingHelper : public _BBS_3MF_Base { - struct ParseContext{ - std::vector objects; - ObjectInfo temp_object; +struct PlateInfo +{ + int index{-1}; + std::vector objects; +}; + +class ModelSettingHelper : public _BBS_3MF_Base +{ + struct ParseContext + { + std::vector plates; + PlateInfo current_plate; + ObjectInfo temp_object; + bool in_plate = false; }; public: - ModelSettingHelper(const std::string& path); + ModelSettingHelper(const std::string &path); + bool Parse(); + std::vector GetPlates(); - bool Parse(); - std::vector GetResults(); private: - std::string path_; + std::string path_; ParseContext context_; - void static XMLCALL StartElementHandler(void* userData, const XML_Char* name, const XML_Char** atts); - void static XMLCALL EndElementHandler(void* userData, const XML_Char* name); - void DataHandler(const XML_Char* s, int len); + static void XMLCALL StartElementHandler(void *userData, const XML_Char *name, const XML_Char **atts); + static void XMLCALL EndElementHandler(void *userData, const XML_Char *name); + void DataHandler(const XML_Char *s, int len); }; } diff --git a/src/slic3r/GUI/StatusPanel.cpp b/src/slic3r/GUI/StatusPanel.cpp index 7763563b5c..8065d09f77 100644 --- a/src/slic3r/GUI/StatusPanel.cpp +++ b/src/slic3r/GUI/StatusPanel.cpp @@ -269,7 +269,8 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) StateColor white_bg(std::pair(wxColour(255, 255, 255), StateColor::Disabled), std::pair(wxColour(255, 255, 255), StateColor::Pressed), std::pair(wxColour(255, 255, 255), StateColor::Hovered), std::pair(wxColour(255, 255, 255), StateColor::Enabled), std::pair(wxColour(255, 255, 255), StateColor::Normal)); - m_button_partskip = new Button(progress_lr_panel, ""); + + m_button_partskip = new Button(progress_lr_panel, wxEmptyString, "print_control_partskip_disable", 0, 20, wxID_ANY); m_button_partskip->Enable(false); m_button_partskip->Hide(); m_button_partskip->SetBackgroundColor(white_bg); @@ -278,7 +279,7 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) m_button_partskip->SetFont(Label::Body_12); m_button_partskip->SetCornerRadius(0); m_button_partskip->SetToolTip(_L("Parts Skip")); - m_button_partskip->Bind(wxEVT_ENTER_WINDOW, [this](auto &e) { m_button_partskip->SetIcon("print_control_partskip"); }); + m_button_partskip->Bind(wxEVT_ENTER_WINDOW, [this](auto &e) { m_button_partskip->SetIcon("print_control_partskip_hover"); }); m_button_partskip->Bind(wxEVT_LEAVE_WINDOW, [this](auto &e) { m_button_partskip->SetIcon("print_control_partskip"); }); m_button_pause_resume = new ScalableButton(progress_lr_panel, wxID_ANY, "print_control_pause", wxEmptyString, wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER,true); @@ -648,15 +649,13 @@ void PrintingTaskPanel::update_machine_object(MachineObject* obj){ void PrintingTaskPanel::enable_partskip_button(bool enable) { - if(m_obj){ - if( m_obj->is_support_partskip ){ - m_button_partskip->Show(); - }else{ - m_button_partskip->Hide(); - } + int stage = 0; + bool in_calibration_mode = false; + if( m_obj && (m_obj->print_type == "system" || CalibUtils::get_calib_mode_by_name(m_obj->subtask_name, stage) != CalibMode::Calib_None)){ + in_calibration_mode = true; } - if (!enable) { + if (!enable || in_calibration_mode) { m_button_partskip->Enable(false); m_button_partskip->SetLabel(""); m_button_partskip->SetIcon("print_control_partskip_disable"); @@ -1971,6 +1970,19 @@ void StatusPanel::on_market_retry(wxCommandEvent &event) } } +void StatusPanel::update_partskip_button(MachineObject *obj) { + if (!obj) return; + + m_project_task_panel->update_machine_object(obj); + auto partskip_button = m_project_task_panel->get_partskip_button(); + if( obj->is_support_partskip ){ + partskip_button->Show(); + }else{ + partskip_button->Hide(); + } + BOOST_LOG_TRIVIAL(info) << "part skip: is_support_partskip: "<< obj->is_support_partskip; +} + void StatusPanel::on_subtask_partskip(wxCommandEvent &event) { if (m_partskip_dlg == nullptr) { @@ -1979,7 +1991,13 @@ void StatusPanel::on_subtask_partskip(wxCommandEvent &event) auto dm = GUI::wxGetApp().getDeviceManager(); m_partskip_dlg->InitSchedule(dm->get_selected_machine()); - m_partskip_dlg->ShowModal(); + BOOST_LOG_TRIVIAL(info) << "part skip: initial part skip dialog."; + if(m_partskip_dlg->ShowModal() == wxID_OK){ + int cnt = m_partskip_dlg->GetAllSkippedPartsNum(); + m_project_task_panel->set_part_skipped_count(cnt); + m_project_task_panel->set_part_skipped_dirty(5); + BOOST_LOG_TRIVIAL(info) << "part skip: prepare to filter printer dirty data."; + } } void StatusPanel::on_subtask_pause_resume(wxCommandEvent &event) @@ -3126,6 +3144,7 @@ void StatusPanel::update_subtask(MachineObject *obj) } update_model_info(); + update_partskip_button(obj); if (obj->is_system_printing() || obj->is_in_calibration()) { @@ -3267,11 +3286,17 @@ void StatusPanel::update_partskip_subtask(MachineObject *obj){ if (!obj) return; if (!obj->subtask_) return; - m_project_task_panel->update_machine_object(obj); - auto partskip_button = m_project_task_panel->get_partskip_button(); if (partskip_button) { - int part_cnt = obj->m_partskip_ids.size(); + int part_cnt = 0; + if(m_project_task_panel->get_part_skipped_dirty() > 0){ + m_project_task_panel->set_part_skipped_dirty(m_project_task_panel->get_part_skipped_dirty() - 1); + part_cnt = m_project_task_panel->get_part_skipped_count(); + BOOST_LOG_TRIVIAL(info) << "part skip: stop recv printer dirty data."; + }else{ + part_cnt = obj->m_partskip_ids.size(); + BOOST_LOG_TRIVIAL(info) << "part skip: recv printer normal data."; + } if (part_cnt > 0) partskip_button->SetLabel(wxString::Format(_L("(%d)"), part_cnt)); else diff --git a/src/slic3r/GUI/StatusPanel.hpp b/src/slic3r/GUI/StatusPanel.hpp index 6afc194cd8..a300d1682e 100644 --- a/src/slic3r/GUI/StatusPanel.hpp +++ b/src/slic3r/GUI/StatusPanel.hpp @@ -205,6 +205,10 @@ private: std::vector m_score_star; bool m_star_count_dirty = false; + // partskip button + int m_part_skipped_count{ 0 }; + int m_part_skipped_dirty{ 0 }; + ProgressBar* m_gauge_progress; Label* m_error_text; PrintingTaskType m_type; @@ -251,6 +255,10 @@ public: std::vector &get_score_star() { return m_score_star; } bool get_star_count_dirty() { return m_star_count_dirty; } void set_star_count_dirty(bool dirty) { m_star_count_dirty = dirty; } + int get_part_skipped_count() { return m_part_skipped_count; } + void set_part_skipped_count(int count) { m_part_skipped_count = count; } + int get_part_skipped_dirty() { return m_part_skipped_dirty; } + void set_part_skipped_dirty(int dirty) { m_part_skipped_dirty = dirty; } void set_has_reted_text(bool has_rated); void paint(wxPaintEvent&); }; @@ -631,6 +639,9 @@ protected: void update_camera_state(MachineObject* obj); bool show_vcamera = false; + // partskip button + void update_partskip_button(MachineObject* obj); + public: void update_error_message(); From 7d147cc550f0f2e514843a29bab10d6a69d32599 Mon Sep 17 00:00:00 2001 From: hemai Date: Tue, 8 Jul 2025 19:23:38 +0800 Subject: [PATCH 09/27] FIX: parts file download failed && canvas display error Jira: [STUDIO-13253] Change-Id: I6ce11c0ceb1c4f7ed49c41eb9c6d7a6d96e866f9 (cherry picked from commit 37f742007bc168d8a3365ca617c7bd27bafca038) --- src/slic3r/GUI/DeviceManager.cpp | 4 +- src/slic3r/GUI/PartSkipDialog.cpp | 575 +++++++++---------- src/slic3r/GUI/PartSkipDialog.hpp | 110 ++-- src/slic3r/GUI/Printer/PrinterFileSystem.cpp | 30 +- src/slic3r/GUI/Printer/PrinterFileSystem.h | 2 + src/slic3r/GUI/SkipPartCanvas.cpp | 18 +- src/slic3r/GUI/SkipPartCanvas.hpp | 2 +- src/slic3r/GUI/Widgets/AnimaController.cpp | 14 +- 8 files changed, 379 insertions(+), 376 deletions(-) diff --git a/src/slic3r/GUI/DeviceManager.cpp b/src/slic3r/GUI/DeviceManager.cpp index 2e4ae75ada..396ed8e17b 100644 --- a/src/slic3r/GUI/DeviceManager.cpp +++ b/src/slic3r/GUI/DeviceManager.cpp @@ -5216,6 +5216,7 @@ void MachineObject::update_slice_info(std::string project_id, std::string profil if (plate_idx >= 0) { plate_index = plate_idx; + this->m_plate_index = plate_idx; } else { std::string subtask_json; @@ -5278,8 +5279,7 @@ void MachineObject::update_slice_info(std::string project_id, std::string profil BOOST_LOG_TRIVIAL(error) << "task_info: get subtask id failed!"; } } - - this->m_plate_index = plate_index; + // this->m_plate_index = plate_index; }); } } diff --git a/src/slic3r/GUI/PartSkipDialog.cpp b/src/slic3r/GUI/PartSkipDialog.cpp index 2b847fd0a0..9b927aa594 100644 --- a/src/slic3r/GUI/PartSkipDialog.cpp +++ b/src/slic3r/GUI/PartSkipDialog.cpp @@ -27,16 +27,14 @@ #include "SkipPartCanvas.hpp" #include "MediaPlayCtrl.h" - namespace Slic3r { namespace GUI { extern wxString hide_passwd(wxString url, std::vector const &passwords); extern void refresh_agora_url(char const *device, char const *dev_ver, char const *channel, void *context, void (*callback)(void *context, char const *url)); - -PartSkipDialog::PartSkipDialog(wxWindow* parent): DPIDialog(parent, wxID_ANY, _L("Skip Objects"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX) +PartSkipDialog::PartSkipDialog(wxWindow *parent) : DPIDialog(parent, wxID_ANY, _L("Skip Objects"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX) { - std::time_t t = std::time(0); + std::time_t t = std::time(0); std::stringstream buf; buf << put_time(std::localtime(&t), "%a_%b_%d_%H_%M_%S/"); m_timestamp = buf.str(); @@ -45,37 +43,35 @@ PartSkipDialog::PartSkipDialog(wxWindow* parent): DPIDialog(parent, wxID_ANY, _L SetIcon(wxIcon(Slic3r::encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO)); SetBackgroundColour(*wxWHITE); - m_sizer = new wxBoxSizer( wxVERTICAL ); + m_sizer = new wxBoxSizer(wxVERTICAL); m_line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL); m_line_top->SetMinSize(wxSize(-1, 1)); m_line_top->SetBackgroundColour(wxColour(0xA6, 0xa9, 0xAA)); - m_sizer->Add(m_line_top, 0, wxEXPAND | wxTOP, FromDIP(5)); + m_sizer->Add(m_line_top, 0, wxEXPAND | wxTOP, FromDIP(0)); - - m_simplebook = new wxSimplebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0); + m_simplebook = new wxSimplebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0); m_simplebook->SetMinSize(wxSize(FromDIP(720), FromDIP(535))); m_simplebook->SetMaxSize(wxSize(FromDIP(720), FromDIP(535))); - m_book_first_panel = new wxPanel(m_simplebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_book_first_panel = new wxPanel(m_simplebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); m_book_third_panel = new wxPanel(m_simplebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); m_book_third_panel->SetBackgroundColour(*wxWHITE); - m_dlg_sizer = new wxBoxSizer( wxVERTICAL ); - m_dlg_content_sizer = new wxBoxSizer( wxHORIZONTAL ); - m_canvas_sizer = new wxBoxSizer( wxVERTICAL ); + m_dlg_sizer = new wxBoxSizer(wxVERTICAL); + m_dlg_content_sizer = new wxBoxSizer(wxHORIZONTAL); + m_canvas_sizer = new wxBoxSizer(wxVERTICAL); // page 3 - wxGLAttributes canvasAttrs; + wxGLAttributes canvasAttrs; canvasAttrs.PlatformDefaults().Defaults().Stencil(8).EndList(); m_canvas = new SkipPartCanvas(m_book_third_panel, canvasAttrs); - m_canvas->SetMinSize( wxSize( FromDIP(400),FromDIP(400) ) ); - m_canvas->SetMaxSize( wxSize( FromDIP(400),FromDIP(400) ) ); + m_canvas->SetMinSize(wxSize(FromDIP(400), FromDIP(400))); + m_canvas->SetMaxSize(wxSize(FromDIP(400), FromDIP(400))); - - m_canvas_btn_sizer = new wxBoxSizer( wxHORIZONTAL ); - m_canvas_btn_sizer->SetMinSize( wxSize( -1,FromDIP(28))); + m_canvas_btn_sizer = new wxBoxSizer(wxHORIZONTAL); + m_canvas_btn_sizer->SetMinSize(wxSize(-1, FromDIP(28))); StateColor zoom_bg(std::pair(wxColour(255, 255, 255), StateColor::Disabled), std::pair(wxColour(206, 206, 206), StateColor::Pressed), - std::pair(wxColour(238, 238, 238), StateColor::Hovered), std::pair(wxColour(255, 255, 255), StateColor::Enabled), + std::pair(wxColour(238, 238, 238), StateColor::Hovered), std::pair(wxColour(255, 255, 255), StateColor::Enabled), std::pair(wxColour(255, 255, 255), StateColor::Normal)); zoom_bg.setTakeFocusedAsHovered(false); StateColor zoom_bd(std::pair(wxColour(144, 144, 144), StateColor::Disabled), std::pair(wxColour(38, 46, 48), StateColor::Enabled)); @@ -89,10 +85,9 @@ PartSkipDialog::PartSkipDialog(wxWindow* parent): DPIDialog(parent, wxID_ANY, _L m_zoom_out_btn->SetCornerRadius(0); m_zoom_out_btn->SetMinSize(wxSize(FromDIP(56), FromDIP(28))); - StateColor percent_bg(std::pair(wxColour(255, 255, 255), StateColor::Disabled), std::pair(wxColour(255, 255, 255), StateColor::Pressed), std::pair(wxColour(255, 255, 255), StateColor::Hovered), std::pair(wxColour(255, 255, 255), StateColor::Enabled), - std::pair(wxColour(255, 255, 255), StateColor::Normal)); + std::pair(wxColour(255, 255, 255), StateColor::Normal)); m_percent_label = new Button(m_book_third_panel, _L("100 %")); m_percent_label->SetBackgroundColor(percent_bg); m_percent_label->SetBorderColor(wxColour(238, 238, 238)); @@ -107,24 +102,24 @@ PartSkipDialog::PartSkipDialog(wxWindow* parent): DPIDialog(parent, wxID_ANY, _L m_zoom_in_btn->SetMinSize(wxSize(FromDIP(56), FromDIP(28))); m_zoom_in_btn->SetCornerRadius(0); - m_switch_drag_btn = new Button(m_book_third_panel, wxEmptyString); + m_switch_drag_btn = new Button(m_book_third_panel, wxEmptyString); m_switch_drag_btn->SetIcon("canvas_drag"); m_switch_drag_btn->SetToolTip(_L("Drag")); m_switch_drag_btn->SetBackgroundColor(*wxWHITE); m_switch_drag_btn->SetBorderColor(wxColour(238, 238, 238)); - m_switch_drag_btn->SetMinSize(wxSize(FromDIP(56),FromDIP(28))); + m_switch_drag_btn->SetMinSize(wxSize(FromDIP(56), FromDIP(28))); m_switch_drag_btn->SetCornerRadius(0); m_canvas_btn_sizer->Add(m_zoom_out_btn, 0, wxEXPAND | wxLEFT, FromDIP(105)); - m_canvas_btn_sizer->Add( m_percent_label, 0, wxEXPAND, 0 ); - m_canvas_btn_sizer->Add(m_zoom_in_btn, 0, wxEXPAND, 0); - m_canvas_btn_sizer->Add( m_switch_drag_btn, 0, wxEXPAND | wxRIGHT, FromDIP(88) ); + m_canvas_btn_sizer->Add(m_percent_label, 0, wxEXPAND, 0); + m_canvas_btn_sizer->Add(m_zoom_in_btn, 0, wxEXPAND, 0); + m_canvas_btn_sizer->Add(m_switch_drag_btn, 0, wxEXPAND | wxRIGHT, FromDIP(88)); - m_list_sizer = new wxBoxSizer( wxVERTICAL ); - m_list_sizer->SetMinSize( wxSize( FromDIP(267),FromDIP(422) ) ); + m_list_sizer = new wxBoxSizer(wxVERTICAL); + m_list_sizer->SetMinSize(wxSize(FromDIP(267), FromDIP(422))); - auto all_checkbox_sizer = new wxBoxSizer(wxHORIZONTAL); - m_all_checkbox = new CheckBox(m_book_third_panel, wxID_ANY); + auto all_checkbox_sizer = new wxBoxSizer(wxHORIZONTAL); + m_all_checkbox = new CheckBox(m_book_third_panel, wxID_ANY); m_all_checkbox->SetValue(false); m_all_checkbox->SetMinSize(wxSize(FromDIP(18), FromDIP(18))); m_all_checkbox->SetBackgroundColour(*wxWHITE); @@ -136,36 +131,36 @@ PartSkipDialog::PartSkipDialog(wxWindow* parent): DPIDialog(parent, wxID_ANY, _L m_all_label->SetMinSize(wxSize(267, -1)); m_all_label->SetMaxSize(wxSize(267, -1)); all_checkbox_sizer->Add(m_all_checkbox, 0, wxALIGN_CENTER_VERTICAL, 0); - all_checkbox_sizer->Add(m_all_label, 0, wxLEFT| wxALIGN_CENTER_VERTICAL, FromDIP(8)); + all_checkbox_sizer->Add(m_all_label, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, FromDIP(8)); - m_line = new wxPanel(m_book_third_panel, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(267), FromDIP(1)), wxTAB_TRAVERSAL); - m_line->SetMinSize(wxSize(FromDIP(267), 1)); + m_line = new wxPanel(m_book_third_panel, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(267), FromDIP(1)), wxTAB_TRAVERSAL); + m_line->SetMinSize(wxSize(FromDIP(267), 1)); m_line->SetMaxSize(wxSize(FromDIP(267), 1)); - m_line->SetBackgroundColour( wxColor(238, 238, 238) ); + m_line->SetBackgroundColour(wxColor(238, 238, 238)); - m_list_view = new wxScrolledWindow( m_book_third_panel, wxID_ANY, wxDefaultPosition, wxSize( 267,-1 ), wxHSCROLL|wxVSCROLL ); - m_list_view->SetScrollRate( 5, 5 ); - m_list_view->SetMinSize( wxSize( FromDIP(267),FromDIP(378))); + m_list_view = new wxScrolledWindow(m_book_third_panel, wxID_ANY, wxDefaultPosition, wxSize(267, -1), wxHSCROLL | wxVSCROLL); + m_list_view->SetScrollRate(5, 5); + m_list_view->SetMinSize(wxSize(FromDIP(267), FromDIP(378))); m_list_view->SetMaxSize(wxSize(FromDIP(267), FromDIP(378))); m_list_view->SetBackgroundColour(*wxWHITE); m_scroll_sizer = new wxBoxSizer(wxVERTICAL); - m_list_view->SetSizer(m_scroll_sizer); - m_list_view->Layout(); + m_list_view->SetSizer(m_scroll_sizer); + m_list_view->Layout(); - m_dlg_btn_sizer = new wxBoxSizer( wxHORIZONTAL ); + m_dlg_btn_sizer = new wxBoxSizer(wxHORIZONTAL); m_dlg_btn_sizer->SetMinSize(wxSize(-1, FromDIP(54))); - m_cnt_label = new Label(m_book_third_panel, wxEmptyString); - m_cnt_label->Wrap( -1 ); + m_cnt_label = new Label(m_book_third_panel, wxEmptyString); + m_cnt_label->Wrap(-1); m_cnt_label->SetBackgroundColour(*wxWHITE); m_cnt_label->SetForegroundColour(wxColour(0, 174, 66)); - m_cnt_label->SetFont(Label::Head_15); + m_cnt_label->SetFont(Label::Head_16); m_cnt_label->SetSize(wxSize(-1, FromDIP(20))); m_cnt_label->SetMaxSize(wxSize(-1, FromDIP(20))); - m_tot_label = new Label(m_book_third_panel, wxEmptyString); - m_tot_label->Wrap( -1 ); + m_tot_label = new Label(m_book_third_panel, wxEmptyString); + m_tot_label->Wrap(-1); m_tot_label->SetBackgroundColour(*wxWHITE); m_tot_label->SetMinSize(wxSize(FromDIP(200), FromDIP(20))); @@ -184,55 +179,54 @@ PartSkipDialog::PartSkipDialog(wxWindow* parent): DPIDialog(parent, wxID_ANY, _L m_apply_btn->SetMinSize(wxSize(FromDIP(80), FromDIP(32))); m_apply_btn->SetCornerRadius(FromDIP(16)); - - m_canvas_sizer->Add( m_canvas, 0, wxLEFT | wxTOP | wxEXPAND, FromDIP(17) ); - m_canvas_sizer->Add(m_canvas_btn_sizer, 0, wxTOP , FromDIP(8)); + m_canvas_sizer->Add(m_canvas, 0, wxLEFT | wxTOP | wxEXPAND, FromDIP(17)); + m_canvas_sizer->Add(m_canvas_btn_sizer, 0, wxTOP, FromDIP(8)); m_list_sizer->Add(0, 0, 1, wxEXPAND, 0); - m_list_sizer->Add(0, 0, 0, wxTOP , FromDIP(27)); - m_list_sizer->Add(all_checkbox_sizer, 0, wxLEFT , FromDIP(0)); - m_list_sizer->Add(m_line, 0, wxEXPAND | wxTOP | wxBOTTOM, FromDIP(12)); + m_list_sizer->Add(0, 0, 0, wxTOP, FromDIP(27)); + m_list_sizer->Add(all_checkbox_sizer, 0, wxLEFT, FromDIP(0)); + m_list_sizer->Add(m_line, 0, wxEXPAND | wxTOP | wxBOTTOM, FromDIP(12)); m_list_sizer->Add(m_list_view, 0, wxEXPAND, 0); m_list_sizer->Add(0, 0, 1, wxEXPAND, 0); - m_dlg_content_sizer->Add( m_canvas_sizer, 0, wxEXPAND, FromDIP(0) ); - m_dlg_content_sizer->Add( 0, 0, 0, wxEXPAND | wxLEFT, FromDIP(22) ); - m_dlg_content_sizer->Add( m_list_sizer, 0, wxRIGHT | wxEXPAND, FromDIP(14) ); + m_dlg_content_sizer->Add(m_canvas_sizer, 0, wxEXPAND, FromDIP(0)); + m_dlg_content_sizer->Add(0, 0, 0, wxEXPAND | wxLEFT, FromDIP(22)); + m_dlg_content_sizer->Add(m_list_sizer, 0, wxRIGHT | wxEXPAND, FromDIP(14)); m_dlg_btn_sizer->Add(0, 0, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, FromDIP(21)); m_dlg_btn_sizer->Add(m_cnt_label, 0, wxALIGN_CENTER_VERTICAL, FromDIP(0) ); #ifdef __WXMSW__ m_dlg_btn_sizer->Add(m_tot_label, 0, wxLEFT |wxTOP| wxALIGN_CENTER_VERTICAL, FromDIP(2)); #else - m_dlg_btn_sizer->Add(m_tot_label, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 0); + m_dlg_btn_sizer->Add(m_tot_label, 0, wxALIGN_CENTER_VERTICAL, 0); #endif m_dlg_btn_sizer->Add(0, 0, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL, FromDIP(0)); - m_dlg_btn_sizer->Add(m_apply_btn, 0, wxALIGN_CENTER_VERTICAL, FromDIP(0)); - m_dlg_btn_sizer->Add(0, 0, 0, wxLEFT | wxEXPAND| wxALIGN_CENTER_VERTICAL, FromDIP(24)); + m_dlg_btn_sizer->Add(m_apply_btn, 0, wxALIGN_CENTER_VERTICAL, FromDIP(0)); + m_dlg_btn_sizer->Add(0, 0, 0, wxLEFT | wxEXPAND | wxALIGN_CENTER_VERTICAL, FromDIP(24)); m_dlg_placeholder = new wxPanel(m_book_third_panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); m_dlg_placeholder->SetMinSize(wxSize(-1, FromDIP(15))); m_dlg_placeholder->SetMaxSize(wxSize(-1, FromDIP(15))); m_dlg_placeholder->SetBackgroundColour(*wxWHITE); - m_dlg_sizer->Add( m_dlg_content_sizer, 1, wxEXPAND, FromDIP(0) ); + m_dlg_sizer->Add(m_dlg_content_sizer, 1, wxEXPAND, FromDIP(0)); // m_dlg_sizer->Add( 0, 0, 1, wxEXPAND, FromDIP(0)); - m_dlg_sizer->Add( m_dlg_btn_sizer, 0, wxEXPAND, FromDIP(0)); - m_dlg_sizer->Add( m_dlg_placeholder, 0, 0, wxEXPAND, 0); + m_dlg_sizer->Add(m_dlg_btn_sizer, 0, wxEXPAND, FromDIP(0)); + m_dlg_sizer->Add(m_dlg_placeholder, 0, 0, wxEXPAND, 0); - m_book_third_panel->SetSizer( m_dlg_sizer ); - m_book_third_panel->Layout(); - m_dlg_sizer->Fit( m_book_third_panel ); + m_book_third_panel->SetSizer(m_dlg_sizer); + m_book_third_panel->Layout(); + m_dlg_sizer->Fit(m_book_third_panel); // page 2 - m_book_second_panel = new wxPanel( m_simplebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_book_second_panel = new wxPanel(m_simplebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); m_book_second_panel->SetBackgroundColour(*wxWHITE); - m_book_second_sizer = new wxBoxSizer( wxVERTICAL ); - m_book_second_btn_sizer = new wxBoxSizer( wxHORIZONTAL ); + m_book_second_sizer = new wxBoxSizer(wxVERTICAL); + m_book_second_btn_sizer = new wxBoxSizer(wxHORIZONTAL); m_retry_bitmap = new wxStaticBitmap(m_book_second_panel, -1, create_scaled_bitmap("partskip_retry", m_book_second_panel, 200), wxDefaultPosition, wxDefaultSize); - m_retry_label = new Label( m_book_second_panel, _L("Load skipping objects information failed. Please try again.")); - m_retry_label->Wrap( -1 ); + m_retry_label = new Label(m_book_second_panel, _L("Load skipping objects information failed. Please try again.")); + m_retry_label->Wrap(-1); m_retry_label->SetBackgroundColour(*wxWHITE); m_book_second_sizer->Add(0, 0, 1, wxEXPAND, 0); m_book_second_sizer->Add(m_retry_bitmap, 0, wxALIGN_CENTER_HORIZONTAL, 0); @@ -251,42 +245,41 @@ PartSkipDialog::PartSkipDialog(wxWindow* parent): DPIDialog(parent, wxID_ANY, _L m_book_second_btn_sizer->Add(m_second_retry_btn, 0, wxALL, FromDIP(24)); m_book_second_sizer->Add(m_book_second_btn_sizer, 0, wxALIGN_RIGHT, 0); - m_book_second_panel->SetSizer( m_book_second_sizer ); - m_book_second_panel->Layout(); - m_book_second_sizer->Fit( m_book_second_panel ); + m_book_second_panel->SetSizer(m_book_second_sizer); + m_book_second_panel->Layout(); + m_book_second_sizer->Fit(m_book_second_panel); // page 1 - m_book_first_panel = new wxPanel( m_simplebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_book_first_sizer = new wxBoxSizer( wxVERTICAL ); + m_book_first_panel = new wxPanel(m_simplebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + m_book_first_sizer = new wxBoxSizer(wxVERTICAL); // m_book_first_sizer->SetMinSize(wxSize(FromDIP(720), FromDIP(500))); - auto m_loading_sizer = new wxBoxSizer(wxHORIZONTAL); + auto m_loading_sizer = new wxBoxSizer(wxHORIZONTAL); std::vector list{"ams_rfid_1", "ams_rfid_2", "ams_rfid_3", "ams_rfid_4"}; m_loading_icon = new AnimaIcon(m_book_first_panel, wxID_ANY, list, "refresh_printer", 100); m_loading_icon->SetMinSize(wxSize(FromDIP(25), FromDIP(25))); - m_loading_label = new Label( m_book_first_panel, _L("Loading ...")); - m_loading_label->Wrap( -1 ); + m_loading_label = new Label(m_book_first_panel, _L("Loading ...")); + m_loading_label->Wrap(-1); m_loading_label->SetBackgroundColour(*wxWHITE); - m_loading_sizer->Add(m_loading_icon, 0, wxALL, FromDIP(5)); - m_loading_sizer->Add(m_loading_label, 0, wxALL, FromDIP(5)); + m_loading_sizer->Add(m_loading_icon, 0, wxALIGN_CENTER_VERTICAL, FromDIP(0)); + m_loading_sizer->Add(m_loading_label, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, FromDIP(5)); m_book_first_sizer->Add(0, 0, 1, wxEXPAND, 0); - m_book_first_sizer->Add( m_loading_sizer, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 0); + m_book_first_sizer->Add(m_loading_sizer, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_CENTER_HORIZONTAL, 0); m_book_first_sizer->Add(0, 0, 1, wxEXPAND, 0); - m_book_first_panel->SetSizer( m_book_first_sizer ); - m_book_first_panel->Layout(); - m_book_first_sizer->Fit( m_book_first_panel ); + m_book_first_panel->SetSizer(m_book_first_sizer); + m_book_first_panel->Layout(); + m_book_first_sizer->Fit(m_book_first_panel); - m_simplebook->AddPage( m_book_first_panel, _("loading page"), false ); - m_simplebook->AddPage( m_book_second_panel, _("retry page"), false ); - m_simplebook->AddPage( m_book_third_panel, _("dialog page"), false ); - m_sizer->Add( m_simplebook, 1, wxEXPAND | wxALL, 5 ); + m_simplebook->AddPage(m_book_first_panel, _("loading page"), false); + m_simplebook->AddPage(m_book_second_panel, _("retry page"), false); + m_simplebook->AddPage(m_book_third_panel, _("dialog page"), false); + m_sizer->Add(m_simplebook, 1, wxEXPAND | wxALL, 5); - - SetSizer( m_sizer ); + SetSizer(m_sizer); m_zoom_in_btn->Bind(wxEVT_BUTTON, &PartSkipDialog::OnZoomIn, this); m_zoom_out_btn->Bind(wxEVT_BUTTON, &PartSkipDialog::OnZoomOut, this); m_switch_drag_btn->Bind(wxEVT_BUTTON, &PartSkipDialog::OnSwitchDrag, this); @@ -296,14 +289,15 @@ PartSkipDialog::PartSkipDialog(wxWindow* parent): DPIDialog(parent, wxID_ANY, _L m_apply_btn->Bind(wxEVT_BUTTON, &PartSkipDialog::OnApplyDialog, this); m_all_checkbox->Bind(wxEVT_TOGGLEBUTTON, &PartSkipDialog::OnAllCheckbox, this); - Fit(); Layout(); + Fit(); CentreOnParent(); } PartSkipDialog::~PartSkipDialog() {} -void PartSkipDialog::on_dpi_changed(const wxRect& suggested_rect) { +void PartSkipDialog::on_dpi_changed(const wxRect &suggested_rect) +{ m_canvas->LoadPickImage(m_local_paths[0]); m_loading_icon->SetMinSize(wxSize(FromDIP(25), FromDIP(25))); @@ -332,7 +326,6 @@ void PartSkipDialog::on_dpi_changed(const wxRect& suggested_rect) { m_line->SetMinSize(wxSize(FromDIP(267), 1)); m_line->SetMaxSize(wxSize(FromDIP(267), 1)); - m_apply_btn->SetMinSize(wxSize(FromDIP(80), FromDIP(32))); m_apply_btn->SetCornerRadius(FromDIP(16)); m_apply_btn->Rescale(); @@ -345,18 +338,16 @@ void PartSkipDialog::on_dpi_changed(const wxRect& suggested_rect) { m_second_retry_btn->SetCornerRadius(FromDIP(16)); m_second_retry_btn->Rescale(); - m_all_checkbox->SetMinSize(wxSize(FromDIP(18), FromDIP(18))); m_all_checkbox->Rescale(); for (auto it = m_scroll_sizer->GetChildren().begin(); it != m_scroll_sizer->GetChildren().end(); ++it) { wxSizerItem *item = *it; if (item && item->IsSizer()) { - wxSizer* sizer = item->GetSizer(); - auto check_item = sizer->GetItem((size_t)0); - if (check_item && check_item->IsWindow()) - { - wxWindow *window = check_item->GetWindow(); + wxSizer *sizer = item->GetSizer(); + auto check_item = sizer->GetItem((size_t) 0); + if (check_item && check_item->IsWindow()) { + wxWindow *window = check_item->GetWindow(); CheckBox *checkbox = dynamic_cast(window); checkbox->SetMinSize(wxSize(FromDIP(18), FromDIP(18))); checkbox->Rescale(); @@ -373,8 +364,8 @@ void PartSkipDialog::on_dpi_changed(const wxRect& suggested_rect) { Layout(); } - -std::string PartSkipDialog::create_tmp_path(){ +std::string PartSkipDialog::create_tmp_path() +{ boost::filesystem::path parent_path(temporary_dir()); std::stringstream buf; @@ -386,53 +377,52 @@ std::string PartSkipDialog::create_tmp_path(){ } else { buf << 1 << "_" << 1 << "/"; } - std::string tmp_path = (parent_path / buf.str() ).string(); + std::string tmp_path = (parent_path / buf.str()).string(); - if (!std::filesystem::exists(tmp_path + "Metadata/") && !fs::create_directories(tmp_path + "Metadata/")) { - wxMessageBox("create file failed."); - } + if (!std::filesystem::exists(tmp_path + "Metadata/") && !fs::create_directories(tmp_path + "Metadata/")) { wxMessageBox("create file failed."); } return tmp_path; } - bool PartSkipDialog::is_local_file_existed(const std::vector &local_paths) { for (auto path : local_paths) { - if (!std::filesystem::exists(path)) { - return false; - } + if (!std::filesystem::exists(path)) { return false; } } return true; } void PartSkipDialog::DownloadPartsFile() { - m_tmp_path = create_tmp_path(); //wxGetApp().app_config->get("download_path"); + m_tmp_path = create_tmp_path(); // wxGetApp().app_config->get("download_path"); m_local_paths.clear(); m_target_paths.clear(); - int plate_idx = m_obj ? m_obj->m_plate_index : 1; + m_plate_idx = m_obj ? m_obj->m_plate_index : -1; + if (m_plate_idx < 0) { + m_plate_idx = 1; + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << "part skip: printer plate index is invalid."; + } - m_local_paths.push_back(m_tmp_path + "Metadata/pick_" + std::to_string(plate_idx) + ".png"); + m_local_paths.push_back(m_tmp_path + "Metadata/pick_" + std::to_string(m_plate_idx) + ".png"); m_local_paths.push_back(m_tmp_path + "Metadata/model_settings.config"); m_local_paths.push_back(m_tmp_path + "Metadata/slice_info.config"); - m_target_paths.push_back("Metadata/pick_" + std::to_string(plate_idx) + ".png"); + m_target_paths.push_back("Metadata/pick_" + std::to_string(m_plate_idx) + ".png"); m_target_paths.push_back("Metadata/model_settings.config"); m_target_paths.push_back("Metadata/slice_info.config"); if (!is_local_file_existed(m_local_paths)) { - if(!m_file_sys){ + if (!m_file_sys) { m_file_sys = boost::make_shared(); m_file_sys->Attached(); m_file_sys->Bind(EVT_STATUS_CHANGED, &PartSkipDialog::OnFileSystemEvent, this); m_file_sys->Bind(EVT_RAMDOWNLOAD, &PartSkipDialog::OnFileSystemResult, this); m_file_sys->Start(); - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ <<"part skip: print file system start."; - }else{ + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "part skip: print file system start."; + } else { m_file_sys->Retry(); - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ <<"part skip: print file system retry."; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "part skip: print file system retry."; } } else { m_file_sys->SendExistedFile(); @@ -451,59 +441,57 @@ void PartSkipDialog::fetchUrl(boost::weak_ptr wfs) fs->SetUrl("0"); return; } - std::string dev_ver = obj->get_ota_version(); - std::string dev_id = obj->dev_id; + std::string dev_ver = obj->get_ota_version(); + std::string dev_id = obj->dev_id; // int remote_proto = obj->get_file_remote(); NetworkAgent *agent = wxGetApp().getAgent(); std::string agent_version = agent ? agent->get_version() : ""; auto url_state = m_url_state; - if(obj->is_lan_mode_printer()){ - url_state = URL_TCP; - } + if (obj->is_lan_mode_printer()) { url_state = URL_TCP; } if (agent) { switch (url_state) { - case URL_TCP: { - std::string devIP = obj->dev_ip; - std::string accessCode = obj->get_access_code(); - std::string tcp_url = "bambu:///local/" + devIP + "?port=6000&user=" + "bblp" + "&passwd=" + accessCode; - CallAfter([=] { - boost::shared_ptr fs(wfs.lock()); - if (!fs) return; - if (boost::algorithm::starts_with(tcp_url, "bambu:///")) { - fs->SetUrl(tcp_url); - } else { - fs->SetUrl("3"); - } - }); - break; + case URL_TCP: { + std::string devIP = obj->dev_ip; + std::string accessCode = obj->get_access_code(); + std::string tcp_url = "bambu:///local/" + devIP + "?port=6000&user=" + "bblp" + "&passwd=" + accessCode; + CallAfter([=] { + boost::shared_ptr fs(wfs.lock()); + if (!fs) return; + if (boost::algorithm::starts_with(tcp_url, "bambu:///")) { + fs->SetUrl(tcp_url); + } else { + fs->SetUrl("3"); } - case URL_TUTK: { - std::string protocols[] = {"", "\"tutk\"", "\"agora\"", "\"tutk\",\"agora\""}; - agent->get_camera_url(obj->dev_id + "|" + dev_ver + "|" + protocols[3], [this, wfs, m = dev_id, v = agent->get_version(), dv = dev_ver](std::string url) { - if (boost::algorithm::starts_with(url, "bambu:///")) { - url += "&device=" + m; - url += "&net_ver=" + v; - url += "&dev_ver=" + dv; - url += "&refresh_url=" + boost::lexical_cast(&refresh_agora_url); - url += "&cli_id=" + wxGetApp().app_config->get("slicer_uuid"); - url += "&cli_ver=" + std::string(SLIC3R_VERSION); - } - CallAfter([=] { - boost::shared_ptr fs(wfs.lock()); - if (!fs) return; - if (boost::algorithm::starts_with(url, "bambu:///")) { - fs->SetUrl(url); - } else { - fs->SetUrl("3"); - } - }); - }); - break; + }); + break; + } + case URL_TUTK: { + std::string protocols[] = {"", "\"tutk\"", "\"agora\"", "\"tutk\",\"agora\""}; + agent->get_camera_url(obj->dev_id + "|" + dev_ver + "|" + protocols[3], [this, wfs, m = dev_id, v = agent->get_version(), dv = dev_ver](std::string url) { + if (boost::algorithm::starts_with(url, "bambu:///")) { + url += "&device=" + m; + url += "&net_ver=" + v; + url += "&dev_ver=" + dv; + url += "&refresh_url=" + boost::lexical_cast(&refresh_agora_url); + url += "&cli_id=" + wxGetApp().app_config->get("slicer_uuid"); + url += "&cli_ver=" + std::string(SLIC3R_VERSION); } - default: break; + CallAfter([=] { + boost::shared_ptr fs(wfs.lock()); + if (!fs) return; + if (boost::algorithm::starts_with(url, "bambu:///")) { + fs->SetUrl(url); + } else { + fs->SetUrl("3"); + } + }); + }); + break; + } + default: break; } } } @@ -511,7 +499,7 @@ void PartSkipDialog::fetchUrl(boost::weak_ptr wfs) void PartSkipDialog::OnFileSystemEvent(wxCommandEvent &e) { e.Skip(); - auto wfs = boost::weak_ptr(m_file_sys); + auto wfs = boost::weak_ptr(m_file_sys); boost::shared_ptr fs(wfs.lock()); if (!fs) return; @@ -520,25 +508,25 @@ void PartSkipDialog::OnFileSystemEvent(wxCommandEvent &e) // int extra = e.GetExtraLong(); switch (status) { - case PrinterFileSystem::Initializing: - case PrinterFileSystem::Connecting: break; - case PrinterFileSystem::ListSyncing: { - m_file_sys->GetPickImages(m_local_paths, m_target_paths); - break; + case PrinterFileSystem::Initializing: + case PrinterFileSystem::Connecting: break; + case PrinterFileSystem::ListSyncing: { + m_file_sys->GetPickImages(m_local_paths, m_target_paths); + break; + } + case PrinterFileSystem::Failed: { + m_file_sys->Stop(); + if (m_url_state == URL_TCP) { + m_url_state = URL_TUTK; + m_file_sys->Retry(); + BOOST_LOG_TRIVIAL(info) << "part skip: print file system connnect failed first."; + } else { + m_file_sys->SendConnectFail(); + BOOST_LOG_TRIVIAL(info) << "part skip: print file system connnect failed second."; } - case PrinterFileSystem::Failed: { - m_file_sys->Stop(); - if( m_url_state == URL_TCP){ - m_url_state = URL_TUTK; - m_file_sys->Retry(); - BOOST_LOG_TRIVIAL(info) << "part skip: print file system connnect failed first."; - }else{ - m_file_sys->SendConnectFail(); - BOOST_LOG_TRIVIAL(info) << "part skip: print file system connnect failed second."; - } - break; - } - case PrinterFileSystem::Reconnecting: break; + break; + } + case PrinterFileSystem::Reconnecting: break; } if (e.GetInt() == PrinterFileSystem::Initializing) { CallAfter([=] { @@ -551,60 +539,61 @@ void PartSkipDialog::OnFileSystemEvent(wxCommandEvent &e) } // reseter: [TCP -> TUTK(TCP)] -> [TCP -> TUTK(TCP)] -void PartSkipDialog::OnFileSystemResult(wxCommandEvent &event){ +void PartSkipDialog::OnFileSystemResult(wxCommandEvent &event) +{ int result = event.GetInt(); m_loading_icon->Stop(); if (result == 0) { InitDialogUI(); SetSimplebookPage(2); m_file_sys->Stop(); - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ <<"part skip: on file system result success."; - }else{ + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "part skip: on file system result success."; + } else { m_url_state = URL_TCP; SetSimplebookPage(1); m_file_sys->Stop(); - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ <<"part skip: on file system result failed."; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "part skip: on file system result failed."; } } -void PartSkipDialog::InitSchedule(MachineObject *obj){ +void PartSkipDialog::InitSchedule(MachineObject *obj) +{ m_obj = obj; SetSimplebookPage(0); m_loading_icon->Play(); DownloadPartsFile(); } -void PartSkipDialog::OnRetryButton(wxCommandEvent &event) { +void PartSkipDialog::OnRetryButton(wxCommandEvent &event) +{ event.Skip(); InitSchedule(m_obj); Refresh(); } +bool PartSkipDialog::is_drag_mode() { return m_is_drag == true; } -bool PartSkipDialog::is_drag_mode(){ - return m_is_drag == true; -} - -PartsInfo PartSkipDialog::GetPartsInfo(){ +PartsInfo PartSkipDialog::GetPartsInfo() +{ PartsInfo parts_info; - for (auto [part_id, part_state] : this->m_parts_state) - { - parts_info.push_back(std::pair(part_id, part_state)); - } + for (auto [part_id, part_state] : this->m_parts_state) { parts_info.push_back(std::pair(part_id, part_state)); } return parts_info; } -void PartSkipDialog::OnZoomIn(wxCommandEvent &event){ +void PartSkipDialog::OnZoomIn(wxCommandEvent &event) +{ m_canvas->ZoomIn(20); UpdateZoomPercent(); } -void PartSkipDialog::OnZoomOut(wxCommandEvent &event){ +void PartSkipDialog::OnZoomOut(wxCommandEvent &event) +{ m_canvas->ZoomOut(20); UpdateZoomPercent(); } -void PartSkipDialog::OnSwitchDrag(wxCommandEvent& event){ +void PartSkipDialog::OnSwitchDrag(wxCommandEvent &event) +{ if (this->is_drag_mode()) { m_is_drag = false; m_switch_drag_btn->SetBackgroundColor(*wxWHITE); @@ -617,7 +606,8 @@ void PartSkipDialog::OnSwitchDrag(wxCommandEvent& event){ m_canvas->SwitchDrag(m_is_drag); } -void PartSkipDialog::OnZoomPercent(wxCommandEvent &event) { +void PartSkipDialog::OnZoomPercent(wxCommandEvent &event) +{ m_zoom_percent = event.GetInt(); if (m_zoom_percent >= 1000) { m_zoom_percent = 1000; @@ -637,25 +627,21 @@ void PartSkipDialog::OnZoomPercent(wxCommandEvent &event) { UpdateZoomPercent(); } -void PartSkipDialog::UpdatePartsStateFromCanvas(wxCommandEvent &event) { - int part_id = event.GetExtraLong(); +void PartSkipDialog::UpdatePartsStateFromCanvas(wxCommandEvent &event) +{ + int part_id = event.GetExtraLong(); PartState part_state = PartState(event.GetInt()); m_parts_state[part_id] = part_state; - if(part_state == psUnCheck){ - m_all_checkbox->SetValue(false); - } - if(IsAllChecked()){ - m_all_checkbox->SetValue(true); - } + if (part_state == psUnCheck) { m_all_checkbox->SetValue(false); } + if (IsAllChecked()) { m_all_checkbox->SetValue(true); } UpdateDialogUI(); } -void PartSkipDialog::UpdateZoomPercent() { - m_percent_label->SetLabel(wxString::Format(_L("%d%%"), m_zoom_percent)); -} +void PartSkipDialog::UpdateZoomPercent() { m_percent_label->SetLabel(wxString::Format(_L("%d%%"), m_zoom_percent)); } -void PartSkipDialog::UpdateCountLabel() { +void PartSkipDialog::UpdateCountLabel() +{ int check_cnt = 0; int tot_cnt = 0; for (auto [part_id, part_state] : m_parts_state) { @@ -678,7 +664,8 @@ bool PartSkipDialog::Show(bool show) return DPIDialog::Show(show); } -void PartSkipDialog::InitDialogUI() { +void PartSkipDialog::InitDialogUI() +{ m_print_lock = true; BOOST_LOG_TRIVIAL(info) << "part skip: lock parts info from printer."; m_scroll_sizer->Clear(true); @@ -686,7 +673,7 @@ void PartSkipDialog::InitDialogUI() { m_parts_state.clear(); m_parts_name.clear(); - string pick_img = m_local_paths[0]; + string pick_img = m_local_paths[0]; string slice_info = m_local_paths[2]; m_switch_drag_btn->SetIcon("canvas_drag"); @@ -702,40 +689,40 @@ void PartSkipDialog::InitDialogUI() { ModelSettingHelper helper(slice_info); if (helper.Parse()) { - int plate_idx = m_obj ? m_obj->m_plate_index : 0; - auto parse_result = helper.GetPlates()[plate_idx - 1].objects; - for (const auto& part : parse_result) { + auto parse_result = helper.GetPlateObjects(m_plate_idx); + for (const auto &part : parse_result) { m_parts_state[part.identify_id] = part.state; - m_parts_name[part.identify_id] = part.name; + m_parts_name[part.identify_id] = part.name; } if (m_obj) { std::vector partskip_ids = m_obj->m_partskip_ids; - for (auto part_id : partskip_ids) { - m_parts_state[part_id] = PartState::psSkipped; - } + for (auto part_id : partskip_ids) { m_parts_state[part_id] = PartState::psSkipped; } } - for(const auto& [part_id, part_state] : m_parts_state) { + for (const auto &[part_id, part_state] : m_parts_state) { auto line_sizer = new wxBoxSizer(wxHORIZONTAL); auto checkbox = new CheckBox(m_list_view); auto label = new Label(m_list_view, wxEmptyString); - checkbox->Bind(wxEVT_TOGGLEBUTTON, [this, part_id=part_id](wxCommandEvent& event) { - m_parts_state[part_id] = event.IsChecked() ? PartState::psChecked : PartState::psUnCheck; - if(!event.IsChecked()){ - m_all_checkbox->SetValue(false); - }else if(IsAllChecked()){ - m_all_checkbox->SetValue(true); - } - m_canvas->UpdatePartsInfo(GetPartsInfo()); - UpdateCountLabel(); - event.Skip(); - }, checkbox->GetId()); + checkbox->Bind( + wxEVT_TOGGLEBUTTON, + [this, part_id = part_id](wxCommandEvent &event) { + m_parts_state[part_id] = event.IsChecked() ? PartState::psChecked : PartState::psUnCheck; + if (!event.IsChecked()) { + m_all_checkbox->SetValue(false); + } else if (IsAllChecked()) { + m_all_checkbox->SetValue(true); + } + m_canvas->UpdatePartsInfo(GetPartsInfo()); + UpdateCountLabel(); + event.Skip(); + }, + checkbox->GetId()); if (part_state == PartState::psChecked) { checkbox->SetValue(true); checkbox->Enable(true); - } else if(part_state == PartState::psUnCheck) { + } else if (part_state == PartState::psUnCheck) { checkbox->SetValue(false); checkbox->Enable(true); } else if (part_state == PartState::psSkipped) { @@ -744,14 +731,14 @@ void PartSkipDialog::InitDialogUI() { } label->SetLabel(wxString::FromUTF8(m_parts_name[part_id])); label->SetBackgroundColour(*wxWHITE); - label->SetForegroundColour(wxColor(107,107,107)); + label->SetForegroundColour(wxColor(107, 107, 107)); label->Wrap(-1); label->SetMinSize(wxSize(-1, FromDIP(18))); checkbox->SetBackgroundColour(*wxWHITE); checkbox->SetMinSize(wxSize(FromDIP(18), FromDIP(18))); checkbox->SetMaxSize(wxSize(FromDIP(18), FromDIP(18))); - line_sizer->Add(checkbox, 0, wxALIGN_CENTER_VERTICAL, 0); + line_sizer->Add(checkbox, 0, wxALIGN_CENTER_VERTICAL, 0); line_sizer->Add(label, 1, wxLEFT | wxALIGN_CENTER_VERTICAL, FromDIP(8)); m_scroll_sizer->Add(line_sizer, 0, wxBOTTOM | wxEXPAND, FromDIP(12)); } @@ -766,47 +753,47 @@ void PartSkipDialog::InitDialogUI() { BOOST_LOG_TRIVIAL(info) << "part skip: unlock parts info from printer."; } -void PartSkipDialog::UpdatePartsStateFromPrinter(MachineObject *obj) { +void PartSkipDialog::UpdatePartsStateFromPrinter(MachineObject *obj) +{ if (m_print_lock) { BOOST_LOG_TRIVIAL(info) << "part skip: parts info from printer is locked."; return; } m_obj = obj; if (m_obj) { - bool update_flag = false; + bool update_flag = false; std::vector partskip_ids = m_obj->m_partskip_ids; - for(auto part_id : partskip_ids) { - if(m_parts_state[part_id] != PartState::psSkipped){ + for (auto part_id : partskip_ids) { + if (m_parts_state[part_id] != PartState::psSkipped) { m_parts_state[part_id] = PartState::psSkipped; - update_flag = true; + update_flag = true; } } - if(update_flag){ + if (update_flag) { m_canvas->UpdatePartsInfo(GetPartsInfo()); UpdateDialogUI(); } } } - -void PartSkipDialog::UpdateDialogUI(){ - if(m_parts_state.size() != m_scroll_sizer->GetItemCount()){ +void PartSkipDialog::UpdateDialogUI() +{ + if (m_parts_state.size() != m_scroll_sizer->GetItemCount()) { BOOST_LOG_TRIVIAL(warning) << "part skip: m_parts_state and m_scroll_sizer mismatch."; return; } for (auto it = m_parts_state.begin(); it != m_parts_state.end(); ++it) { - int idx = std::distance(m_parts_state.begin(), it); + int idx = std::distance(m_parts_state.begin(), it); auto part_state = it->second; wxSizerItem *item = m_scroll_sizer->GetItem(idx); if (item && item->IsSizer()) { - wxSizer* sizer = item->GetSizer(); - auto check_item = sizer->GetItem((size_t)0); + wxSizer *sizer = item->GetSizer(); + auto check_item = sizer->GetItem((size_t) 0); - if (check_item && check_item->IsWindow()) - { - wxWindow *window = check_item->GetWindow(); + if (check_item && check_item->IsWindow()) { + wxWindow *window = check_item->GetWindow(); CheckBox *checkbox = dynamic_cast(window); if (part_state == PartState::psChecked) { checkbox->SetValue(true); @@ -824,14 +811,12 @@ void PartSkipDialog::UpdateDialogUI(){ Refresh(); } -void PartSkipDialog::SetSimplebookPage(int page) { - m_simplebook->SetSelection(page); -} +void PartSkipDialog::SetSimplebookPage(int page) { m_simplebook->SetSelection(page); } -bool PartSkipDialog::IsAllChecked() { - for (auto& [part_id, part_state] : m_parts_state) { - if (part_state == PartState::psUnCheck) - return false; +bool PartSkipDialog::IsAllChecked() +{ + for (auto &[part_id, part_state] : m_parts_state) { + if (part_state == PartState::psUnCheck) return false; } return true; } @@ -839,15 +824,12 @@ bool PartSkipDialog::IsAllChecked() { void PartSkipDialog::OnAllCheckbox(wxCommandEvent &event) { if (m_all_checkbox->GetValue()) { - for (auto& [part_id, part_state] : m_parts_state) { - if (part_state == PartState::psUnCheck) - part_state = PartState::psChecked; + for (auto &[part_id, part_state] : m_parts_state) { + if (part_state == PartState::psUnCheck) part_state = PartState::psChecked; } - } - else{ - for (auto& [part_id, part_state] : m_parts_state) { - if (part_state == PartState::psChecked) - part_state = PartState::psUnCheck; + } else { + for (auto &[part_id, part_state] : m_parts_state) { + if (part_state == PartState::psChecked) part_state = PartState::psUnCheck; } } m_canvas->UpdatePartsInfo(GetPartsInfo()); @@ -855,15 +837,12 @@ void PartSkipDialog::OnAllCheckbox(wxCommandEvent &event) event.Skip(); } - void PartSkipDialog::OnApplyDialog(wxCommandEvent &event) { event.Skip(); m_partskip_ids.clear(); - for (const auto& [part_id, part_state] : m_parts_state) { - if (part_state == PartState::psChecked) { - m_partskip_ids.push_back(part_id); - } + for (const auto &[part_id, part_state] : m_parts_state) { + if (part_state == PartState::psChecked) { m_partskip_ids.push_back(part_id); } } bool all_skipped = true; @@ -872,17 +851,17 @@ void PartSkipDialog::OnApplyDialog(wxCommandEvent &event) } PartSkipConfirmDialog confirm_dialog(this); - if (all_skipped){ + if (all_skipped) { confirm_dialog.SetMsgLabel(_L("Skipping all objects.")); confirm_dialog.SetTipLabel(_L("The printing job will be stopped. Continue?")); - }else{ + } else { confirm_dialog.SetMsgLabel(wxString::Format(_L("Skipping %d objects."), m_partskip_ids.size())); confirm_dialog.SetTipLabel(_L("This action cannot be undone. Continue?")); } - if(confirm_dialog.ShowModal() == wxID_OK){ + if (confirm_dialog.ShowModal() == wxID_OK) { if (m_obj) { - BOOST_LOG_TRIVIAL(info) << "part skip: skipping "<< m_partskip_ids.size() <<" objects."; + BOOST_LOG_TRIVIAL(info) << "part skip: skipping " << m_partskip_ids.size() << " objects."; if (all_skipped) { m_obj->command_task_abort(); @@ -898,11 +877,11 @@ void PartSkipDialog::OnApplyDialog(wxCommandEvent &event) } } -int PartSkipDialog::GetAllSkippedPartsNum() { +int PartSkipDialog::GetAllSkippedPartsNum() +{ int skipped_cnt = 0; - for (auto& [part_id, part_state] : m_parts_state) { - if (part_state == PartState::psSkipped || part_state == PartState::psChecked) - skipped_cnt++; + for (auto &[part_id, part_state] : m_parts_state) { + if (part_state == PartState::psSkipped || part_state == PartState::psChecked) skipped_cnt++; } return skipped_cnt; } @@ -915,8 +894,8 @@ PartSkipConfirmDialog::PartSkipConfirmDialog(wxWindow *parent) : DPIDialog(paren SetMinSize(wxSize(FromDIP(480), FromDIP(215))); SetSizeHints(wxDefaultSize, wxDefaultSize); - wxBoxSizer* m_sizer; - m_sizer = new wxBoxSizer( wxVERTICAL ); + wxBoxSizer *m_sizer; + m_sizer = new wxBoxSizer(wxVERTICAL); auto m_line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL); m_line_top->SetMinSize(wxSize(FromDIP(480), 1)); @@ -925,24 +904,23 @@ PartSkipConfirmDialog::PartSkipConfirmDialog(wxWindow *parent) : DPIDialog(paren m_sizer->Add(m_line_top, 0, wxEXPAND, 0); m_sizer->Add(0, 0, 0, wxALL, FromDIP(15)); - m_msg_label = new Label( this, _L("Skipping objects.")); - m_msg_label->Wrap( -1 ); + m_msg_label = new Label(this, _L("Skipping objects.")); + m_msg_label->Wrap(-1); m_msg_label->SetBackgroundColour(*wxWHITE); m_tip_label = new Label(this, _L("This action cannot be undone. Continue?")); m_tip_label->Wrap(-1); m_tip_label->SetBackgroundColour(*wxWHITE); - m_tip_label->SetForegroundColour(wxColor(92,92,92)); + m_tip_label->SetForegroundColour(wxColor(92, 92, 92)); m_sizer->Add(m_msg_label, 0, wxLEFT, FromDIP(29)); m_sizer->Add(0, 0, 0, wxTOP, FromDIP(9)); m_sizer->Add(m_tip_label, 0, wxLEFT, FromDIP(29)); - wxBoxSizer* m_button_sizer; + wxBoxSizer *m_button_sizer; m_button_sizer = new wxBoxSizer(wxHORIZONTAL); m_button_sizer->SetMinSize(wxSize(FromDIP(480), FromDIP(54))); - m_button_sizer->Add(0, 0, 1, wxEXPAND, 0); - + m_button_sizer->Add(0, 0, 1, wxEXPAND, 0); StateColor btn_bg_white(std::pair(wxColour(206, 206, 206), StateColor::Pressed), std::pair(wxColour(238, 238, 238), StateColor::Hovered), std::pair(*wxWHITE, StateColor::Normal)); @@ -958,23 +936,21 @@ PartSkipConfirmDialog::PartSkipConfirmDialog(wxWindow *parent) : DPIDialog(paren m_apply_button->SetSize(wxSize(FromDIP(80), FromDIP(32))); m_apply_button->SetMinSize(wxSize(FromDIP(80), FromDIP(32))); m_apply_button->SetCornerRadius(FromDIP(16)); - m_apply_button->Bind(wxEVT_BUTTON, [this](auto& e){ + m_apply_button->Bind(wxEVT_BUTTON, [this](auto &e) { EndModal(wxID_OK); e.Skip(); }); m_button_sizer->Add(m_apply_button, 0, wxRIGHT | wxBOTTOM, FromDIP(24)); - m_sizer->Add(m_button_sizer, 1, wxEXPAND, FromDIP(5)); - m_sizer->Fit( this ); + m_sizer->Add(m_button_sizer, 1, wxEXPAND, FromDIP(5)); + m_sizer->Fit(this); - SetSizer( m_sizer ); + SetSizer(m_sizer); Layout(); Fit(); } -PartSkipConfirmDialog::~PartSkipConfirmDialog() -{ -} +PartSkipConfirmDialog::~PartSkipConfirmDialog() {} bool PartSkipConfirmDialog::Show(bool show) { @@ -988,7 +964,8 @@ bool PartSkipConfirmDialog::Show(bool show) return DPIDialog::Show(show); } -void PartSkipConfirmDialog::on_dpi_changed(const wxRect& suggested_rect) { +void PartSkipConfirmDialog::on_dpi_changed(const wxRect &suggested_rect) +{ m_apply_button->SetMinSize(wxSize(FromDIP(80), FromDIP(32))); m_apply_button->SetCornerRadius(FromDIP(16)); m_apply_button->Rescale(); @@ -996,20 +973,10 @@ void PartSkipConfirmDialog::on_dpi_changed(const wxRect& suggested_rect) { Fit(); } +Button *PartSkipConfirmDialog::GetConfirmButton() { return m_apply_button; } -Button* PartSkipConfirmDialog::GetConfirmButton() -{ - return m_apply_button; -} +void PartSkipConfirmDialog::SetMsgLabel(wxString msg) { m_msg_label->SetLabel(msg); } -void PartSkipConfirmDialog::SetMsgLabel(wxString msg){ - m_msg_label->SetLabel(msg); -} +void PartSkipConfirmDialog::SetTipLabel(wxString msg) { m_tip_label->SetLabel(msg); } -void PartSkipConfirmDialog::SetTipLabel(wxString msg){ - m_tip_label->SetLabel(msg); -} - - -} -} // namespace Slic3r::GUI +}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/PartSkipDialog.hpp b/src/slic3r/GUI/PartSkipDialog.hpp index ef5cb743af..92abbed0b9 100644 --- a/src/slic3r/GUI/PartSkipDialog.hpp +++ b/src/slic3r/GUI/PartSkipDialog.hpp @@ -38,19 +38,19 @@ class PartSkipConfirmDialog : public DPIDialog { private: protected: - Label *m_msg_label; - Label *m_tip_label; - Button *m_apply_button; + Label *m_msg_label; + Label *m_tip_label; + Button *m_apply_button; public: PartSkipConfirmDialog(wxWindow *parent); ~PartSkipConfirmDialog(); - void on_dpi_changed(const wxRect &suggested_rect); - Button* GetConfirmButton(); - void SetMsgLabel(wxString msg); - void SetTipLabel(wxString msg); - bool Show(bool show); + void on_dpi_changed(const wxRect &suggested_rect); + Button *GetConfirmButton(); + void SetMsgLabel(wxString msg); + void SetTipLabel(wxString msg); + bool Show(bool show); }; class PartSkipDialog : public DPIDialog @@ -69,68 +69,69 @@ public: MachineObject *m_obj{nullptr}; - wxSimplebook* m_simplebook; - wxPanel* m_book_third_panel; - wxPanel* m_book_second_panel; - wxPanel* m_book_first_panel; + wxSimplebook *m_simplebook; + wxPanel *m_book_third_panel; + wxPanel *m_book_second_panel; + wxPanel *m_book_first_panel; - SkipPartCanvas* m_canvas; - Button* m_zoom_in_btn; - Button* m_zoom_out_btn; - Button* m_switch_drag_btn; - CheckBox* m_all_checkbox; - Button* m_percent_label; - Label* m_all_label; - wxPanel* m_line; - wxPanel* m_line_top; - wxScrolledWindow* m_list_view; + SkipPartCanvas *m_canvas; + Button *m_zoom_in_btn; + Button *m_zoom_out_btn; + Button *m_switch_drag_btn; + CheckBox *m_all_checkbox; + Button *m_percent_label; + Label *m_all_label; + wxPanel *m_line; + wxPanel *m_line_top; + wxScrolledWindow *m_list_view; - wxPanel* m_dlg_placeholder; - Label* m_cnt_label; - Label* m_tot_label; + wxPanel *m_dlg_placeholder; + Label *m_cnt_label; + Label *m_tot_label; - Button* m_apply_btn; + Button *m_apply_btn; - Label* m_loading_label; - Label* m_retry_label; - ScalableBitmap* m_retry_icon; - wxStaticBitmap* m_retry_bitmap; + Label *m_loading_label; + Label *m_retry_label; + ScalableBitmap *m_retry_icon; + wxStaticBitmap *m_retry_bitmap; - wxBoxSizer* m_sizer; - wxBoxSizer* m_dlg_sizer; - wxBoxSizer* m_dlg_content_sizer; - wxBoxSizer* m_dlg_btn_sizer; - wxBoxSizer* m_canvas_sizer; - wxBoxSizer* m_canvas_btn_sizer; - wxBoxSizer* m_list_sizer; - wxBoxSizer* m_scroll_sizer; - wxBoxSizer* m_book_first_sizer; - wxBoxSizer* m_book_second_sizer; - wxBoxSizer* m_book_second_btn_sizer; - Button* m_second_retry_btn; - AnimaIcon* m_loading_icon; + wxBoxSizer *m_sizer; + wxBoxSizer *m_dlg_sizer; + wxBoxSizer *m_dlg_content_sizer; + wxBoxSizer *m_dlg_btn_sizer; + wxBoxSizer *m_canvas_sizer; + wxBoxSizer *m_canvas_btn_sizer; + wxBoxSizer *m_list_sizer; + wxBoxSizer *m_scroll_sizer; + wxBoxSizer *m_book_first_sizer; + wxBoxSizer *m_book_second_sizer; + wxBoxSizer *m_book_second_btn_sizer; + Button *m_second_retry_btn; + AnimaIcon *m_loading_icon; private: - int m_zoom_percent{100}; + int m_plate_idx{-1}; + int m_zoom_percent{100}; bool m_is_drag{false}; bool m_print_lock{true}; - std::map m_parts_state; + std::map m_parts_state; std::map m_parts_name; - std::vector m_partskip_ids; + std::vector m_partskip_ids; enum URL_STATE m_url_state = URL_STATE::URL_TCP; PartsInfo GetPartsInfo(); - bool is_drag_mode(); + bool is_drag_mode(); boost::shared_ptr m_file_sys; - bool m_file_sys_result{false}; - std::string m_timestamp; - std::string m_tmp_path; - std::vector m_local_paths; - std::vector m_target_paths; - std::string create_tmp_path(); + bool m_file_sys_result{false}; + std::string m_timestamp; + std::string m_tmp_path; + std::vector m_local_paths; + std::vector m_target_paths; + std::string create_tmp_path(); bool is_local_file_existed(const std::vector &local_paths); @@ -139,7 +140,6 @@ private: void OnFileSystemResult(wxCommandEvent &event); void fetchUrl(boost::weak_ptr wfs); - void OnZoomIn(wxCommandEvent &event); void OnZoomOut(wxCommandEvent &event); void OnSwitchDrag(wxCommandEvent &event); @@ -156,4 +156,4 @@ private: void OnApplyDialog(wxCommandEvent &event); }; -}} \ No newline at end of file +}} // namespace Slic3r::GUI \ No newline at end of file diff --git a/src/slic3r/GUI/Printer/PrinterFileSystem.cpp b/src/slic3r/GUI/Printer/PrinterFileSystem.cpp index a48310254d..d961971d0e 100644 --- a/src/slic3r/GUI/Printer/PrinterFileSystem.cpp +++ b/src/slic3r/GUI/Printer/PrinterFileSystem.cpp @@ -252,6 +252,7 @@ struct PrinterFileSystem::Download : Progress void PrinterFileSystem::GetPickImages(const std::vector &local_paths, const std::vector &targetpaths) { + m_download_states.clear(); GetPickImage(1, local_paths[0], targetpaths[0]); GetPickImage(2, local_paths[1], targetpaths[1]); @@ -303,6 +304,9 @@ void PrinterFileSystem::DownloadRamFile(int index, const std::string &local_path wxLogWarning("Download failed: result = 1"); return ERROR_JSON; } + if(mem_dl_json.contains("size") && mem_dl_json["size"] == 0 ) + return FILE_SIZE_ERR; + return CONTINUE; } @@ -355,13 +359,27 @@ void PrinterFileSystem::DownloadRamFile(int index, const std::string &local_path if (result == CONTINUE) { return; } std::string msg; if (result == SUCCESS) { - wxLogMessage("DownloadImageFromRam finished: %s", download->local_path); - msg = "SUCCESS"; - SendChangedEvent(EVT_RAMDOWNLOAD, result, result ? download->error : download->local_path); + if (std::filesystem::exists(download->local_path)) { + m_download_states.emplace_back(true); + BOOST_LOG_TRIVIAL(info) <<"DownloadImageFromRam finished: " << download->local_path << "result = " << result; + }else{ + m_download_states.emplace_back(false); + BOOST_LOG_TRIVIAL(warning) <<"DownloadImageFromRam finished, but file not exist: " << download->local_path << "result = " << result; + } } else if (result != CONTINUE) { - wxLogWarning("DownloadImageFromRam failed: %s", download->error); - msg = "ERROR"; - SendChangedEvent(EVT_RAMDOWNLOAD, result, result ? download->error : download->local_path); + m_download_states.emplace_back(false); + BOOST_LOG_TRIVIAL(warning) << "DownloadImageFromRam failed: " << download->error << "result = " << result; + } + + if(m_download_states.size() == 3){ + if(m_download_states[0] && m_download_states[1] && m_download_states[2]){ + SendChangedEvent(EVT_RAMDOWNLOAD, SUCCESS); + }else{ + // FILE_NO_EXIST is not really error_code + SendChangedEvent(EVT_RAMDOWNLOAD, FILE_NO_EXIST); + } + }else{ + BOOST_LOG_TRIVIAL(warning) << "m_download_states current size is : " << m_download_states.size(); } },param); } diff --git a/src/slic3r/GUI/Printer/PrinterFileSystem.h b/src/slic3r/GUI/Printer/PrinterFileSystem.h index a94a5e3e96..259baf7e45 100644 --- a/src/slic3r/GUI/Printer/PrinterFileSystem.h +++ b/src/slic3r/GUI/Printer/PrinterFileSystem.h @@ -317,6 +317,8 @@ private: size_t m_lock_end = 0; int m_task_flags = 0; + std::vector m_download_states; + private: struct Session { diff --git a/src/slic3r/GUI/SkipPartCanvas.cpp b/src/slic3r/GUI/SkipPartCanvas.cpp index 85c2cc6c6d..75b99d4cc5 100644 --- a/src/slic3r/GUI/SkipPartCanvas.cpp +++ b/src/slic3r/GUI/SkipPartCanvas.cpp @@ -9,6 +9,7 @@ #include #include #include +#include wxDEFINE_EVENT(EVT_ZOOM_PERCENT, wxCommandEvent); wxDEFINE_EVENT(EVT_CANVAS_PART, wxCommandEvent); @@ -32,6 +33,8 @@ SkipPartCanvas::SkipPartCanvas(wxWindow *parent, const wxGLAttributes& dispAttrs void SkipPartCanvas::LoadPickImage(const std::string & path) { + if(!std::filesystem::exists(path)) return; + auto ParseShapeId = [](cv::Mat image, const std::vector> &contours, const std::vector &hierarchy, int root_idx) -> uint32_t { cv::Mat mask = cv::Mat::zeros(image.size(), CV_8UC1); @@ -249,8 +252,12 @@ void SkipPartCanvas::Render() int w, h; GetClientSize(&w, &h); +#if defined(__APPLE__) + double scale = GetDPIScaleFactor(); + glViewport(0, 0, w * scale, h * scale); +#else glViewport(0, 0, w, h); - +#endif glMatrixMode(GL_PROJECTION); glLoadIdentity(); auto view_rect = ViewPtToImagePt(wxPoint(w, h)); @@ -661,7 +668,14 @@ void XMLCALL ModelSettingHelper::EndElementHandler(void *userData, const XML_Cha } } -std::vector ModelSettingHelper::GetPlates() { return context_.plates; } +std::vector ModelSettingHelper::GetPlateObjects(int plate_idx) { + for (const auto &plate : context_.plates) { + if (plate.index == plate_idx) { + return plate.objects; + } + } + return std::vector(); +} void ModelSettingHelper::DataHandler(const XML_Char *s, int len) { diff --git a/src/slic3r/GUI/SkipPartCanvas.hpp b/src/slic3r/GUI/SkipPartCanvas.hpp index 77dd4ffa42..038b53d371 100644 --- a/src/slic3r/GUI/SkipPartCanvas.hpp +++ b/src/slic3r/GUI/SkipPartCanvas.hpp @@ -150,7 +150,7 @@ public: ModelSettingHelper(const std::string &path); bool Parse(); - std::vector GetPlates(); + std::vector GetPlateObjects(int plate_idx); private: std::string path_; diff --git a/src/slic3r/GUI/Widgets/AnimaController.cpp b/src/slic3r/GUI/Widgets/AnimaController.cpp index d034fe9fd8..4cd110342f 100644 --- a/src/slic3r/GUI/Widgets/AnimaController.cpp +++ b/src/slic3r/GUI/Widgets/AnimaController.cpp @@ -9,12 +9,13 @@ AnimaIcon::AnimaIcon(wxWindow *parent, wxWindowID id, std::vector img_list, std::string img_enable, int ivt) : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize), m_ivt(ivt) { + auto sizer = new wxBoxSizer(wxHORIZONTAL); SetBackgroundColour((wxColour(255, 255, 255))); - m_size = 20; + m_size = 25; //add ScalableBitmap - for (const auto &filename : img_list) m_images.emplace_back(create_scaled_bitmap(filename, this, FromDIP(m_size))); - m_image_enable = create_scaled_bitmap(img_enable, this, FromDIP(m_size-8)); + for (const auto &filename : img_list) m_images.emplace_back(create_scaled_bitmap(filename, this, m_size)); + m_image_enable = create_scaled_bitmap(img_enable, this, m_size-8); // show first wxStaticBitmap if (!m_images.empty()) m_bitmap = new wxStaticBitmap(this, wxID_ANY, m_images[0], wxDefaultPosition, wxSize(FromDIP(m_size), FromDIP(m_size))); @@ -47,12 +48,13 @@ AnimaIcon::AnimaIcon(wxWindow *parent, wxWindowID id, std::vector i SetCursor(wxCursor(wxCURSOR_ARROW)); e.Skip(); }); - + sizer->Add(m_bitmap, 0, wxALIGN_CENTER, 0); + SetSizer(sizer); SetSize(wxSize(FromDIP(m_size), FromDIP(m_size))); SetMaxSize(wxSize(FromDIP(m_size), FromDIP(m_size))); SetMinSize(wxSize(FromDIP(m_size), FromDIP(m_size))); - Refresh(); - + Layout(); + Fit(); Play(); } From ddf6ed00c5c31e7b4a6697f333bb467548bd2d75 Mon Sep 17 00:00:00 2001 From: hemai Date: Wed, 9 Jul 2025 17:57:17 +0800 Subject: [PATCH 10/27] ENH: if nothing selected then disable aplly btn && update plate index parse Jira: [STUDIO-13299] Change-Id: I1ce7271d005f7fec4d71ece9db13916a9fcc1e1d (cherry picked from commit b2386964814dee68a1936da912eea339f5317ddc) --- src/slic3r/GUI/DeviceManager.cpp | 2 +- src/slic3r/GUI/PartSkipDialog.cpp | 83 ++++++++++++++++++++++--------- src/slic3r/GUI/PartSkipDialog.hpp | 2 + 3 files changed, 63 insertions(+), 24 deletions(-) diff --git a/src/slic3r/GUI/DeviceManager.cpp b/src/slic3r/GUI/DeviceManager.cpp index 396ed8e17b..7a6629c456 100644 --- a/src/slic3r/GUI/DeviceManager.cpp +++ b/src/slic3r/GUI/DeviceManager.cpp @@ -2988,7 +2988,7 @@ int MachineObject::parse_json(std::string payload, bool key_field_only) } } } - if (j_pre["print"].contains("plate_idx") && m_plate_index == -1){ + if (j_pre["print"].contains("plate_idx")){ // && m_plate_index == -1 m_plate_index = j_pre["print"]["plate_idx"].get(); } } diff --git a/src/slic3r/GUI/PartSkipDialog.cpp b/src/slic3r/GUI/PartSkipDialog.cpp index 9b927aa594..ca8eac7ea2 100644 --- a/src/slic3r/GUI/PartSkipDialog.cpp +++ b/src/slic3r/GUI/PartSkipDialog.cpp @@ -32,6 +32,33 @@ namespace Slic3r { namespace GUI { extern wxString hide_passwd(wxString url, std::vector const &passwords); extern void refresh_agora_url(char const *device, char const *dev_ver, char const *channel, void *context, void (*callback)(void *context, char const *url)); +StateColor percent_bg(std::pair(wxColour(255, 255, 255), StateColor::Disabled), + std::pair(wxColour(255, 255, 255), StateColor::Pressed), + std::pair(wxColour(255, 255, 255), StateColor::Hovered), + std::pair(wxColour(255, 255, 255), StateColor::Enabled), + std::pair(wxColour(255, 255, 255), StateColor::Normal)); + +static StateColor zoom_bg(std::pair(wxColour(255, 255, 255), StateColor::Disabled), + std::pair(wxColour(206, 206, 206), StateColor::Pressed), + std::pair(wxColour(238, 238, 238), StateColor::Hovered), + std::pair(wxColour(255, 255, 255), StateColor::Enabled), + std::pair(wxColour(255, 255, 255), StateColor::Normal)); + +static StateColor zoom_bd(std::pair(wxColour(144, 144, 144), StateColor::Disabled), std::pair(wxColour(38, 46, 48), StateColor::Enabled)); +static StateColor zoom_text(std::pair(wxColour(144, 144, 144), StateColor::Disabled), std::pair(wxColour(38, 46, 48), StateColor::Enabled)); + +static StateColor btn_bg_white(std::pair(wxColour(206, 206, 206), StateColor::Pressed), + std::pair(wxColour(238, 238, 238), StateColor::Hovered), + std::pair(*wxWHITE, StateColor::Normal)); + +static StateColor btn_bg_gray(std::pair(wxColour(194, 194, 194), StateColor::Pressed), + std::pair(wxColour(194, 194, 194), StateColor::Hovered), + std::pair(wxColour(194, 194, 194), StateColor::Normal)); + +static StateColor btn_bg_green(std::pair(wxColour(27, 136, 68), StateColor::Pressed), + std::pair(wxColour(61, 203, 115), StateColor::Hovered), + std::pair(wxColour(0, 177, 66), StateColor::Normal)); + PartSkipDialog::PartSkipDialog(wxWindow *parent) : DPIDialog(parent, wxID_ANY, _L("Skip Objects"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX) { std::time_t t = std::time(0); @@ -70,12 +97,7 @@ PartSkipDialog::PartSkipDialog(wxWindow *parent) : DPIDialog(parent, wxID_ANY, _ m_canvas_btn_sizer = new wxBoxSizer(wxHORIZONTAL); m_canvas_btn_sizer->SetMinSize(wxSize(-1, FromDIP(28))); - StateColor zoom_bg(std::pair(wxColour(255, 255, 255), StateColor::Disabled), std::pair(wxColour(206, 206, 206), StateColor::Pressed), - std::pair(wxColour(238, 238, 238), StateColor::Hovered), std::pair(wxColour(255, 255, 255), StateColor::Enabled), - std::pair(wxColour(255, 255, 255), StateColor::Normal)); zoom_bg.setTakeFocusedAsHovered(false); - StateColor zoom_bd(std::pair(wxColour(144, 144, 144), StateColor::Disabled), std::pair(wxColour(38, 46, 48), StateColor::Enabled)); - StateColor zoom_text(std::pair(wxColour(144, 144, 144), StateColor::Disabled), std::pair(wxColour(38, 46, 48), StateColor::Enabled)); m_zoom_out_btn = new Button(m_book_third_panel, wxEmptyString); m_zoom_out_btn->SetIcon("canvas_zoom_out"); @@ -85,9 +107,6 @@ PartSkipDialog::PartSkipDialog(wxWindow *parent) : DPIDialog(parent, wxID_ANY, _ m_zoom_out_btn->SetCornerRadius(0); m_zoom_out_btn->SetMinSize(wxSize(FromDIP(56), FromDIP(28))); - StateColor percent_bg(std::pair(wxColour(255, 255, 255), StateColor::Disabled), std::pair(wxColour(255, 255, 255), StateColor::Pressed), - std::pair(wxColour(255, 255, 255), StateColor::Hovered), std::pair(wxColour(255, 255, 255), StateColor::Enabled), - std::pair(wxColour(255, 255, 255), StateColor::Normal)); m_percent_label = new Button(m_book_third_panel, _L("100 %")); m_percent_label->SetBackgroundColor(percent_bg); m_percent_label->SetBorderColor(wxColour(238, 238, 238)); @@ -164,20 +183,15 @@ PartSkipDialog::PartSkipDialog(wxWindow *parent) : DPIDialog(parent, wxID_ANY, _ m_tot_label->SetBackgroundColour(*wxWHITE); m_tot_label->SetMinSize(wxSize(FromDIP(200), FromDIP(20))); - StateColor btn_bg_white(std::pair(wxColour(206, 206, 206), StateColor::Pressed), std::pair(wxColour(238, 238, 238), StateColor::Hovered), - std::pair(*wxWHITE, StateColor::Normal)); - - StateColor btn_bg_green(std::pair(wxColour(27, 136, 68), StateColor::Pressed), std::pair(wxColour(61, 203, 115), StateColor::Hovered), - std::pair(wxColour(0, 177, 66), StateColor::Normal)); - m_apply_btn = new Button(m_book_third_panel, _L("Skip")); - m_apply_btn->SetBackgroundColor(btn_bg_green); + m_apply_btn->SetBackgroundColor(btn_bg_gray); m_apply_btn->SetTextColor(*wxWHITE); // m_apply_btn->SetBorderColor(wxColour(38, 46, 48)); m_apply_btn->SetFont(Label::Body_14); m_apply_btn->SetSize(wxSize(FromDIP(80), FromDIP(32))); m_apply_btn->SetMinSize(wxSize(FromDIP(80), FromDIP(32))); m_apply_btn->SetCornerRadius(FromDIP(16)); + m_apply_btn->Enable(false); m_canvas_sizer->Add(m_canvas, 0, wxLEFT | wxTOP | wxEXPAND, FromDIP(17)); m_canvas_sizer->Add(m_canvas_btn_sizer, 0, wxTOP, FromDIP(8)); @@ -194,12 +208,12 @@ PartSkipDialog::PartSkipDialog(wxWindow *parent) : DPIDialog(parent, wxID_ANY, _ m_dlg_content_sizer->Add(m_list_sizer, 0, wxRIGHT | wxEXPAND, FromDIP(14)); m_dlg_btn_sizer->Add(0, 0, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, FromDIP(21)); - m_dlg_btn_sizer->Add(m_cnt_label, 0, wxALIGN_CENTER_VERTICAL, FromDIP(0) ); - #ifdef __WXMSW__ - m_dlg_btn_sizer->Add(m_tot_label, 0, wxLEFT |wxTOP| wxALIGN_CENTER_VERTICAL, FromDIP(2)); - #else - m_dlg_btn_sizer->Add(m_tot_label, 0, wxALIGN_CENTER_VERTICAL, 0); - #endif + m_dlg_btn_sizer->Add(m_cnt_label, 0, wxALIGN_CENTER_VERTICAL, FromDIP(0)); +#ifdef __WXMSW__ + m_dlg_btn_sizer->Add(m_tot_label, 0, wxLEFT | wxTOP | wxALIGN_CENTER_VERTICAL, FromDIP(2)); +#else + m_dlg_btn_sizer->Add(m_tot_label, 0, wxALIGN_CENTER_VERTICAL, 0); +#endif m_dlg_btn_sizer->Add(0, 0, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL, FromDIP(0)); m_dlg_btn_sizer->Add(m_apply_btn, 0, wxALIGN_CENTER_VERTICAL, FromDIP(0)); m_dlg_btn_sizer->Add(0, 0, 0, wxLEFT | wxEXPAND | wxALIGN_CENTER_VERTICAL, FromDIP(24)); @@ -289,7 +303,7 @@ PartSkipDialog::PartSkipDialog(wxWindow *parent) : DPIDialog(parent, wxID_ANY, _ m_apply_btn->Bind(wxEVT_BUTTON, &PartSkipDialog::OnApplyDialog, this); m_all_checkbox->Bind(wxEVT_TOGGLEBUTTON, &PartSkipDialog::OnAllCheckbox, this); - Layout(); + Layout(); Fit(); CentreOnParent(); } @@ -635,6 +649,8 @@ void PartSkipDialog::UpdatePartsStateFromCanvas(wxCommandEvent &event) m_parts_state[part_id] = part_state; if (part_state == psUnCheck) { m_all_checkbox->SetValue(false); } if (IsAllChecked()) { m_all_checkbox->SetValue(true); } + + UpdateApplyButtonStatus(); UpdateDialogUI(); } @@ -715,6 +731,7 @@ void PartSkipDialog::InitDialogUI() } m_canvas->UpdatePartsInfo(GetPartsInfo()); UpdateCountLabel(); + UpdateApplyButtonStatus(); event.Skip(); }, checkbox->GetId()); @@ -821,6 +838,14 @@ bool PartSkipDialog::IsAllChecked() return true; } +bool PartSkipDialog::IsAllCancled() +{ + for (auto &[part_id, part_state] : m_parts_state) { + if (part_state == PartState::psChecked) return false; + } + return true; +} + void PartSkipDialog::OnAllCheckbox(wxCommandEvent &event) { if (m_all_checkbox->GetValue()) { @@ -832,11 +857,23 @@ void PartSkipDialog::OnAllCheckbox(wxCommandEvent &event) if (part_state == PartState::psChecked) part_state = PartState::psUnCheck; } } + UpdateApplyButtonStatus(); m_canvas->UpdatePartsInfo(GetPartsInfo()); UpdateDialogUI(); event.Skip(); } +void PartSkipDialog::UpdateApplyButtonStatus() +{ + if (IsAllCancled()) { + m_apply_btn->SetBackgroundColor(btn_bg_gray); + m_apply_btn->Enable(false); + } else { + m_apply_btn->SetBackgroundColor(btn_bg_green); + m_apply_btn->Enable(true); + } +} + void PartSkipDialog::OnApplyDialog(wxCommandEvent &event) { event.Skip(); @@ -866,7 +903,7 @@ void PartSkipDialog::OnApplyDialog(wxCommandEvent &event) if (all_skipped) { m_obj->command_task_abort(); BOOST_LOG_TRIVIAL(info) << "part skip: command skip all parts, abort task."; - } else { + } else if (m_partskip_ids.size() > 0) { m_obj->command_task_partskip(m_partskip_ids); BOOST_LOG_TRIVIAL(info) << "part skip: command skip " << m_partskip_ids.size() << " parts."; } diff --git a/src/slic3r/GUI/PartSkipDialog.hpp b/src/slic3r/GUI/PartSkipDialog.hpp index 92abbed0b9..e90a097b2f 100644 --- a/src/slic3r/GUI/PartSkipDialog.hpp +++ b/src/slic3r/GUI/PartSkipDialog.hpp @@ -149,7 +149,9 @@ private: void UpdateZoomPercent(); void UpdateCountLabel(); void UpdateDialogUI(); + void UpdateApplyButtonStatus(); bool IsAllChecked(); + bool IsAllCancled(); void OnRetryButton(wxCommandEvent &event); void OnAllCheckbox(wxCommandEvent &event); From b49ca9094039502d0aa83cd3beeffdecdbc6c177 Mon Sep 17 00:00:00 2001 From: hemai Date: Thu, 10 Jul 2025 21:52:37 +0800 Subject: [PATCH 11/27] FIX: printer file system network reconnect bug && invalid obj bug Jira: [STUDIO-13295] Change-Id: I3b0c907ab4357d09ad592a0d937d83aaa15e8680 (cherry picked from commit 928d6b4c6a8c3ea8c9bd179c51afdb7dc55f45ce) --- src/slic3r/GUI/PartSkipDialog.cpp | 1 + src/slic3r/GUI/StatusPanel.cpp | 21 ++++++++------------- src/slic3r/GUI/StatusPanel.hpp | 3 +-- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/slic3r/GUI/PartSkipDialog.cpp b/src/slic3r/GUI/PartSkipDialog.cpp index ca8eac7ea2..f79842787b 100644 --- a/src/slic3r/GUI/PartSkipDialog.cpp +++ b/src/slic3r/GUI/PartSkipDialog.cpp @@ -532,6 +532,7 @@ void PartSkipDialog::OnFileSystemEvent(wxCommandEvent &e) m_file_sys->Stop(); if (m_url_state == URL_TCP) { m_url_state = URL_TUTK; + m_file_sys->SetUrl("3"); m_file_sys->Retry(); BOOST_LOG_TRIVIAL(info) << "part skip: print file system connnect failed first."; } else { diff --git a/src/slic3r/GUI/StatusPanel.cpp b/src/slic3r/GUI/StatusPanel.cpp index 8065d09f77..5e63c73815 100644 --- a/src/slic3r/GUI/StatusPanel.cpp +++ b/src/slic3r/GUI/StatusPanel.cpp @@ -643,15 +643,11 @@ void PrintingTaskPanel::reset_printing_value() this->set_plate_index(-1); } -void PrintingTaskPanel::update_machine_object(MachineObject* obj){ - if(obj) m_obj = obj; -} - -void PrintingTaskPanel::enable_partskip_button(bool enable) +void PrintingTaskPanel::enable_partskip_button(MachineObject* obj, bool enable) { int stage = 0; bool in_calibration_mode = false; - if( m_obj && (m_obj->print_type == "system" || CalibUtils::get_calib_mode_by_name(m_obj->subtask_name, stage) != CalibMode::Calib_None)){ + if( obj && (obj->print_type == "system" || CalibUtils::get_calib_mode_by_name(obj->subtask_name, stage) != CalibMode::Calib_None)){ in_calibration_mode = true; } @@ -659,7 +655,7 @@ void PrintingTaskPanel::enable_partskip_button(bool enable) m_button_partskip->Enable(false); m_button_partskip->SetLabel(""); m_button_partskip->SetIcon("print_control_partskip_disable"); - }else if(m_obj && m_obj->is_support_brtc){ + }else if(obj && obj->is_support_brtc){ m_button_partskip->Enable(true); m_button_partskip->SetIcon("print_control_partskip"); } @@ -1758,7 +1754,7 @@ StatusPanel::StatusPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos, co m_switch_cham_fan->SetValue(false); /* set default enable state */ - m_project_task_panel->enable_partskip_button(false); + m_project_task_panel->enable_partskip_button(nullptr, false); m_project_task_panel->enable_pause_resume_button(false, "resume_disable"); m_project_task_panel->enable_abort_button(false); @@ -1973,7 +1969,6 @@ void StatusPanel::on_market_retry(wxCommandEvent &event) void StatusPanel::update_partskip_button(MachineObject *obj) { if (!obj) return; - m_project_task_panel->update_machine_object(obj); auto partskip_button = m_project_task_panel->get_partskip_button(); if( obj->is_support_partskip ){ partskip_button->Show(); @@ -3155,7 +3150,7 @@ void StatusPanel::update_subtask(MachineObject *obj) if (obj->is_in_prepare() || obj->print_status == "SLICING") { m_project_task_panel->market_scoring_hide(); m_project_task_panel->get_request_failed_panel()->Hide(); - m_project_task_panel->enable_partskip_button(false); + m_project_task_panel->enable_partskip_button(nullptr, false); m_project_task_panel->enable_abort_button(false); m_project_task_panel->enable_pause_resume_button(false, "pause_disable"); wxString prepare_text; @@ -3197,7 +3192,7 @@ void StatusPanel::update_subtask(MachineObject *obj) } else { m_project_task_panel->enable_pause_resume_button(true, "pause"); } - m_project_task_panel->enable_partskip_button(true); + m_project_task_panel->enable_partskip_button(obj, true); // update printing stage m_project_task_panel->update_left_time(obj->mc_left_time); if (obj->subtask_) { @@ -3214,7 +3209,7 @@ void StatusPanel::update_subtask(MachineObject *obj) if (obj->is_printing_finished()) { obj->update_model_task(); m_project_task_panel->enable_abort_button(false); - m_project_task_panel->enable_partskip_button(false); + m_project_task_panel->enable_partskip_button(nullptr, false); m_project_task_panel->enable_pause_resume_button(false, "resume_disable"); // is makeworld subtask if (wxGetApp().has_model_mall() && obj->is_makeworld_subtask()) { @@ -3372,7 +3367,7 @@ void StatusPanel::update_sdcard_subtask(MachineObject *obj) void StatusPanel::reset_printing_values() { - m_project_task_panel->enable_partskip_button(false); + m_project_task_panel->enable_partskip_button(nullptr, false); m_project_task_panel->enable_pause_resume_button(false, "pause_disable"); m_project_task_panel->enable_abort_button(false); m_project_task_panel->reset_printing_value(); diff --git a/src/slic3r/GUI/StatusPanel.hpp b/src/slic3r/GUI/StatusPanel.hpp index a300d1682e..28fded24f7 100644 --- a/src/slic3r/GUI/StatusPanel.hpp +++ b/src/slic3r/GUI/StatusPanel.hpp @@ -223,7 +223,7 @@ public: void msw_rescale(); public: - void enable_partskip_button(bool enable); + void enable_partskip_button(MachineObject* obj, bool enable); void enable_pause_resume_button(bool enable, std::string type); void enable_abort_button(bool enable); void update_subtask_name(wxString name); @@ -239,7 +239,6 @@ public: void set_plate_index(int plate_idx = -1); void market_scoring_show(); void market_scoring_hide(); - void update_machine_object(MachineObject* obj); public: ScalableButton* get_abort_button() {return m_button_abort;}; From 219300277c02fb19891e45162b57b0b7a63b3906 Mon Sep 17 00:00:00 2001 From: "haolin.tian" Date: Tue, 15 Jul 2025 14:46:39 +0800 Subject: [PATCH 12/27] FIX: adjust ortho, do not scale source image jira: [STUDIO-12333] Change-Id: I4fabccc9c489ed7fa4a8ad1239ed26c1710b3ee9 (cherry picked from commit 3fbb9946d1deee1855498e97cc66844e1f2e54aa) --- src/slic3r/GUI/SkipPartCanvas.cpp | 7 ++++--- src/slic3r/GUI/SkipPartCanvas.hpp | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/SkipPartCanvas.cpp b/src/slic3r/GUI/SkipPartCanvas.cpp index 75b99d4cc5..f7e8a6d4ec 100644 --- a/src/slic3r/GUI/SkipPartCanvas.cpp +++ b/src/slic3r/GUI/SkipPartCanvas.cpp @@ -86,7 +86,8 @@ void SkipPartCanvas::LoadPickImage(const std::string & path) image_scale = zoom_x; else image_scale = zoom_y; - cv::resize(src_image, pick_image_, cv::Size(), image_scale, image_scale, cv::INTER_NEAREST); + image_view_scale_ = 1 / image_scale; + pick_image_ = src_image; std::vector channels; cv::Mat gray; // convert to gray cv::cvtColor(pick_image_, gray, cv::COLOR_BGR2GRAY); @@ -399,7 +400,7 @@ inline double SkipPartCanvas::Zoom() const inline wxPoint SkipPartCanvas::ViewPtToImagePt(const wxPoint& view_pt) const { - return wxPoint(view_pt.x / Zoom(), view_pt.y / Zoom()) + offset_; + return wxPoint(view_pt.x * image_view_scale_ / Zoom(), view_pt.y * image_view_scale_ / Zoom()) + offset_; } uint32_t SkipPartCanvas::GetIdAtImagePt(const wxPoint& image_pt) const @@ -460,7 +461,7 @@ void SkipPartCanvas::StartDrag(const wxPoint& mouse_pt) void SkipPartCanvas::ProcessDrag(const wxPoint& mouse_pt) { - wxPoint drag_offset = mouse_pt - drag_start_pt_; + wxPoint drag_offset = (mouse_pt - drag_start_pt_) * image_view_scale_; SetOffset(- wxPoint(drag_offset.x / Zoom(), drag_offset.y / Zoom()) + drag_start_offset_); Refresh(); } diff --git a/src/slic3r/GUI/SkipPartCanvas.hpp b/src/slic3r/GUI/SkipPartCanvas.hpp index 038b53d371..ab25e4f618 100644 --- a/src/slic3r/GUI/SkipPartCanvas.hpp +++ b/src/slic3r/GUI/SkipPartCanvas.hpp @@ -97,6 +97,7 @@ private: bool left_down_{false}; ColorRGB parent_color_ = ColorRGB(); int hover_id_{-1}; + double image_view_scale_{1}; void SendSelectEvent(int id, PartState state); void SendZoomEvent(int zoom_percent); From 6148c73ffca779158bf9a92da6d2d0e23f966546 Mon Sep 17 00:00:00 2001 From: hemai Date: Mon, 7 Jul 2025 20:44:27 +0800 Subject: [PATCH 13/27] FIX: scroll view can't slide && cnt_label display incomplete Jira: [STUDIO-13176] Change-Id: I32dfc793a431412a7df02ae2b9d952d175d97deb (cherry picked from commit bdda60dd1dc37a4750cdb7b426de9e9518c581ea) --- src/slic3r/GUI/PartSkipDialog.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/slic3r/GUI/PartSkipDialog.cpp b/src/slic3r/GUI/PartSkipDialog.cpp index f79842787b..e5604f2e80 100644 --- a/src/slic3r/GUI/PartSkipDialog.cpp +++ b/src/slic3r/GUI/PartSkipDialog.cpp @@ -666,7 +666,10 @@ void PartSkipDialog::UpdateCountLabel() if (part_state != PartState::psSkipped) tot_cnt++; } m_cnt_label->SetLabel(wxString::Format(_L("%d"), check_cnt)); + m_cnt_label->Fit(); m_tot_label->SetLabel(wxString::Format(_L("/%d Selected"), tot_cnt)); + m_tot_label->Fit(); + m_dlg_btn_sizer->Layout(); } bool PartSkipDialog::Show(bool show) @@ -765,6 +768,7 @@ void PartSkipDialog::InitDialogUI() } m_scroll_sizer->Layout(); + m_list_view->FitInside(); UpdateCountLabel(); Refresh(); m_print_lock = false; From b4c08f9bdfbecf21a6524fcf8011401512c57099 Mon Sep 17 00:00:00 2001 From: hemai Date: Tue, 15 Jul 2025 21:55:18 +0800 Subject: [PATCH 14/27] ENH: disable skip when objects number>64 or model is not support Jira: [TUDIO-13411] Change-Id: I93617965e69ba72a1cc34dd0981b8fd92eb58d3a (cherry picked from commit 417109e5f02613bd18eae125705effa7ae61cfda) --- src/slic3r/GUI/PartSkipDialog.cpp | 29 ++++++++++++++++++++++++----- src/slic3r/GUI/PartSkipDialog.hpp | 2 ++ src/slic3r/GUI/SkipPartCanvas.cpp | 9 +++++++++ src/slic3r/GUI/SkipPartCanvas.hpp | 2 ++ 4 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/PartSkipDialog.cpp b/src/slic3r/GUI/PartSkipDialog.cpp index e5604f2e80..70d39a6f21 100644 --- a/src/slic3r/GUI/PartSkipDialog.cpp +++ b/src/slic3r/GUI/PartSkipDialog.cpp @@ -191,7 +191,7 @@ PartSkipDialog::PartSkipDialog(wxWindow *parent) : DPIDialog(parent, wxID_ANY, _ m_apply_btn->SetSize(wxSize(FromDIP(80), FromDIP(32))); m_apply_btn->SetMinSize(wxSize(FromDIP(80), FromDIP(32))); m_apply_btn->SetCornerRadius(FromDIP(16)); - m_apply_btn->Enable(false); + m_apply_btn->SetToolTip(wxEmptyString); m_canvas_sizer->Add(m_canvas, 0, wxLEFT | wxTOP | wxEXPAND, FromDIP(17)); m_canvas_sizer->Add(m_canvas_btn_sizer, 0, wxTOP, FromDIP(8)); @@ -407,7 +407,9 @@ bool PartSkipDialog::is_local_file_existed(const std::vector &local_path void PartSkipDialog::DownloadPartsFile() { + BOOST_LOG_TRIVIAL(info) << "part skip: create temp path begin."; m_tmp_path = create_tmp_path(); // wxGetApp().app_config->get("download_path"); + BOOST_LOG_TRIVIAL(info) << "part skip: create temp path end."; m_local_paths.clear(); m_target_paths.clear(); @@ -440,6 +442,7 @@ void PartSkipDialog::DownloadPartsFile() } } else { m_file_sys->SendExistedFile(); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "part skip: local parts info file is existed."; } } // actor @@ -686,7 +689,8 @@ bool PartSkipDialog::Show(bool show) void PartSkipDialog::InitDialogUI() { - m_print_lock = true; + m_print_lock = true; + is_model_support_partskip = false; BOOST_LOG_TRIVIAL(info) << "part skip: lock parts info from printer."; m_scroll_sizer->Clear(true); m_all_checkbox->SetValue(false); @@ -709,7 +713,8 @@ void PartSkipDialog::InitDialogUI() ModelSettingHelper helper(slice_info); if (helper.Parse()) { - auto parse_result = helper.GetPlateObjects(m_plate_idx); + is_model_support_partskip = helper.GetLabelObjectEnabled(m_plate_idx); + auto parse_result = helper.GetPlateObjects(m_plate_idx); for (const auto &part : parse_result) { m_parts_state[part.identify_id] = part.state; m_parts_name[part.identify_id] = part.name; @@ -769,6 +774,7 @@ void PartSkipDialog::InitDialogUI() m_scroll_sizer->Layout(); m_list_view->FitInside(); + UpdateApplyButtonStatus(); UpdateCountLabel(); Refresh(); m_print_lock = false; @@ -872,16 +878,29 @@ void PartSkipDialog::UpdateApplyButtonStatus() { if (IsAllCancled()) { m_apply_btn->SetBackgroundColor(btn_bg_gray); - m_apply_btn->Enable(false); + m_apply_btn->SetToolTip(_L("Nothing selected")); + m_enable_apply_btn = false; + } else if (m_parts_state.size() > 64) { + m_apply_btn->SetBackgroundColor(btn_bg_gray); + m_apply_btn->SetToolTip(_L("Over 64 objects in single plate")); + m_enable_apply_btn = false; + } else if (!is_model_support_partskip) { + m_apply_btn->SetBackgroundColor(btn_bg_gray); + m_apply_btn->SetToolTip(_L("The current print job cannot be skipped")); + m_enable_apply_btn = false; } else { m_apply_btn->SetBackgroundColor(btn_bg_green); - m_apply_btn->Enable(true); + m_apply_btn->SetToolTip(wxEmptyString); + m_enable_apply_btn = true; } } void PartSkipDialog::OnApplyDialog(wxCommandEvent &event) { event.Skip(); + + if (!m_enable_apply_btn) return; + m_partskip_ids.clear(); for (const auto &[part_id, part_state] : m_parts_state) { if (part_state == PartState::psChecked) { m_partskip_ids.push_back(part_id); } diff --git a/src/slic3r/GUI/PartSkipDialog.hpp b/src/slic3r/GUI/PartSkipDialog.hpp index e90a097b2f..4f0b5bf07f 100644 --- a/src/slic3r/GUI/PartSkipDialog.hpp +++ b/src/slic3r/GUI/PartSkipDialog.hpp @@ -115,6 +115,8 @@ private: int m_zoom_percent{100}; bool m_is_drag{false}; bool m_print_lock{true}; + bool m_enable_apply_btn{false}; + bool is_model_support_partskip{false}; std::map m_parts_state; std::map m_parts_name; diff --git a/src/slic3r/GUI/SkipPartCanvas.cpp b/src/slic3r/GUI/SkipPartCanvas.cpp index f7e8a6d4ec..d6b98dae10 100644 --- a/src/slic3r/GUI/SkipPartCanvas.cpp +++ b/src/slic3r/GUI/SkipPartCanvas.cpp @@ -649,6 +649,7 @@ void XMLCALL ModelSettingHelper::StartElementHandler(void *userData, const XML_C if (strcmp(atts[i], "value") == 0) value = atts[i + 1]; } if (key == "index") { self->context_.current_plate.index = std::stoi(value); } + if (key == "label_object_enabled") { self->context_.current_plate.label_object_enabled = value == "true"; } } else if (strcmp(name, "object") == 0 && self->context_.in_plate) { ObjectInfo obj; for (int i = 0; atts[i]; i += 2) { @@ -678,6 +679,14 @@ std::vector ModelSettingHelper::GetPlateObjects(int plate_idx) { return std::vector(); } +bool ModelSettingHelper::GetLabelObjectEnabled(int plate_idx) +{ + for (const auto &plate : context_.plates) { + if (plate.index == plate_idx) { return plate.label_object_enabled; } + } + return false; +} + void ModelSettingHelper::DataHandler(const XML_Char *s, int len) { // do nothing diff --git a/src/slic3r/GUI/SkipPartCanvas.hpp b/src/slic3r/GUI/SkipPartCanvas.hpp index ab25e4f618..a9c8f1fec7 100644 --- a/src/slic3r/GUI/SkipPartCanvas.hpp +++ b/src/slic3r/GUI/SkipPartCanvas.hpp @@ -135,6 +135,7 @@ struct PlateInfo { int index{-1}; std::vector objects; + bool label_object_enabled = false; }; class ModelSettingHelper : public _BBS_3MF_Base @@ -152,6 +153,7 @@ public: bool Parse(); std::vector GetPlateObjects(int plate_idx); + bool GetLabelObjectEnabled(int plate_idx); private: std::string path_; From 22c42323a4a6aa1db4ec76895772d0102b754ec4 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Fri, 18 Jul 2025 11:54:03 +0200 Subject: [PATCH 15/27] FIX: Fix missing boost::lexical_cast declaration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /run/build/BambuStudio/src/slic3r/GUI/PartSkipDialog.cpp: In lambda function: /run/build/BambuStudio/src/slic3r/GUI/PartSkipDialog.cpp:495: error: ‘lexical_cast’ is not a member of ‘boost’ 495 | url += "&refresh_url=" + boost::lexical_cast(&refresh_agora_url); (cherry picked from commit fc8537c038a8dcc419f8b5558feb71700b77c996) --- src/slic3r/GUI/PartSkipDialog.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/slic3r/GUI/PartSkipDialog.cpp b/src/slic3r/GUI/PartSkipDialog.cpp index 70d39a6f21..4ce45c3a18 100644 --- a/src/slic3r/GUI/PartSkipDialog.cpp +++ b/src/slic3r/GUI/PartSkipDialog.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include "Widgets/CheckBox.hpp" #include "Widgets/Label.hpp" From 13d92131e7976b7f2422c59b640618ecef255298 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Fri, 18 Jul 2025 12:01:47 +0200 Subject: [PATCH 16/27] FIX: Fix missing BOOST_LOG_TRIVIAL() declaration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /run/build/BambuStudio/src/slic3r/GUI/SkipPartCanvas.cpp: In member function ‘void Slic3r::GUI::_BBS_3MF_Base::log_errors()’: /run/build/BambuStudio/src/slic3r/GUI/SkipPartCanvas.cpp:598:47: error: ‘BOOST_LOG_TRIVIAL’ was not declared in this scope 598 | for (const std::string &error : m_errors) BOOST_LOG_TRIVIAL(error) << error; | ^~~~~~~~~~~~~~~~~ (cherry picked from commit dba7f3ad07f530ddce3e75eaf8f357794cfd1935) --- src/slic3r/GUI/SkipPartCanvas.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/slic3r/GUI/SkipPartCanvas.cpp b/src/slic3r/GUI/SkipPartCanvas.cpp index d6b98dae10..c6cb9e6391 100644 --- a/src/slic3r/GUI/SkipPartCanvas.cpp +++ b/src/slic3r/GUI/SkipPartCanvas.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include From 2048f57cda8924315f9811c20ba332a4735e8f6d Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Fri, 18 Jul 2025 12:01:55 +0200 Subject: [PATCH 17/27] FIX: Fix missing std::map declaration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /run/build/BambuStudio/src/slic3r/GUI/SkipPartCanvas.cpp: In lambda function: /run/build/BambuStudio/src/slic3r/GUI/SkipPartCanvas.cpp:55:14: error: ‘map’ is not a member of ‘std’ 55 | std::map> colorCount( | ^~~ /run/build/BambuStudio/src/slic3r/GUI/SkipPartCanvas.cpp:13:1: note: ‘std::map’ is defined in header ‘’; this is probably fixable by adding ‘#include ’ 12 | #include +++ |+#include 13 | (cherry picked from commit b9402b5be60188e8867471b64c3bc1d8c1d6bfa7) --- src/slic3r/GUI/SkipPartCanvas.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/slic3r/GUI/SkipPartCanvas.cpp b/src/slic3r/GUI/SkipPartCanvas.cpp index c6cb9e6391..98bc3bdbe4 100644 --- a/src/slic3r/GUI/SkipPartCanvas.cpp +++ b/src/slic3r/GUI/SkipPartCanvas.cpp @@ -11,6 +11,7 @@ #include #include #include +#include wxDEFINE_EVENT(EVT_ZOOM_PERCENT, wxCommandEvent); wxDEFINE_EVENT(EVT_CANVAS_PART, wxCommandEvent); From 2c6bb1a443c5dbd7c9d3c5debd8f08ece4eb42b5 Mon Sep 17 00:00:00 2001 From: Noisyfox Date: Fri, 26 Sep 2025 15:21:23 +0800 Subject: [PATCH 18/27] Update layout --- src/slic3r/GUI/StatusPanel.cpp | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/slic3r/GUI/StatusPanel.cpp b/src/slic3r/GUI/StatusPanel.cpp index 5e63c73815..864ed44dae 100644 --- a/src/slic3r/GUI/StatusPanel.cpp +++ b/src/slic3r/GUI/StatusPanel.cpp @@ -318,11 +318,12 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) wxBoxSizer *bSizer_buttons = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer *bSizer_text = new wxBoxSizer(wxHORIZONTAL); - wxPanel* penel_bottons = new wxPanel(parent); - wxPanel* penel_text = new wxPanel(penel_bottons); + wxBoxSizer *bSizer_finish_time = new wxBoxSizer(wxHORIZONTAL); + wxPanel* penel_text = new wxPanel(progress_lr_panel); + wxPanel* penel_finish_time = new wxPanel(progress_lr_panel); penel_text->SetBackgroundColour(*wxWHITE); - penel_bottons->SetBackgroundColour(*wxWHITE); + penel_finish_time->SetBackgroundColour(*wxWHITE); wxBoxSizer *sizer_percent = new wxBoxSizer(wxVERTICAL); sizer_percent->Add(0, 0, 1, wxEXPAND, 0); @@ -355,13 +356,6 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) m_staticText_progress_left->SetFont(wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxT("HarmonyOS Sans SC"))); m_staticText_progress_left->SetForegroundColour(wxColour(146, 146, 146)); - // Orca: display the end time of the print - m_staticText_progress_end = new wxStaticText(penel_text, wxID_ANY, L("N/A"), wxDefaultPosition, wxDefaultSize, 0); - m_staticText_progress_end->Wrap(-1); - m_staticText_progress_end->SetFont( - wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxT("HarmonyOS Sans SC"))); - m_staticText_progress_end->SetForegroundColour(wxColour(146, 146, 146)); - m_staticText_layers = new wxStaticText(penel_text, wxID_ANY, _L("Layer: N/A")); m_staticText_layers->SetFont(wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxT("HarmonyOS Sans SC"))); m_staticText_layers->SetForegroundColour(wxColour(146, 146, 146)); @@ -373,20 +367,30 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) bSizer_text->Add(m_staticText_layers, 0, wxALIGN_CENTER_VERTICAL | wxALL, 0); bSizer_text->Add(0, 0, 0, wxLEFT, FromDIP(20)); bSizer_text->Add(m_staticText_progress_left, 0, wxALIGN_CENTER_VERTICAL | wxALL, 0); - // Orca: display the end time of the print - bSizer_text->Add(0, 0, 0, wxLEFT, FromDIP(8)); - bSizer_text->Add(m_staticText_progress_end, 0, wxALIGN_CENTER | wxALL, 0); // penel_text->SetMaxSize(wxSize(FromDIP(600), -1)); penel_text->SetSizer(bSizer_text); penel_text->Layout(); + // Orca: display the end time of the print + m_staticText_progress_end = new wxStaticText(penel_finish_time, wxID_ANY, L("N/A"), wxDefaultPosition, wxDefaultSize, 0); + m_staticText_progress_end->Wrap(-1); + m_staticText_progress_end->SetFont( + wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, false, wxT("HarmonyOS Sans SC"))); + m_staticText_progress_end->SetForegroundColour(wxColour(146, 146, 146)); + bSizer_finish_time->Add(0, 0, 1, wxEXPAND, 0); + bSizer_finish_time->Add(m_staticText_progress_end, 0, wxLEFT | wxEXPAND, 0); + // penel_finish_time->SetMaxSize(wxSize(FromDIP(600), -1)); + penel_finish_time->SetSizer(bSizer_finish_time); + penel_finish_time->Layout(); + auto progress_lr_sizer = new wxBoxSizer(wxHORIZONTAL); auto progress_left_sizer = new wxBoxSizer(wxVERTICAL); auto progress_right_sizer = new wxBoxSizer(wxHORIZONTAL); progress_left_sizer->Add(penel_text, 0, wxEXPAND | wxALL, 0); - progress_left_sizer->Add(m_gauge_progress, 0, wxEXPAND | wxALL, 0); + progress_left_sizer->Add(m_gauge_progress, 0, wxEXPAND | wxTOP | wxBOTTOM, FromDIP(10)); + progress_left_sizer->Add(penel_finish_time, 0, wxEXPAND |wxALL, 0); // progress_left_sizer->SetMaxSize(wxSize(FromDIP(600), -1)); progress_right_sizer->Add(0, 0, 0, wxEXPAND | wxLEFT, FromDIP(18)); @@ -418,7 +422,6 @@ void PrintingTaskPanel::create_panel(wxWindow* parent) m_printing_sizer->Add(FromDIP(8), 0, 0, wxEXPAND, 0); m_printing_sizer->Add(bSizer_subtask_info, 1, wxALL | wxEXPAND, 0); - m_staticline = new wxPanel( parent, wxID_ANY); m_staticline->SetBackgroundColour(wxColour(238,238,238)); m_staticline->Layout(); From dc494da0b373bf99ec178a4022fe1bee4cfa32f5 Mon Sep 17 00:00:00 2001 From: Noisyfox Date: Fri, 26 Sep 2025 17:16:20 +0800 Subject: [PATCH 19/27] Move earcut to deps_src --- deps_src/CMakeLists.txt | 1 + {src => deps_src}/earcut/CHANGELOG.md | 0 deps_src/earcut/CMakeLists.txt | 13 +++ {src => deps_src}/earcut/LICENSE | 0 {src => deps_src}/earcut/README.md | 0 {src => deps_src}/earcut/earcut.hpp | 0 src/earcut/CMakeLists.txt | 151 -------------------------- 7 files changed, 14 insertions(+), 151 deletions(-) rename {src => deps_src}/earcut/CHANGELOG.md (100%) create mode 100644 deps_src/earcut/CMakeLists.txt rename {src => deps_src}/earcut/LICENSE (100%) rename {src => deps_src}/earcut/README.md (100%) rename {src => deps_src}/earcut/earcut.hpp (100%) delete mode 100644 src/earcut/CMakeLists.txt diff --git a/deps_src/CMakeLists.txt b/deps_src/CMakeLists.txt index f9e8c8fc3b..5c4e62b99f 100644 --- a/deps_src/CMakeLists.txt +++ b/deps_src/CMakeLists.txt @@ -6,6 +6,7 @@ project(deps_src) # Header-only libraries (INTERFACE) add_subdirectory(agg) add_subdirectory(ankerl) +add_subdirectory(earcut) add_subdirectory(fast_float) add_subdirectory(nanosvg) add_subdirectory(nlohmann) diff --git a/src/earcut/CHANGELOG.md b/deps_src/earcut/CHANGELOG.md similarity index 100% rename from src/earcut/CHANGELOG.md rename to deps_src/earcut/CHANGELOG.md diff --git a/deps_src/earcut/CMakeLists.txt b/deps_src/earcut/CMakeLists.txt new file mode 100644 index 0000000000..533241e92a --- /dev/null +++ b/deps_src/earcut/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.13) +project(earcut) + +add_library(earcut INTERFACE) + +target_include_directories(earcut SYSTEM + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR} +) + +target_sources(earcut INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/earcut.hpp +) diff --git a/src/earcut/LICENSE b/deps_src/earcut/LICENSE similarity index 100% rename from src/earcut/LICENSE rename to deps_src/earcut/LICENSE diff --git a/src/earcut/README.md b/deps_src/earcut/README.md similarity index 100% rename from src/earcut/README.md rename to deps_src/earcut/README.md diff --git a/src/earcut/earcut.hpp b/deps_src/earcut/earcut.hpp similarity index 100% rename from src/earcut/earcut.hpp rename to deps_src/earcut/earcut.hpp diff --git a/src/earcut/CMakeLists.txt b/src/earcut/CMakeLists.txt deleted file mode 100644 index 08c7bb1bed..0000000000 --- a/src/earcut/CMakeLists.txt +++ /dev/null @@ -1,151 +0,0 @@ -cmake_minimum_required(VERSION 3.2) -project(earcut_hpp LANGUAGES CXX C) - -option(EARCUT_BUILD_TESTS "Build the earcut test program" ON) -option(EARCUT_BUILD_BENCH "Build the earcut benchmark program" ON) -option(EARCUT_BUILD_VIZ "Build the earcut visualizer program" ON) -option(EARCUT_WARNING_IS_ERROR "Treat warnings as errors" OFF) - -if (NOT CMAKE_BUILD_TYPE AND NOT GENERATOR_IS_MULTI_CONFIG) - message(STATUS "No build type specified. Setting to 'Release'") - set(CMAKE_BUILD_TYPE "Release" CACHE STRING "The type of build." FORCE) -endif() - - -include(GNUInstallDirs) - -add_library(earcut_hpp INTERFACE) -add_library(earcut_hpp::earcut_hpp ALIAS earcut_hpp) - -target_include_directories(earcut_hpp INTERFACE - $ - $ -) - -set(CMAKE_CXX_STANDARD 11) - -if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" GREATER 3.7) - # Allow C++11 requirements to propagate when using recent CMake versions - target_compile_features(earcut_hpp INTERFACE cxx_std_11) -endif() - -file(GLOB FIXTURE_SOURCE_FILES test/fixtures/*.cpp test/fixtures/*.hpp) -source_group(fixtures FILES ${FIXTURE_SOURCE_FILES}) -add_library(fixtures OBJECT ${FIXTURE_SOURCE_FILES}) -target_compile_options(fixtures PRIVATE $<$:/Od>) - -# In CMake 3.12, use target_link_libraries(fixtures PUBLIC earcut_hpp libtess2). -# Since we support down to CMake 3.2, we need to manually propagate usage requirements of earcut_hpp -target_include_directories(fixtures PRIVATE "$") -target_compile_features(fixtures PRIVATE "$") - - -file(GLOB COMPARISON_SOURCE_FILES test/comparison/*.cpp test/comparison/*.hpp) -source_group(comparison FILES ${COMPARISON_SOURCE_FILES}) -# this is interface since there is no cpp files in the comparison directory -add_library(comparison INTERFACE) - - -file(GLOB LIBTESS2_SOURCE_FILES test/comparison/libtess2/*.c test/comparison/libtess2/*.h) -source_group(comparison/libtess2 FILES ${LIBTESS2_SOURCE_FILES}) -add_library(libtess2 ${LIBTESS2_SOURCE_FILES}) -target_compile_options(libtess2 PRIVATE - $<$:/wd4244 /wd4267> - $<$>:-w> -) - -add_library(common INTERFACE) -target_link_libraries(common INTERFACE libtess2 comparison) - -# optional: -march=native (builds with the optimizations available on the build machine (only for local use!)) -target_compile_options(common INTERFACE - $<$>:-pipe -Wall -Wextra -Wconversion -Wpedantic> -) - -if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang$" OR CMAKE_COMPILER_IS_GNUCXX) - if ("${CMAKE_CXX_FLAGS}" MATCHES "--coverage") - # We disable debug code for the coverage so it won't see assertion and other things only enabled for debugging - target_compile_definitions(common INTERFACE NDEBUG) - else() - # Here we enable the undefined behavior sanitizer for the tests, benchmarks and the viz - include(CheckCXXCompilerFlag) - check_cxx_compiler_flag("-fsanitize=undefined" HAVE_FLAG_SANITIZE_UNDEFINED) - if(HAVE_FLAG_SANITIZE_UNDEFINED) - target_compile_options(common INTERFACE $<$:-fsanitize=undefined>) - # TODO: Replace with target link option once we support CMake 3.13 - target_link_libraries(common INTERFACE $<$:-fsanitize=undefined>) - endif() - endif() -endif() - -if (EARCUT_WARNING_IS_ERROR) - target_compile_options(common INTERFACE - $<$:/WX> - $<$>:-Werror> - ) -endif() - -if (EARCUT_BUILD_TESTS) - enable_testing() - add_executable(tests test/tap.cpp test/tap.hpp test/test.cpp $) - target_link_libraries(tests PRIVATE earcut_hpp common) - add_test(NAME earcut_tests COMMAND tests) -endif() -if (EARCUT_BUILD_BENCH) - add_executable(bench test/bench.cpp $) - target_link_libraries(bench PRIVATE earcut_hpp common) -endif() -if (EARCUT_BUILD_VIZ) - add_executable(viz test/viz.cpp $) - - # Setup viz target - # OpenGL - # linux: xorg-dev libgl1-mesa-glx libgl1-mesa-dev - # windows: in the windows sdk - find_package(OpenGL REQUIRED) - - # GLFW3 - find_package(glfw3 QUIET) # try to use the system default - if (NOT glfw3_FOUND) - if(EXISTS "${PROJECT_SOURCE_DIR}/.gitmodules") - find_package(Git REQUIRED) - execute_process( - COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - OUTPUT_QUIET - ERROR_QUIET - ) - endif() - - set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "Build the GLFW example programs" FORCE) - set(GLFW_BUILD_TESTS OFF CACHE BOOL "Build the GLFW test programs" FORCE) - set(GLFW_BUILD_DOCS OFF CACHE BOOL "Build the GLFW documentation" FORCE) - set(GLFW_INSTALL OFF CACHE BOOL "Generate installation target" FORCE) - add_subdirectory(glfw) - endif() - - target_compile_definitions(viz PRIVATE GL_SILENCE_DEPRECATION) - - # TODO: Using old variables for OpenGL package since they were added in CMake 3.8 - target_link_libraries(viz PRIVATE earcut_hpp common glfw ${OPENGL_LIBRARIES}) - target_include_directories(viz PRIVATE ${OPENGL_INCLUDE_DIR}) -endif() - -install( - DIRECTORY include/mapbox - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "*.hpp" -) - -install(TARGETS earcut_hpp EXPORT earcut_hpp-config) - -# Since there is two projects, we need to export into the parent directory -export( - TARGETS earcut_hpp - NAMESPACE earcut_hpp:: - FILE "${PROJECT_BINARY_DIR}/earcut_hpp-config.cmake" -) - -install(EXPORT earcut_hpp-config - DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/earcut_hpp" - NAMESPACE earcut_hpp:: -) From 5a6cbc0a9a941d83b3bd6d33dc82ace33a4e7123 Mon Sep 17 00:00:00 2001 From: Kunlong Ma Date: Wed, 4 Sep 2024 14:09:32 +0800 Subject: [PATCH 20/27] NEW: support send to sd card with cloud JIRA: STUDIO-7378 Change-Id: I95fee50db29825e508d276d52c7a3e85e1347ebd (cherry picked from commit 13db95ceb4f18b8cbce1e67447eeaa6ee36cc8ad) --- src/slic3r/GUI/MediaFilePanel.cpp | 7 +- src/slic3r/GUI/Printer/PrinterFileSystem.cpp | 299 ++++++++++++++- src/slic3r/GUI/Printer/PrinterFileSystem.h | 123 +++++-- src/slic3r/GUI/SelectMachine.hpp | 4 +- src/slic3r/GUI/SendToPrinter.cpp | 364 +++++++++++++++++-- src/slic3r/GUI/SendToPrinter.hpp | 16 + 6 files changed, 735 insertions(+), 78 deletions(-) diff --git a/src/slic3r/GUI/MediaFilePanel.cpp b/src/slic3r/GUI/MediaFilePanel.cpp index 9a7b4386e6..a623e077cf 100644 --- a/src/slic3r/GUI/MediaFilePanel.cpp +++ b/src/slic3r/GUI/MediaFilePanel.cpp @@ -297,7 +297,12 @@ void MediaFilePanel::SetMachineObject(MachineObject* obj) case PrinterFileSystem::Initializing: icon = m_bmp_loading; msg = _L("Initializing..."); break; case PrinterFileSystem::Connecting: icon = m_bmp_loading; msg = _L("Connecting..."); break; case PrinterFileSystem::Failed: icon = m_bmp_failed; if (extra != 1) msg = _L("Please check the network and try again. You can restart or update the printer if the issue persists."); break; - case PrinterFileSystem::ListSyncing: icon = m_bmp_loading; msg = _L("Loading file list..."); break; + case PrinterFileSystem::ListSyncing: { + icon = m_bmp_loading; + msg = _L("Loading file list..."); + fs->ListAllFiles(); + break; + } case PrinterFileSystem::ListReady: icon = extra == 0 ? m_bmp_empty : m_bmp_failed; msg = extra == 0 ? _L("No files") : _L("Load failed"); break; } int err = fs->GetLastError(); diff --git a/src/slic3r/GUI/Printer/PrinterFileSystem.cpp b/src/slic3r/GUI/Printer/PrinterFileSystem.cpp index d961971d0e..f4b04f41e4 100644 --- a/src/slic3r/GUI/Printer/PrinterFileSystem.cpp +++ b/src/slic3r/GUI/Printer/PrinterFileSystem.cpp @@ -40,18 +40,30 @@ wxDEFINE_EVENT(EVT_SELECT_CHANGED, wxCommandEvent); wxDEFINE_EVENT(EVT_THUMBNAIL, wxCommandEvent); wxDEFINE_EVENT(EVT_DOWNLOAD, wxCommandEvent); wxDEFINE_EVENT(EVT_RAMDOWNLOAD, wxCommandEvent); +wxDEFINE_EVENT(EVT_MEDIA_ABILITY_CHANGED, wxCommandEvent); +wxDEFINE_EVENT(EVT_UPLOADING, wxCommandEvent); +wxDEFINE_EVENT(EVT_UPLOAD_CHANGED, wxCommandEvent); wxDEFINE_EVENT(EVT_FILE_CALLBACK, wxCommandEvent); static wxBitmap default_thumbnail; static std::map error_messages = { - {PrinterFileSystem::ERROR_PIPE, L("Reconnecting the printer, the operation cannot be completed immediately, please try again later.")}, - {PrinterFileSystem::ERROR_RES_BUSY, L("The device cannot handle more conversations. Please retry later.")}, - {PrinterFileSystem::FILE_NO_EXIST, L("File does not exist.")}, - {PrinterFileSystem::FILE_CHECK_ERR, L("File checksum error. Please retry.")}, - {PrinterFileSystem::FILE_TYPE_ERR, L("Not supported on the current printer version.")}, - {PrinterFileSystem::STORAGE_UNAVAILABLE, L("Storage unavailable, insert SD card.")} + {PrinterFileSystem::ERROR_PIPE, L("Reconnecting the printer, the operation cannot be completed immediately, please try again later.")}, + {PrinterFileSystem::ERROR_RES_BUSY, L("The device cannot handle more conversations. Please retry later.")}, + {PrinterFileSystem::ERROR_TIME_OUT, L("Timeout, please try again.")}, + {PrinterFileSystem::FILE_NO_EXIST, L("File does not exist.")}, + {PrinterFileSystem::FILE_CHECK_ERR, L("File checksum error. Please retry.")}, + {PrinterFileSystem::FILE_TYPE_ERR, L("Not supported on the current printer version.")}, + {PrinterFileSystem::STORAGE_UNAVAILABLE, L("Please check if the SD card is inserted into the printer.\nIf it still cannot be read, you can try formatting the SD card.")}, + {PrinterFileSystem::API_VERSION_UNSUPPORT, L("The firmware version of the printer is too low. Please update the firmware and try again.")}, + {PrinterFileSystem::FILE_EXIST, L("The file already exists, do you want to replace it?")}, + {PrinterFileSystem::STORAGE_SPACE_NOT_ENOUGH, L("Insufficient storage space, please clear the space and try again.")}, + {PrinterFileSystem::FILE_CREATE_ERR, L("File creation failed, please try again.")}, + {PrinterFileSystem::FILE_WRITE_ERR, L("File write failed, please try again.")}, + {PrinterFileSystem::MD5_COMPARE_ERR, L("MD5 verification failed, please try again.")}, + {PrinterFileSystem::FILE_RENAME_ERR, L("File renaming failed, please try again.")}, + {PrinterFileSystem::SEND_ERR, L("File upload failed, please try again.")} }; struct StaticBambuLib : BambuLib { @@ -118,7 +130,6 @@ void PrinterFileSystem::SetFileType(FileType type, std::string const &storage) return; m_status = Status::ListSyncing; SendChangedEvent(EVT_STATUS_CHANGED, m_status); - ListAllFiles(); } void PrinterFileSystem::SetGroupMode(GroupMode mode) @@ -249,6 +260,13 @@ struct PrinterFileSystem::Download : Progress boost::uuids::detail::md5 boost_md5; }; +struct PrinterFileSystem::Upload : Progress +{ + std::string error; + boost::uint32_t frag_id{0}; + MD5_CTX ctx; + boost::filesystem::ifstream ifs; +}; void PrinterFileSystem::GetPickImages(const std::vector &local_paths, const std::vector &targetpaths) { @@ -672,6 +690,16 @@ void PrinterFileSystem::Stop(bool quit) m_cond.notify_all(); } +void PrinterFileSystem::SetUploadFile(const std::string &path, const std::string &name, const std::string &select_storage) +{ + if (!m_upload_file) { + m_upload_file = std::make_unique(); + } + m_upload_file->path = path; + m_upload_file->name = name; + m_upload_file->select_storage = select_storage; +} + void PrinterFileSystem::BuildGroups() { m_group_year.clear(); @@ -1178,6 +1206,194 @@ void PrinterFileSystem::DumpLog(void * thiz, int, tchar const *msg) static_cast(thiz)->Bambu_FreeLogMsg(msg); } +boost::uint32_t PrinterFileSystem::RequestMediaAbility(int api_version) +{ + json req; + req["peer"] = "studio"; + req["api_version"] = api_version; + + return SendRequest( + REQUEST_MEDIA_ABILITY, req, [this](const json &resp, MediaAbilityList &list, auto) -> int { + json abliity_list = resp["storage"]; + list = abliity_list.get(); + return 0; + }, + [this](int result, MediaAbilityList list){ + if (result != 0) { + m_last_error = result; + m_media_ability_list.clear(); + SendChangedEvent(EVT_MEDIA_ABILITY_CHANGED, RequestMediaAbilityStatus::S_FAILED, "", m_last_error); + return result; + } + + m_media_ability_list.swap(list); + SendChangedEvent(EVT_MEDIA_ABILITY_CHANGED, RequestMediaAbilityStatus::S_SUCCESS); + return 0; + }); +} + +void PrinterFileSystem::RequestUploadFile() +{ + json req; + req["type"] = "model"; + req["storage"] = m_upload_file->select_storage; + req["path"] = m_upload_file->name; + + m_upload_file->upload = std::make_unique(); + boost::filesystem::path path = boost::filesystem::path(m_upload_file->path); + boost::system::error_code ec; + boost::uint32_t file_size = boost::filesystem::file_size(path, ec); + + req["total"] = file_size; + m_upload_file->size = file_size; + m_upload_file->upload->total = file_size; + + m_upload_seq = SendRequest( + FILE_UPLOAD, req, + [this](int result, const json& resp, auto) -> int{ + if (result != SUCCESS && result != CONTINUE && result != FILE_EXIST) { + std::string error_msg = ""; + if (result == ERROR_CANCEL) { + error_msg = L("User cancels task."); + } else if (result == FILE_READ_WRITE_ERR || result == FILE_OPEN_ERR) { + error_msg = L("Failed to read file, please try again."); + } + wxLogWarning("PrinterFileSystem::UploadFile error: %d\n", result); + SendChangedEvent(EVT_UPLOAD_CHANGED, FF_UPLOADCANCEL, error_msg, result); + } else if (result == SUCCESS) { + SendChangedEvent(EVT_UPLOADING, 100); + SendChangedEvent(EVT_UPLOAD_CHANGED, FF_UPLOADDONE); + } else if (result == CONTINUE || result == FILE_EXIST) { + if (m_upload_file) { + m_upload_file->chunk_size = resp["chunk_size"]; + m_upload_file->upload->size = resp["offset"]; + m_upload_file->flags |= FF_UPLOADING; + } + + { + boost::unique_lock l(m_mutex); + auto cb = [this, upload_file = m_upload_file, seq = m_upload_seq](std::string &msg) -> int { + return UploadFileTask(upload_file, seq, msg); + }; + m_produce_message_cb_map[m_upload_seq] = cb; + } + + return CONTINUE; + } + + // reset m_upload_file + if (m_upload_file) { + m_upload_file.reset(); + } + return result; + }); +} + +int PrinterFileSystem::UploadFileTask(std::shared_ptr upload_file, boost::uint64_t seq, std::string &msg) +{ + if (!upload_file) + return FILE_OPEN_ERR; + + if (!(upload_file->flags & FF_UPLOADING)) + return FILE_OPEN_ERR; + + auto &upload = upload_file->upload; + if (!upload->ifs.is_open()) { + upload->ifs.open(upload_file->path, std::ios::binary); + if (!upload_file->upload->ifs) { + wxLogWarning("PrinterFileSystem::UploadFile open error: %s\n", wxString::FromUTF8(upload_file->path)); + return FILE_OPEN_ERR; + } + MD5_Init(&upload->ctx); + } + + const boost::uint32_t buffer_size = upload_file->chunk_size * 1024; + char *buffer = new char[buffer_size]; + + upload->ifs.seekg(upload->size, std::ios::beg); + upload->ifs.read(buffer, buffer_size); + boost::int32_t read_size = upload->ifs.gcount(); + + if (read_size <= 0) { + wxLogWarning("PrinterFileSystem::Upload read error.\n"); + upload->ifs.close(); + + if (buffer) { + delete[] buffer; + buffer = nullptr; + } + return FILE_READ_WRITE_ERR; + } + + json req; + req["frag_id"] = upload->frag_id; + req["offset"] = upload->size; + req["size"] = read_size; + + MD5_Update(&upload->ctx, buffer, read_size); + upload->size += read_size; + if (upload->size == upload->total) { + unsigned char digest[16]; + MD5_Final(digest, &upload->ctx); + char md5_str[33]; + for (int j = 0; j < 16; j++) { sprintf(&md5_str[j * 2], "%02X", (unsigned int) digest[j]); } + std::string md5_out = std::string(md5_str); + std::transform(md5_out.begin(), md5_out.end(), md5_out.begin(), ::tolower); + + req["file_md5"] = md5_out; + // OutputDebugStringA(md5_out.c_str()); + // OutputDebugStringA("\n"); + } + + if (m_upload_file && m_upload_file->flags & FF_UPLOADING) { + upload->frag_id++; + upload->progress = upload->size * 100 / upload->total; + int progress = upload->progress == 100 ? 99 : upload->progress; + SendChangedEvent(EVT_UPLOADING, progress); + } + + json root; + + root["cmdtype"] = FILE_UPLOAD; + root["sequence"] = seq; + root["req"] = req; + + std::ostringstream oss; + oss << root; + oss << "\n\n"; + oss << std::string(buffer, read_size); + msg = oss.str(); + + if (buffer) { + delete[] buffer; + buffer = nullptr; + } + + if (upload->size == upload->total) { + upload->ifs.close(); + return SUCCESS; + } + + return CONTINUE; +} + +PrinterFileSystem::MediaAbilityList PrinterFileSystem::GetMediaAbilityList() const +{ + return m_media_ability_list; +} + +void PrinterFileSystem::CancelUploadTask(bool send_cancel_req) +{ + if (!m_upload_file) + return; + + if (send_cancel_req) { + CancelRequest(m_upload_seq); + } else { + CancelRequests2({m_upload_seq}); + } +} + boost::uint32_t PrinterFileSystem::SendRequest(int type, json const &req, callback_t2 const &callback,const std::string& param) { if (m_session.tunnel == nullptr) { @@ -1185,7 +1401,7 @@ boost::uint32_t PrinterFileSystem::SendRequest(int type, json const &req, callba callback(ERROR_PIPE, json(), nullptr); return 0; } - boost::uint32_t seq = m_sequence + m_callbacks.size(); + boost::uint32_t seq = m_sequence + m_callbacks.size(); json root; root["cmdtype"] = type; root["sequence"] = seq; @@ -1199,7 +1415,7 @@ boost::uint32_t PrinterFileSystem::SendRequest(int type, json const &req, callba } // OutputDebugStringA(oss.str().c_str()); // OutputDebugStringA("\n"); - auto msg = oss.str(); + auto msg = oss.str(); boost::unique_lock l(m_mutex); m_messages.push_back(msg); m_callbacks.push_back(callback); @@ -1240,13 +1456,15 @@ void PrinterFileSystem::CancelRequests2(std::vector const &seqs for (auto &f : seqs) { boost::uint32_t seq = f; seq -= m_sequence; - if (size_t(seq) >= m_callbacks.size()) - continue; + if (size_t(seq) >= m_callbacks.size()) continue; auto &c = m_callbacks[seq]; - if (c == nullptr) - continue; + if (c == nullptr) continue; callbacks.emplace_back(f, c); c = nullptr; + + // erase m_produce_message_cb + if (m_produce_message_cb_map.find(seq) != m_produce_message_cb_map.end()) + m_produce_message_cb_map.erase(seq); } while (!m_callbacks.empty() && m_callbacks.front() == nullptr) { m_callbacks.pop_front(); @@ -1273,6 +1491,45 @@ void PrinterFileSystem::RecvMessageThread() break; } } + if (m_messages.empty() && !m_produce_message_cb_map.empty()) { + auto it = m_produce_message_cb_map.begin(); + while(it != m_produce_message_cb_map.end()) { + std::string msg; + auto prodeuce_message_cb = it->second; + l.unlock(); + int res = prodeuce_message_cb(msg); + l.lock(); + if (res == CONTINUE || res == SUCCESS) { + m_messages.emplace_back(msg); + if (res == SUCCESS) { + it = m_produce_message_cb_map.erase(it); + continue; + } + it++; + } else { + int seq2 = it->first - m_sequence; + // erase it + it = m_produce_message_cb_map.erase(it); + if (size_t(seq2) >= m_callbacks.size()) + continue; + auto c = m_callbacks[seq2]; + if (c == nullptr) + continue;; + m_callbacks[seq2] = nullptr; + if (seq2 == 0) { + // if produce message return error, erase callback and sequence should plus + while (!m_callbacks.empty() && m_callbacks.front() == nullptr) { + m_callbacks.pop_front(); + ++m_sequence; + } + } + + l.unlock(); + c(res, json(), nullptr); + l.lock(); + } + } + } if (!m_messages.empty()) { auto & msg = m_messages.front(); // OutputDebugStringA(msg.c_str()); @@ -1356,7 +1613,7 @@ void PrinterFileSystem::HandleResponse(boost::unique_lock &l, Bamb int result2 = c(result, resp, json_end); l.lock(); if (result2 != CONTINUE) { - int seq2 = seq - m_sequence; + int seq2 = seq - m_sequence; m_callbacks[seq2] = callback_t2(); if (seq2 == 0) { while (!m_callbacks.empty() && m_callbacks.front() == nullptr) { @@ -1369,6 +1626,11 @@ void PrinterFileSystem::HandleResponse(boost::unique_lock &l, Bamb CancelRequest(seq); l.lock(); } + + // error should erase m_produce_message_cb + if (m_produce_message_cb_map.find(seq2) != m_produce_message_cb_map.end()) { + m_produce_message_cb_map.erase(seq2); + } } } } @@ -1459,12 +1721,15 @@ void PrinterFileSystem::Reconnect(boost::unique_lock &l, int resul SendChangedEvent(EVT_STATUS_CHANGED, m_status, "", url.size() < 2 ? 1 : m_last_error); m_cond.timed_wait(l, boost::posix_time::seconds(10)); } - m_status = Status::ListSyncing; - SendChangedEvent(EVT_STATUS_CHANGED, m_status); + #ifdef PRINTER_FILE_SYSTEM_TEST PostCallback([this] { SendChangedEvent(EVT_FILE_CHANGED); }); #else - PostCallback([this] { m_task_flags = 0; ListAllFiles(); }); + PostCallback([this] { + m_task_flags = 0; + m_status = Status::ListSyncing; + SendChangedEvent(EVT_STATUS_CHANGED, m_status); + }); #endif } diff --git a/src/slic3r/GUI/Printer/PrinterFileSystem.h b/src/slic3r/GUI/Printer/PrinterFileSystem.h index 259baf7e45..58c40000db 100644 --- a/src/slic3r/GUI/Printer/PrinterFileSystem.h +++ b/src/slic3r/GUI/Printer/PrinterFileSystem.h @@ -23,39 +23,52 @@ wxDECLARE_EVENT(EVT_SELECT_CHANGED, wxCommandEvent); wxDECLARE_EVENT(EVT_THUMBNAIL, wxCommandEvent); wxDECLARE_EVENT(EVT_DOWNLOAD, wxCommandEvent); wxDECLARE_EVENT(EVT_RAMDOWNLOAD, wxCommandEvent); +wxDECLARE_EVENT(EVT_MEDIA_ABILITY_CHANGED, wxCommandEvent); +wxDECLARE_EVENT(EVT_UPLOADING, wxCommandEvent); +wxDECLARE_EVENT(EVT_UPLOAD_CHANGED, wxCommandEvent); class PrinterFileSystem : public wxEvtHandler, public boost::enable_shared_from_this, BambuLib { static const int CTRL_TYPE = 0x3001; enum { - LIST_INFO = 0x0001, - SUB_FILE = 0x0002, - FILE_DEL = 0x0003, - FILE_DOWNLOAD = 0X0004, - NOTIFY_FIRST = 0x0100, - LIST_CHANGE_NOTIFY = 0x0100, - LIST_RESYNC_NOTIFY = 0x0101, - TASK_CANCEL = 0x1000 + LIST_INFO = 0x0001, + SUB_FILE = 0x0002, + FILE_DEL = 0x0003, + FILE_DOWNLOAD = 0x0004, + FILE_UPLOAD = 0x0005, + REQUEST_MEDIA_ABILITY = 0x0007, + NOTIFY_FIRST = 0x0100, + LIST_CHANGE_NOTIFY = 0x0100, + LIST_RESYNC_NOTIFY = 0x0101, + TASK_CANCEL = 0x1000 }; public: enum { - SUCCESS = 0, - CONTINUE = 1, - ERROR_JSON = 2, - ERROR_PIPE = 3, - ERROR_CANCEL = 4, - ERROR_RES_BUSY = 5, - - FILE_NO_EXIST = 10, - FILE_NAME_INVALID = 11, - FILE_SIZE_ERR = 12, - FILE_OPEN_ERR = 13, - FILE_READ_WRITE_ERR = 14, - FILE_CHECK_ERR = 15, - FILE_TYPE_ERR = 16, - STORAGE_UNAVAILABLE = 17, + SUCCESS = 0, + CONTINUE = 1, + ERROR_JSON = 2, + ERROR_PIPE = 3, + ERROR_CANCEL = 4, + ERROR_RES_BUSY = 5, + ERROR_TIME_OUT = 6, + FILE_NO_EXIST = 10, + FILE_NAME_INVALID = 11, + FILE_SIZE_ERR = 12, + FILE_OPEN_ERR = 13, + FILE_READ_WRITE_ERR = 14, + FILE_CHECK_ERR = 15, + FILE_TYPE_ERR = 16, + STORAGE_UNAVAILABLE = 17, + API_VERSION_UNSUPPORT = 18, + FILE_EXIST = 19, + STORAGE_SPACE_NOT_ENOUGH = 20, + FILE_CREATE_ERR = 21, + FILE_WRITE_ERR = 22, + MD5_COMPARE_ERR = 23, + FILE_RENAME_ERR = 24, + SEND_ERR = 25, }; @@ -91,12 +104,28 @@ public: template using Callback = std::function; enum Flags { - FF_SELECT = 1, - FF_THUMNAIL = 2, // Thumbnail ready - FF_DOWNLOAD = 4, // Request download - FF_DELETED = 8, // Request delete - FF_FETCH_MODEL = 16,// Request model - FF_THUMNAIL_RETRY = 0x100, // Thumbnail need retry + FF_SELECT = 1, + FF_THUMNAIL = 2, // Thumbnail ready + FF_DOWNLOAD = 4, // Request download + FF_DELETED = 8, // Request delete + FF_FETCH_MODEL = 16, // Request model + FF_UPLOADING = 1 << 5, // File uploading + FF_UPLOADDONE = 1 << 6, // File upload done + FF_UPLOADCANCEL = 1 << 7, // File upload cancel + FF_THUMNAIL_RETRY = 0x100, // Thumbnail need retry + }; + + enum UploadStatus + { + Uploading = 1 << 0, + UploadDone = 1 << 1, + UploadCancel = 1 << 2, + }; + + enum RequestMediaAbilityStatus + { + S_SUCCESS, + S_FAILED }; struct Progress @@ -107,6 +136,7 @@ public: }; struct Download; + struct Upload; struct File { @@ -129,9 +159,23 @@ public: friend bool operator<(File const & l, File const & r) { return l.time > r.time; } }; + struct UploadFile + { + std::string name; + std::string path; + std::string select_storage; + int flags{0}; + boost::uint32_t size{0}; + boost::uint32_t chunk_size{0}; // KB + std::unique_ptr upload; + + bool IsUploading() const { return flags & FF_UPLOADING; } + }; + struct Void {}; typedef std::vector FileList; + typedef std::vector MediaAbilityList; void ListAllFiles(); @@ -198,6 +242,16 @@ public: void Stop(bool quit = false); + boost::uint32_t RequestMediaAbility(int api_version); + + void RequestUploadFile(); + + MediaAbilityList GetMediaAbilityList() const; + + void SetUploadFile(const std::string& path, const std::string& name, const std::string& select_storage); + + void CancelUploadTask(bool send_cancel_req = true); + private: void BuildGroups(); @@ -230,12 +284,14 @@ private: typedef std::function callback_t2; + typedef std::function callback_t3; + template boost::uint32_t SendRequest(int type, json const &req, Translator const &translator, Callback const &callback, const std::string ¶m = "") { auto c = [translator, callback, this](int result, json const &resp, unsigned char const *data) -> int { T t; - if (result == 0 || result == CONTINUE) { + if (result == 0 || result == CONTINUE || result == FILE_EXIST) { try { int n = (translator != nullptr) ? translator(resp, t, data) : 0; result = n == 0 ? result : n; @@ -301,6 +357,8 @@ private: void PostCallback(std::function const & callback); + int UploadFileTask(std::shared_ptr upload_file, boost::uint64_t seq, std::string &msg); + protected: FileType m_file_type = F_INVALID_TYPE; std::string m_file_storage; @@ -310,6 +368,7 @@ protected: std::vector m_group_year; std::vector m_group_month; std::vector m_group_flags; + std::shared_ptr m_upload_file; private: size_t m_select_count = 0; @@ -329,6 +388,7 @@ private: boost::uint32_t m_sequence = 0; boost::uint32_t m_download_seq = 0; boost::uint32_t m_fetch_model_seq = 0; + boost::uint32_t m_upload_seq = 0; std::deque m_messages; std::deque m_callbacks; std::deque m_notifies; @@ -338,6 +398,9 @@ private: boost::thread m_recv_thread; Status m_status; int m_last_error = 0; + + MediaAbilityList m_media_ability_list; + std::map m_produce_message_cb_map; }; #endif // !slic3r_GUI_PrinterFileSystem_h_ diff --git a/src/slic3r/GUI/SelectMachine.hpp b/src/slic3r/GUI/SelectMachine.hpp index 0c22c4c488..bf0de2556e 100644 --- a/src/slic3r/GUI/SelectMachine.hpp +++ b/src/slic3r/GUI/SelectMachine.hpp @@ -88,7 +88,9 @@ enum PrintDialogStatus { PrintStatusNotSupportedPrintAll, PrintStatusBlankPlate, PrintStatusUnsupportedPrinter, - PrintStatusTimelapseWarning + PrintStatusTimelapseWarning, + PrintStatusPublicInitFailed, + PrintStatusPublicUploadFiled }; class Material diff --git a/src/slic3r/GUI/SendToPrinter.cpp b/src/slic3r/GUI/SendToPrinter.cpp index f8cc7fa3a3..65218ebe1f 100644 --- a/src/slic3r/GUI/SendToPrinter.cpp +++ b/src/slic3r/GUI/SendToPrinter.cpp @@ -29,12 +29,13 @@ namespace GUI { #define LIST_REFRESH_INTERVAL 200 #define MACHINE_LIST_REFRESH_INTERVAL 2000 +constexpr int timeout_period = 15000; // ms + wxDEFINE_EVENT(EVT_UPDATE_USER_MACHINE_LIST, wxCommandEvent); wxDEFINE_EVENT(EVT_PRINT_JOB_CANCEL, wxCommandEvent); wxDEFINE_EVENT(EVT_SEND_JOB_SUCCESS, wxCommandEvent); wxDEFINE_EVENT(EVT_CLEAR_IPADDRESS, wxCommandEvent); - void SendToPrinterDialog::stripWhiteSpace(std::string& str) { if (str == "") { return; } @@ -260,6 +261,14 @@ SendToPrinterDialog::SendToPrinterDialog(Plater *plater) m_button_refresh->Bind(wxEVT_BUTTON, &SendToPrinterDialog::on_refresh, this); m_sizer_printer->Add(m_button_refresh, 0, wxALL | wxLEFT, FromDIP(5)); + + /*select storage*/ + m_storage_panel = new wxPanel(this); + m_storage_panel->SetBackgroundColour(*wxWHITE); + m_storage_sizer = new wxBoxSizer(wxHORIZONTAL); + m_storage_panel->SetSizer(m_storage_sizer); + m_storage_panel->Layout(); + m_statictext_printer_msg = new wxStaticText(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_HORIZONTAL); m_statictext_printer_msg->SetFont(::Label::Body_13); m_statictext_printer_msg->SetForegroundColour(*wxBLACK); @@ -517,6 +526,7 @@ SendToPrinterDialog::SendToPrinterDialog(Plater *plater) m_sizer_main->Add(m_line_materia, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(30)); m_sizer_main->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(12)); m_sizer_main->Add(m_sizer_printer, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(30)); + m_sizer_main->Add(m_storage_panel, 0, wxALIGN_CENTER|wxTOP, FromDIP(8)); m_sizer_main->Add(0, 0, 0, wxEXPAND | wxTOP, FromDIP(11)); m_sizer_main->Add(m_statictext_printer_msg, 0, wxALIGN_CENTER_HORIZONTAL, 0); m_sizer_main->Add(0, 1, 0, wxTOP, FromDIP(22)); @@ -538,6 +548,49 @@ SendToPrinterDialog::SendToPrinterDialog(Plater *plater) wxGetApp().UpdateDlgDarkUI(this); } +std::string SendToPrinterDialog::get_storage_selected() +{ + for (const auto& radio : m_storage_radioBox) { + if (radio->GetValue()) { + return radio->GetLabel().ToStdString(); + } + } + return ""; +} + +void SendToPrinterDialog::update_storage_list(std::vector storages) +{ + m_storage_radioBox.clear(); + m_storage_panel->DestroyChildren(); + + for (int i=0; i < storages.size(); i++) { + RadioBox* radiobox = new RadioBox(m_storage_panel); + Label* storage_text = new Label(m_storage_panel, storages[i]); + radiobox->SetLabel(storages[i]); + radiobox->Bind(wxEVT_LEFT_DOWN, [this, radiobox](auto& e) { + for (const auto& radio : m_storage_radioBox) { + radio->SetValue(false); + } + radiobox->SetValue(true); + }); + + if (m_storage_radioBox.size() > 0) { + m_storage_sizer->Add(0, 0, 0, wxEXPAND|wxLEFT, FromDIP(6)); + auto radio = m_storage_radioBox.front(); + radio->SetValue(true); + } + + m_storage_sizer->Add(radiobox, 0, wxALIGN_CENTER, 0); + m_storage_sizer->Add(0, 0, 0, wxEXPAND|wxLEFT, FromDIP(6)); + m_storage_sizer->Add(storage_text, 0, wxALIGN_CENTER,0); + m_storage_radioBox.push_back(radiobox); + } + + m_storage_panel->Layout(); + m_storage_panel->Fit(); + Layout(); + Fit(); +} void SendToPrinterDialog::update_print_error_info(int code, std::string msg, std::string extra) { m_print_error_code = code; @@ -669,6 +722,15 @@ void SendToPrinterDialog::init_timer() void SendToPrinterDialog::on_cancel(wxCloseEvent &event) { m_worker->cancel_all(); + if (m_file_sys) { + m_file_sys->CancelUploadTask(); + + if (m_task_timer && m_task_timer->IsRunning()) { + m_task_timer->Stop(); + m_task_timer.reset(); + } + } + this->EndModal(wxID_CANCEL); } @@ -706,6 +768,9 @@ void SendToPrinterDialog::on_ok(wxCommandEvent &event) m_status_bar->set_cancel_callback_fina([this]() { BOOST_LOG_TRIVIAL(info) << "print_job: enter canceled"; m_worker->cancel_all(); + if (m_file_sys) { + m_file_sys->CancelUploadTask(); + } m_is_canceled = true; wxCommandEvent* event = new wxCommandEvent(EVT_PRINT_JOB_CANCEL); wxQueueEvent(this, event); @@ -760,42 +825,70 @@ void SendToPrinterDialog::on_ok(wxCommandEvent &event) fs::path default_output_file_path = boost::filesystem::path(default_output_file.c_str()); file_name = default_output_file_path.filename().string(); }*/ + if (obj_->is_lan_mode_printer()) { + update_print_status_msg(wxEmptyString, false, false); + if (m_file_sys) { + PrintPrepareData print_data; + m_plater->get_print_job_data(&print_data); + std::string project_name = m_current_project_name.utf8_string() + ".3mf"; + std::string _3mf_path = print_data._3mf_path.string(); + m_file_sys->SetUploadFile(_3mf_path, project_name, get_storage_selected()); + m_file_sys->RequestUploadFile(); + // time out + if (m_task_timer && m_task_timer->IsRunning()) + m_task_timer->Stop(); + m_task_timer.reset(new wxTimer()); + m_task_timer->SetOwner(this); + this->Bind( + wxEVT_TIMER, + [this, wfs = boost::weak_ptr(m_file_sys)](auto e) { + show_status(PrintDialogStatus::PrintStatusPublicUploadFiled); + + boost::shared_ptr fs(wfs.lock()); + if (!fs) return; + fs->CancelUploadTask(false); + update_print_status_msg(_L("Upload file timeout, please check if the firmware version supports it."), false, true); + }, + m_task_timer->GetId()); + m_task_timer->StartOnce(timeout_period); + } + } else { auto m_send_job = std::make_unique(m_printer_last_select); - m_send_job->m_dev_ip = obj_->dev_ip; - m_send_job->m_access_code = obj_->get_access_code(); + m_send_job->m_dev_ip = obj_->dev_ip; + m_send_job->m_access_code = obj_->get_access_code(); #if !BBL_RELEASE_TO_PUBLIC - m_send_job->m_local_use_ssl_for_ftp = wxGetApp().app_config->get("enable_ssl_for_ftp") == "true" ? true : false; - m_send_job->m_local_use_ssl_for_mqtt = wxGetApp().app_config->get("enable_ssl_for_mqtt") == "true" ? true : false; + m_send_job->m_local_use_ssl_for_ftp = wxGetApp().app_config->get("enable_ssl_for_ftp") == "true" ? true : false; + m_send_job->m_local_use_ssl_for_mqtt = wxGetApp().app_config->get("enable_ssl_for_mqtt") == "true" ? true : false; #else - m_send_job->m_local_use_ssl_for_ftp = obj_->local_use_ssl_for_ftp; - m_send_job->m_local_use_ssl_for_mqtt = obj_->local_use_ssl_for_mqtt; + m_send_job->m_local_use_ssl_for_ftp = obj_->local_use_ssl_for_ftp; + m_send_job->m_local_use_ssl_for_mqtt = obj_->local_use_ssl_for_mqtt; #endif - m_send_job->connection_type = obj_->connection_type(); - m_send_job->cloud_print_only = true; - m_send_job->has_sdcard = obj_->get_sdcard_state() == MachineObject::SdcardState::HAS_SDCARD_NORMAL; - m_send_job->set_project_name(m_current_project_name.utf8_string()); + m_send_job->connection_type = obj_->connection_type(); + m_send_job->cloud_print_only = true; + m_send_job->has_sdcard = obj_->get_sdcard_state() == MachineObject::SdcardState::HAS_SDCARD_NORMAL; + m_send_job->set_project_name(m_current_project_name.utf8_string()); - enable_prepare_mode = false; + enable_prepare_mode = false; - m_send_job->on_check_ip_address_fail([this](int result) { - wxCommandEvent* evt = new wxCommandEvent(EVT_CLEAR_IPADDRESS); - wxQueueEvent(this, evt); - wxGetApp().show_ip_address_enter_dialog(); - }); + m_send_job->on_check_ip_address_fail([this](int result) { + wxCommandEvent *evt = new wxCommandEvent(EVT_CLEAR_IPADDRESS); + wxQueueEvent(this, evt); + wxGetApp().show_ip_address_enter_dialog(); + }); - if (obj_->is_lan_mode_printer()) { - m_send_job->set_check_mode(); - m_send_job->check_and_continue(); + if (obj_->is_lan_mode_printer()) { + m_send_job->set_check_mode(); + m_send_job->check_and_continue(); + } + + replace_job(*m_worker, std::move(m_send_job)); } - - replace_job(*m_worker, std::move(m_send_job)); - BOOST_LOG_TRIVIAL(info) << "send_job: send print job"; } @@ -837,9 +930,10 @@ void SendToPrinterDialog::update_user_machine_list() void SendToPrinterDialog::on_refresh(wxCommandEvent &event) { BOOST_LOG_TRIVIAL(info) << "m_printer_last_select: on_refresh"; - show_status(PrintDialogStatus::PrintStatusRefreshingMachineList); - - update_user_machine_list(); + /* show_status(PrintDialogStatus::PrintStatusRefreshingMachineList); + update_user_machine_list();*/ + /*todo refresh*/ + if (m_file_sys) { m_file_sys->Retry(); } } void SendToPrinterDialog::on_print_job_cancel(wxCommandEvent &evt) @@ -991,8 +1085,11 @@ void SendToPrinterDialog::on_selection_changed(wxCommandEvent &event) obj->command_request_push_all(); if (!dev->get_selected_machine()) { dev->set_selected_machine(m_printer_last_select, true); + if (m_file_sys) m_file_sys.reset(); }else if (dev->get_selected_machine()->dev_id != m_printer_last_select) { + update_storage_list(std::vector()); dev->set_selected_machine(m_printer_last_select, true); + if (m_file_sys) m_file_sys.reset(); } } else { @@ -1076,8 +1173,137 @@ void SendToPrinterDialog::update_show_status() } if (!m_is_in_sending_mode) { - show_status(PrintDialogStatus::PrintStatusReadingFinished); - return; + if (obj_->connection_type() == "lan") { + show_status(PrintDialogStatus::PrintStatusReadingFinished); + return; + } else if (obj_->connection_type() == "cloud") { + Enable(obj_ && obj_->is_connected() && obj_->m_push_count > 0); + std::string dev_id = obj_->dev_ip; + if (m_file_sys) { + if (dev_id == m_device_select) { + if ((m_waiting_enable && IsEnabled()) || (m_waiting_support && obj_->file_remote)) + m_file_sys->Retry(); + return; + } + } + + m_device_select.swap(dev_id); + m_file_sys = boost::make_shared(); + m_file_sys->Attached(); + + m_file_sys->Bind(EVT_STATUS_CHANGED, [this, wfs = boost::weak_ptr(m_file_sys)](auto e) { + e.Skip(); + boost::shared_ptr fs(wfs.lock()); + if (!fs) return; + + wxString msg; + int status = e.GetInt(); + int extra = e.GetExtraLong(); + switch (status) { + case PrinterFileSystem::Initializing: + case PrinterFileSystem::Connecting: show_status(PrintDialogStatus::PrintStatusReading); break; + case PrinterFileSystem::ListSyncing: { + show_status(PrintDialogStatus::PrintStatusReading); + boost::uint32_t seq = fs->RequestMediaAbility(3); + + if (m_task_timer && m_task_timer->IsRunning()) + m_task_timer->Stop(); + + m_task_timer.reset(new wxTimer()); + m_task_timer->SetOwner(this); + + this->Bind(wxEVT_TIMER, [this, wfs_1 = boost::weak_ptr(fs), seq](auto e) { + show_status(PrintDialogStatus::PrintStatusPublicUploadFiled); + boost::shared_ptr fs_1(wfs_1.lock()); + if (!fs_1) return; + fs_1->CancelUploadTask(false); + update_print_status_msg(_L("Media capability acquisition timeout, please check if the firmware version supports it."), false, true); + }, m_task_timer->GetId()); + m_task_timer->StartOnce(timeout_period); + + break; + } + case PrinterFileSystem::Failed: msg = _L("Please check the network and try again, You can restart or update the printer if the issue persists."); break; + } + + if (!msg.empty()) { + show_status(PrintDialogStatus::PrintStatusPublicInitFailed); + update_print_status_msg(msg, false, true); + } + + if (e.GetInt() == PrinterFileSystem::Initializing) { + CallAfter([=] { + boost::shared_ptr fs(wfs.lock()); + if (!fs) return; + fetchUrl(boost::weak_ptr(fs)); + }); + } + }); + + m_file_sys->Bind(EVT_MEDIA_ABILITY_CHANGED, [this, wfs = boost::weak_ptr(m_file_sys)](auto e) { + boost::shared_ptr fs(wfs.lock()); + if (!fs) return; + + if (m_task_timer && m_task_timer->IsRunning()) { + m_task_timer->Stop(); + m_task_timer.reset(); + } + + m_ability_list = fs->GetMediaAbilityList(); + + if (e.GetInt() == PrinterFileSystem::RequestMediaAbilityStatus::S_SUCCESS) { + update_storage_list(m_ability_list); + show_status(PrintDialogStatus::PrintStatusReadingFinished); + } else { + show_status(PrintDialogStatus::PrintStatusPublicInitFailed); + update_print_status_msg(e.GetString(), false, true); + } + }); + + m_file_sys->Bind(EVT_UPLOADING, [this, wfs = boost::weak_ptr(m_file_sys)](auto e) { + boost::shared_ptr fs(wfs.lock()); + if (!fs) return; + int progress = e.GetInt(); + m_status_bar->set_progress(10 + std::floor(progress * 0.9)); + + if (m_task_timer && m_task_timer->IsRunning()) m_task_timer->Stop(); + + if (progress == 99) { + m_task_timer.reset(new wxTimer()); + m_task_timer->SetOwner(this); + + this->Bind( + wxEVT_TIMER, + [this, wfs = boost::weak_ptr(m_file_sys)](auto e) { + show_status(PrintDialogStatus::PrintStatusPublicUploadFiled); + boost::shared_ptr fs(wfs.lock()); + if (!fs) return; + fs->CancelUploadTask(false); + update_print_status_msg(_L("Upload file timeout, please check if the firmware version supports it."), false, true); + }, + m_task_timer->GetId()); + m_task_timer->StartOnce(timeout_period); + } + }); + m_file_sys->Bind(EVT_UPLOAD_CHANGED, [this, wfs = boost::weak_ptr(m_file_sys)](auto e) { + boost::shared_ptr fs(wfs.lock()); + if (!fs) return; + + if (e.GetInt() == PrinterFileSystem::FF_UPLOADDONE) { + show_status(PrintDialogStatus::PrintStatusReadingFinished); + wxCommandEvent *evt = new wxCommandEvent(m_plater->get_send_finished_event()); + evt->SetString(from_u8(m_current_project_name.utf8_string())); + wxQueueEvent(m_plater, evt); + } else if (PrinterFileSystem::FF_UPLOADCANCEL) { + show_status(PrintDialogStatus::PrintStatusPublicUploadFiled); + wxString err_msg = e.GetString(); + if (err_msg.IsEmpty()) + err_msg = _u8L("Sending failed, please try again!"); + update_print_status_msg(err_msg, false, true); + } + }); + m_file_sys->Start(); + } } } @@ -1139,7 +1365,7 @@ void SendToPrinterDialog::show_status(PrintDialogStatus status, std::vector()); wxGetApp().reset_to_active(); set_default(); update_user_machine_list(); @@ -1383,12 +1617,84 @@ bool SendToPrinterDialog::Show(bool show) Layout(); Fit(); if (show) { CenterOnParent(); } + + if (m_file_sys) { + show ? m_file_sys->Start() : m_file_sys->Stop(); + } + return DPIDialog::Show(show); } +extern wxString hide_passwd(wxString url, std::vector const &passwords); +extern void refresh_agora_url(char const *device, char const *dev_ver, char const *channel, void *context, void (*callback)(void *context, char const *url)); + +void SendToPrinterDialog::fetchUrl(boost::weak_ptr wfs) +{ + boost::shared_ptr fs(wfs.lock()); + if (!fs) return; + + if (!IsEnabled()) { + m_waiting_enable = true; + fs->SetUrl("0"); + return; + } + + m_waiting_enable = false; + DeviceManager *dm = GUI::wxGetApp().getDeviceManager(); + MachineObject *obj = dm->get_selected_machine(); + + std::string dev_ver = obj->get_ota_version(); + std::string dev_id = obj->dev_id; + int remote_proto = obj->file_remote; + if (!remote_proto) { + m_waiting_support = true; + fs->SetUrl("0"); + return; + } + + if (obj->is_camera_busy_off()) { + fs->SetUrl("0"); + return; + } + + m_waiting_support = false; + NetworkAgent *agent = wxGetApp().getAgent(); + std::string agent_version = agent ? agent->get_version() : ""; + + if (agent) { + std::string protocols[] = {"", "\"tutk\"", "\"agora\"", "\"tutk\",\"agora\""}; + agent->get_camera_url(obj->dev_id + "|" + dev_ver + "|" + protocols[remote_proto], + [this, wfs, m = dev_id, v = agent->get_version(), dv = dev_ver](std::string url) { + if (boost::algorithm::starts_with(url, "bambu:///")) { + url += "&device=" + m; + url += "&net_ver=" + v; + url += "&dev_ver=" + dv; + url += "&refresh_url=" + boost::lexical_cast(&refresh_agora_url); + url += "&cli_id=" + wxGetApp().app_config->get("slicer_uuid"); + url += "&cli_ver=" + std::string(SLIC3R_VERSION); + } + BOOST_LOG_TRIVIAL(info) << "SendToPrinter::fetchUrl: camera_url: " << hide_passwd(url, {"?uid=", "authkey=", "passwd="}); + std::cout << "SendToPrinter::fetchUrl: camera_url: " << hide_passwd(url, {"?uid=", "authkey=", "passwd="}); + CallAfter([=] { + boost::shared_ptr fs(wfs.lock()); + if (!fs) return; + if (boost::algorithm::starts_with(url, "bambu:///")) { + fs->SetUrl(url); + } else { + fs->SetUrl("3"); + } + }); + }); + } + + return; +} + SendToPrinterDialog::~SendToPrinterDialog() { delete m_refresh_timer; + if (m_task_timer && m_task_timer->IsRunning()) + m_task_timer->Stop(); } } diff --git a/src/slic3r/GUI/SendToPrinter.hpp b/src/slic3r/GUI/SendToPrinter.hpp index 9f477be993..7534f0e8a0 100644 --- a/src/slic3r/GUI/SendToPrinter.hpp +++ b/src/slic3r/GUI/SendToPrinter.hpp @@ -38,6 +38,7 @@ #include "Widgets/ScrolledWindow.hpp" #include #include +#include "Printer/PrinterFileSystem.h" namespace Slic3r { namespace GUI { @@ -62,6 +63,7 @@ private: std::string m_print_error_extra; std::string m_print_info; std::string m_printer_last_select; + std::string m_device_select; wxString m_current_project_name; TextInput* m_rename_input{ nullptr }; @@ -83,6 +85,8 @@ private: wxPanel* m_panel_image{ nullptr }; wxPanel* m_rename_normal_panel{ nullptr }; wxPanel* m_line_materia{ nullptr }; + wxBoxSizer* m_storage_sizer{ nullptr }; + wxPanel* m_storage_panel{ nullptr }; wxSimplebook* m_simplebook{ nullptr }; wxStaticText* m_statictext_finish{ nullptr }; wxStaticText* m_stext_sending{ nullptr }; @@ -111,10 +115,17 @@ private: wxColour m_colour_def_color{ wxColour(255, 255, 255) }; wxColour m_colour_bold_color{ wxColour(38, 46, 48) }; wxTimer* m_refresh_timer{ nullptr }; + std::unique_ptr m_task_timer{ nullptr }; std::shared_ptr m_status_bar; std::unique_ptr m_worker; wxScrolledWindow* m_sw_print_failed_info{nullptr}; std::shared_ptr m_token = std::make_shared(0); + std::vector m_storage_radioBox; + + bool m_waiting_support{ false }; + bool m_waiting_enable{ false }; + boost::shared_ptr m_file_sys; + std::vector m_ability_list; public: SendToPrinterDialog(Plater* plater = nullptr); @@ -153,8 +164,13 @@ public: void show_print_failed_info(bool show, int code = 0, wxString description = wxEmptyString, wxString extra = wxEmptyString); void update_print_error_info(int code, std::string msg, std::string extra); void on_change_color_mode() { wxGetApp().UpdateDlgDarkUI(this); } + void update_storage_list(std::vector storages); + std::string get_storage_selected(); + wxString format_text(wxString& m_msg); std::vector sort_string(std::vector strArray); + + void fetchUrl(boost::weak_ptr wfs); }; wxDECLARE_EVENT(EVT_CLEAR_IPADDRESS, wxCommandEvent); From c64ee8156c74c8be87a593e06f56e9176d9dd962 Mon Sep 17 00:00:00 2001 From: Noisyfox Date: Fri, 26 Sep 2025 22:29:23 +0800 Subject: [PATCH 21/27] Fix crash --- resources/images/refresh_printer.svg | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 resources/images/refresh_printer.svg diff --git a/resources/images/refresh_printer.svg b/resources/images/refresh_printer.svg new file mode 100644 index 0000000000..e2e123893f --- /dev/null +++ b/resources/images/refresh_printer.svg @@ -0,0 +1,3 @@ + + + From 042cf846144267538910da28c3a474bd44f91363 Mon Sep 17 00:00:00 2001 From: Noisyfox Date: Fri, 26 Sep 2025 23:00:03 +0800 Subject: [PATCH 22/27] Update color --- src/slic3r/GUI/PartSkipDialog.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/PartSkipDialog.cpp b/src/slic3r/GUI/PartSkipDialog.cpp index 4ce45c3a18..6dbd6ff546 100644 --- a/src/slic3r/GUI/PartSkipDialog.cpp +++ b/src/slic3r/GUI/PartSkipDialog.cpp @@ -56,9 +56,9 @@ static StateColor btn_bg_gray(std::pair(wxColour(194, 194, 194), std::pair(wxColour(194, 194, 194), StateColor::Hovered), std::pair(wxColour(194, 194, 194), StateColor::Normal)); -static StateColor btn_bg_green(std::pair(wxColour(27, 136, 68), StateColor::Pressed), - std::pair(wxColour(61, 203, 115), StateColor::Hovered), - std::pair(wxColour(0, 177, 66), StateColor::Normal)); +static StateColor btn_bg_green(std::pair(wxColour(0, 137, 123), StateColor::Pressed), + std::pair(wxColour(38, 166, 154), StateColor::Hovered), + std::pair(wxColour(0, 150, 136), StateColor::Normal)); PartSkipDialog::PartSkipDialog(wxWindow *parent) : DPIDialog(parent, wxID_ANY, _L("Skip Objects"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX) { @@ -174,7 +174,7 @@ PartSkipDialog::PartSkipDialog(wxWindow *parent) : DPIDialog(parent, wxID_ANY, _ m_cnt_label = new Label(m_book_third_panel, wxEmptyString); m_cnt_label->Wrap(-1); m_cnt_label->SetBackgroundColour(*wxWHITE); - m_cnt_label->SetForegroundColour(wxColour(0, 174, 66)); + m_cnt_label->SetForegroundColour(wxColour(0, 150, 136)); m_cnt_label->SetFont(Label::Head_16); m_cnt_label->SetSize(wxSize(-1, FromDIP(20))); m_cnt_label->SetMaxSize(wxSize(-1, FromDIP(20))); @@ -186,7 +186,7 @@ PartSkipDialog::PartSkipDialog(wxWindow *parent) : DPIDialog(parent, wxID_ANY, _ m_apply_btn = new Button(m_book_third_panel, _L("Skip")); m_apply_btn->SetBackgroundColor(btn_bg_gray); - m_apply_btn->SetTextColor(*wxWHITE); + m_apply_btn->SetTextColor(wxColour("#FFFFFE")); // m_apply_btn->SetBorderColor(wxColour(38, 46, 48)); m_apply_btn->SetFont(Label::Body_14); m_apply_btn->SetSize(wxSize(FromDIP(80), FromDIP(32))); @@ -619,7 +619,7 @@ void PartSkipDialog::OnSwitchDrag(wxCommandEvent &event) m_switch_drag_btn->SetIcon("canvas_drag"); } else { m_is_drag = true; - m_switch_drag_btn->SetBackgroundColor(wxColour(0, 174, 66)); + m_switch_drag_btn->SetBackgroundColor(wxColour(0, 150, 136)); m_switch_drag_btn->SetIcon("canvas_drag_active"); } m_canvas->SwitchDrag(m_is_drag); @@ -757,11 +757,11 @@ void PartSkipDialog::InitDialogUI() checkbox->Enable(false); } label->SetLabel(wxString::FromUTF8(m_parts_name[part_id])); - label->SetBackgroundColour(*wxWHITE); - label->SetForegroundColour(wxColor(107, 107, 107)); + label->SetBackgroundColour(StateColor::darkModeColorFor(*wxWHITE)); + label->SetForegroundColour(StateColor::darkModeColorFor(wxColor(107, 107, 107))); label->Wrap(-1); label->SetMinSize(wxSize(-1, FromDIP(18))); - checkbox->SetBackgroundColour(*wxWHITE); + checkbox->SetBackgroundColour(StateColor::darkModeColorFor(*wxWHITE)); checkbox->SetMinSize(wxSize(FromDIP(18), FromDIP(18))); checkbox->SetMaxSize(wxSize(FromDIP(18), FromDIP(18))); From 1331e51dae52bc560122d2169a6f047f297f2f5e Mon Sep 17 00:00:00 2001 From: "xin.zhang" Date: Mon, 14 Jul 2025 16:45:15 +0800 Subject: [PATCH 23/27] FIX: The value maybe string or int JIRA: [STUDIO-13018] Change-Id: I4af161431fb51e1e0234bc37de8346b45f3ca4b7 (cherry picked from commit 4e2934685033c352ea2c3baded585381c285a90d) --- src/slic3r/GUI/DeviceManager.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/DeviceManager.cpp b/src/slic3r/GUI/DeviceManager.cpp index 7a6629c456..f4df504896 100644 --- a/src/slic3r/GUI/DeviceManager.cpp +++ b/src/slic3r/GUI/DeviceManager.cpp @@ -2989,7 +2989,18 @@ int MachineObject::parse_json(std::string payload, bool key_field_only) } } if (j_pre["print"].contains("plate_idx")){ // && m_plate_index == -1 - m_plate_index = j_pre["print"]["plate_idx"].get(); + if (j_pre["print"]["plate_idx"].is_number()) + { + m_plate_index = j_pre["print"]["plate_idx"].get(); + } + else if (j_pre["print"]["plate_idx"].is_string()) + { + try + { + m_plate_index = std::stoi(j_pre["print"]["plate_idx"].get()); + } + catch (...) { BOOST_LOG_TRIVIAL(error) << "parse_json: failed to convert plate_idx to int"; } + } } } if (j_pre.contains("system")) { From d2fc250e5f5fe29455d105b5a11dbc3537659359 Mon Sep 17 00:00:00 2001 From: hemai Date: Mon, 21 Jul 2025 09:24:19 +0800 Subject: [PATCH 24/27] FIX: change AMS n3s ams_id & tray_id from 512 to 128 Jira: [STUDIO-13477] Change-Id: I3844cb59df18220a22d8ff7524216b9fff2b6632 (cherry picked from commit 8c7b66d24b1e795c3070883f3f439cf160b38037) --- src/slic3r/GUI/PartSkipDialog.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/PartSkipDialog.cpp b/src/slic3r/GUI/PartSkipDialog.cpp index 6dbd6ff546..e133b99b92 100644 --- a/src/slic3r/GUI/PartSkipDialog.cpp +++ b/src/slic3r/GUI/PartSkipDialog.cpp @@ -387,7 +387,8 @@ std::string PartSkipDialog::create_tmp_path() buf << "/bamboo_task/"; buf << m_timestamp; if (m_obj) { - buf << m_obj->dev_id << "_"; + buf << m_obj->dev_id.substr(0, 3) << "_"; + buf << m_obj->dev_id.substr(m_obj->dev_id.length() - 3, 3) << "_"; buf << m_obj->job_id_ << "/"; } else { buf << 1 << "_" << 1 << "/"; From df1c5722ef60c9a80f7395723887253f216f35bd Mon Sep 17 00:00:00 2001 From: Noisyfox Date: Sun, 5 Oct 2025 16:06:40 +0800 Subject: [PATCH 25/27] Revert "OpenCV don't build its own libjpeg, to avoid symbol conflict" This reverts commit 7a90b26e57449567f864efce2d312f348a0d4328. --- deps/OpenCV/OpenCV.cmake | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/deps/OpenCV/OpenCV.cmake b/deps/OpenCV/OpenCV.cmake index 4446936fd0..3794094fcf 100644 --- a/deps/OpenCV/OpenCV.cmake +++ b/deps/OpenCV/OpenCV.cmake @@ -12,7 +12,6 @@ orcaslicer_add_cmake_project(OpenCV URL https://github.com/opencv/opencv/archive/refs/tags/4.6.0.tar.gz URL_HASH SHA256=1ec1cba65f9f20fe5a41fda1586e01c70ea0c9a6d7b67c9e13edf0cfe2239277 PATCH_COMMAND git apply ${OpenCV_DIRECTORY_FLAG} --verbose --ignore-space-change --whitespace=fix ${CMAKE_CURRENT_LIST_DIR}/0001-vs2022.patch ${CMAKE_CURRENT_LIST_DIR}/0002-clang19-macos.patch - DEPENDS ${JPEG_PKG} CMAKE_ARGS -DBUILD_SHARED_LIBS=0 -DBUILD_PERE_TESTS=OFF @@ -21,7 +20,7 @@ orcaslicer_add_cmake_project(OpenCV -DBUILD_EXAMPLES=OFF -DBUILD_JASPER=OFF -DBUILD_JAVA=OFF - -DBUILD_JPEG=OFF + -DBUILD_JPEG=ON -DBUILD_APPS_LIST=version -DBUILD_opencv_apps=OFF -DBUILD_opencv_java=OFF From 9061b12aee8030ef56e42d48d3b29fbe2d842e0c Mon Sep 17 00:00:00 2001 From: bytedream Date: Tue, 9 Sep 2025 13:41:50 +0200 Subject: [PATCH 26/27] Fix timelapse crash (cherry picked from commit 1600f476bb8461fbc8b23c538880731977bd3211) --- deps/OpenCV/OpenCV.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/deps/OpenCV/OpenCV.cmake b/deps/OpenCV/OpenCV.cmake index 3794094fcf..d9be1b7f50 100644 --- a/deps/OpenCV/OpenCV.cmake +++ b/deps/OpenCV/OpenCV.cmake @@ -53,6 +53,7 @@ orcaslicer_add_cmake_project(OpenCV -DWITH_OPENJPEG=OFF -DWITH_QUIRC=OFF -DWITH_VTK=OFF + -DWITH_JPEG=OFF -DWITH_WEBP=OFF -DENABLE_PRECOMPILED_HEADERS=OFF -DINSTALL_TESTS=OFF From cf26243740a569829860078ea896070e010fc6ee Mon Sep 17 00:00:00 2001 From: Noisyfox Date: Sun, 12 Oct 2025 12:53:13 +0800 Subject: [PATCH 27/27] Update color --- resources/images/refresh_printer.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/images/refresh_printer.svg b/resources/images/refresh_printer.svg index e2e123893f..c377397055 100644 --- a/resources/images/refresh_printer.svg +++ b/resources/images/refresh_printer.svg @@ -1,3 +1,3 @@ - +