From 7888452666c31bab80d1dd0675a642f2e41c5d31 Mon Sep 17 00:00:00 2001 From: Kris Austin Date: Thu, 10 Sep 2026 05:39:14 -0500 Subject: [PATCH 1/7] build: clear 7 warning categories across 26 sites (#15615) * build: clear 2 warnings - cast the NSTextField the class check already proved mainframe_text_field is NSTextField* and was assigned a bare NSView*, which Clang reports as -Wincompatible-pointer-types. Both assignments sit inside if ([viewObject class] == [NSTextField self]), so the runtime type is already guaranteed, and the line above the second one casts the same variable the same way to call setTextColor. macOS only, since nothing else compiles this file. * build: clear 6 warning categories from the clang-cl inventory -Wmissing-braces (9). Aggregates whose first member is itself an aggregate. GUID's fourth member is BYTE[8], so the trailing eight bytes take their own braces. The others were reaching for zero-initialization with {0} and say {} now. bbs_3mf's backup Task ends in an anonymous union, which needs braces of its own; those braces initialize the union's first member rather than the one named at the call site, so the RemoveBackup site says so in a comment. -Wmacro-redefined (11). SendMultiMachinePage.hpp defines five names that Preferences.hpp, PresetBundleDialog.hpp, ExportPresetBundleDialog.hpp and TroubleshootDialog.hpp also define with different values, so the value in force depended on include order. All nine of this file's DESIGN_ macros take the SEND_ prefix it already uses for its own macros, values unchanged, so a DESIGN_ name added elsewhere later cannot collide with it again. They read as one page-local palette, a 900 to 400 gray ramp plus sizes, so the four with no current readers stay: dropping them would leave gaps in a named scale. test_marchingsquares.cpp defines NOMINMAX, which libslic3r already passes as a PUBLIC compile definition, so it takes the #ifndef guard the other suites use. -Wbraced-scalar-init (3). Two PushStyleVar calls resolve to the float overload, so the braces were initializing a scalar. ConfigOptionFloatsNullable already takes an initializer_list, so the inner braces did the same thing. -Wmicrosoft-goto (2). Both gotos in copy_file_gui jump forward over the initialization of size, dwRead and dwWrite, which only MSVC accepts. Those declarations move up to join the others at the top of the function. -Wunused-private-field (3). Every use of ColourPicker's m_clrData and m_picker_widget is behind !defined(__linux__), so on Linux they are written and never read; the members now carry the same guard. ParamsPanel's m_size_move is read nowhere. Tab has its own, which is the one Tab.cpp uses. -Wnonportable-include-path (2). BaseException.h asked for "stackwalker.h" and the file on disk is StackWalker.h. --- src/dev-utils/BaseException.h | 2 +- src/dev-utils/StackWalker.cpp | 2 +- src/libslic3r/Format/bbs_3mf.cpp | 8 ++++---- src/libslic3r/PrintConfig.cpp | 2 +- src/slic3r/GUI/Field.hpp | 2 ++ src/slic3r/GUI/GUI_App.cpp | 2 +- src/slic3r/GUI/GUI_Utils.cpp | 5 +++-- .../GUI/Gizmos/GizmoObjectManipulation.cpp | 2 +- src/slic3r/GUI/IMSlider.cpp | 2 +- src/slic3r/GUI/MainFrame.cpp | 4 ++-- src/slic3r/GUI/ParamsPanel.hpp | 1 - src/slic3r/GUI/PartPlate.cpp | 2 +- src/slic3r/GUI/SendMultiMachinePage.cpp | 14 +++++++------- src/slic3r/GUI/SendMultiMachinePage.hpp | 18 +++++++++--------- src/slic3r/Utils/MacDarkMode.mm | 4 ++-- tests/libslic3r/test_marchingsquares.cpp | 2 ++ 16 files changed, 38 insertions(+), 34 deletions(-) diff --git a/src/dev-utils/BaseException.h b/src/dev-utils/BaseException.h index 2cb65d945e..20b6fb0c89 100644 --- a/src/dev-utils/BaseException.h +++ b/src/dev-utils/BaseException.h @@ -1,7 +1,7 @@ #pragma once #include #include -#include "stackwalker.h" +#include "StackWalker.h" #include class CBaseException : public CStackWalker diff --git a/src/dev-utils/StackWalker.cpp b/src/dev-utils/StackWalker.cpp index 6038196cb0..3ef983cd86 100644 --- a/src/dev-utils/StackWalker.cpp +++ b/src/dev-utils/StackWalker.cpp @@ -425,7 +425,7 @@ LPSTACKINFO CStackWalker::StackWalker(HANDLE hThread, const CONTEXT* context) else c = *context; - STACKFRAME64 sf = {0}; + STACKFRAME64 sf = {}; DWORD imageType; //intel X86 diff --git a/src/libslic3r/Format/bbs_3mf.cpp b/src/libslic3r/Format/bbs_3mf.cpp index 96d98dbe9f..e2091da1db 100644 --- a/src/libslic3r/Format/bbs_3mf.cpp +++ b/src/libslic3r/Format/bbs_3mf.cpp @@ -8844,7 +8844,7 @@ public: auto model = object.get_model(); auto o = m_temp_model.add_object(object); int backup_id = model->get_object_backup_id(object); - push_task({ AddObject, (size_t) backup_id, object.get_model()->get_backup_path(), o, 1 }); + push_task({ AddObject, (size_t) backup_id, object.get_model()->get_backup_path(), o, { 1 } }); } void remove_object_mesh(ModelObject& object) { @@ -8854,7 +8854,7 @@ public: void backup_soon() { boost::lock_guard lock(m_mutex); m_other_changes_backup = true; - m_tasks.push_back({ Backup, 0, std::string(), nullptr, ++m_task_seq }); + m_tasks.push_back({ Backup, 0, std::string(), nullptr, { ++m_task_seq } }); m_cond.notify_all(); } @@ -8872,7 +8872,7 @@ public: m_ui_tasks.clear(); m_tasks.clear(); } - m_tasks.push_back({ RemoveBackup, model.id().id, model.get_backup_path(), nullptr, removeAll }); + m_tasks.push_back({ RemoveBackup, model.id().id, model.get_backup_path(), nullptr, { removeAll } }); ++m_task_seq; if (model.is_need_backup()) { m_other_changes = false; @@ -9087,7 +9087,7 @@ public: else m_cond.wait(lock); if (m_interval > 0 && boost::get_system_time() > m_next_backup) { - m_tasks.push_back({ Backup, 0, std::string(), nullptr, ++m_task_seq }); + m_tasks.push_back({ Backup, 0, std::string(), nullptr, { ++m_task_seq } }); m_next_backup += boost::posix_time::seconds(m_interval); // Maybe wakeup from power sleep if (m_next_backup < boost::get_system_time()) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 1db476eede..4c27995ba0 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -5430,7 +5430,7 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->readonly = false; def->nullable = true; - def->set_default_value(new ConfigOptionFloatsNullable { {0.0} }); + def->set_default_value(new ConfigOptionFloatsNullable { 0.0 }); def = this->add("cooling_tube_retraction", coFloat); def->label = L("Cooling tube position"); diff --git a/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp index 5d5d549427..74011983c6 100644 --- a/src/slic3r/GUI/Field.hpp +++ b/src/slic3r/GUI/Field.hpp @@ -628,8 +628,10 @@ private: void on_button_click(wxCommandEvent &WXUNUSED(ev)); void save_colors_to_config(); private: +#if !defined(__linux__) && !defined(__LINUX__) wxColourData* m_clrData{nullptr}; wxColourPickerWidget* m_picker_widget{nullptr}; +#endif }; class PointCtrl : public Field { diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 99a829bdcc..6d08f052da 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -598,7 +598,7 @@ wxString file_wildcards(FileType file_type, const std::string &custom_extension) static std::string libslic3r_translate_callback(const char *s) { return wxGetTranslation(wxString(s, wxConvUTF8)).utf8_str().data(); } #ifdef WIN32 -static GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2, 0xF16F, 0x11CF, 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 }; +static GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } }; static void register_win32_device_notification_event() { diff --git a/src/slic3r/GUI/GUI_Utils.cpp b/src/slic3r/GUI/GUI_Utils.cpp index bc66d90ffd..10dd29c9c1 100644 --- a/src/slic3r/GUI/GUI_Utils.cpp +++ b/src/slic3r/GUI/GUI_Utils.cpp @@ -69,6 +69,8 @@ CopyFileResult copy_file_gui(const std::string &from, const std::string &to, std HANDLE handlesrc = nullptr; HANDLE handledst = nullptr; CopyFileResult ret = SUCCESS; + DWORD size = 0; + DWORD dwRead = 0, dwWrite = 0; handlesrc = CreateFile(src.wc_str(), GENERIC_READ, @@ -96,9 +98,8 @@ CopyFileResult copy_file_gui(const std::string &from, const std::string &to, std goto __finished; } - DWORD size=GetFileSize(handlesrc,NULL); + size = GetFileSize(handlesrc,NULL); buff = new char[size+1]; - DWORD dwRead=0,dwWrite; result = ReadFile(handlesrc, buff, size, &dwRead, NULL); if (!result) { DWORD errCode = GetLastError(); diff --git a/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp b/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp index 7c4a2afd38..5d70fde833 100644 --- a/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp +++ b/src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp @@ -702,7 +702,7 @@ bool GizmoObjectManipulation::reset_zero_button(ImGuiWrapper *imgui_wrapper, bo for (int i = 0; i < number; i++) { - char buf[3][64] = {0}; + char buf[3][64] = {}; float buf_size[3] = {0}; for (int j = 0; j < 3; j++) { ImGui::DataTypeFormatString(buf[j], IM_ARRAYSIZE(buf[j]), ImGuiDataType_Double, (void *) &vec[i][j], "%.2f"); diff --git a/src/slic3r/GUI/IMSlider.cpp b/src/slic3r/GUI/IMSlider.cpp index c008963646..0d0d6739f8 100644 --- a/src/slic3r/GUI/IMSlider.cpp +++ b/src/slic3r/GUI/IMSlider.cpp @@ -790,7 +790,7 @@ void IMSlider::draw_ticks(const ImRect& slideable_region) { void IMSlider::show_tooltip(const std::string tooltip) { ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 6 * m_scale, 3 * m_scale }); - ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, { 3 * m_scale }); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 3 * m_scale); ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BACKGROUND); ImGui::PushStyleColor(ImGuiCol_Border, { 0,0,0,0 }); ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.00f, 1.00f, 1.00f, 1.00f)); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 167b2b4cda..c734804c22 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1591,7 +1591,7 @@ void MainFrame::register_win32_callbacks() //static GUID GUID_DEVINTERFACE_USB_DEVICE = { 0xA5DCBF10, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED }; //static GUID GUID_DEVINTERFACE_DISK = { 0x53f56307, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b }; //static GUID GUID_DEVINTERFACE_VOLUME = { 0x71a27cdd, 0x812a, 0x11d0, 0xbe, 0xc7, 0x08, 0x00, 0x2b, 0xe2, 0x09, 0x2f }; - static GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2, 0xF16F, 0x11CF, 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 }; + static GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } }; // Register USB HID (Human Interface Devices) notifications to trigger the 3DConnexion enumeration. DEV_BROADCAST_DEVICEINTERFACE NotificationFilter = { 0 }; @@ -1631,7 +1631,7 @@ void MainFrame::register_win32_callbacks() { static constexpr int device_count = 1; - RAWINPUTDEVICE devices[device_count] = { 0 }; + RAWINPUTDEVICE devices[device_count] = {}; // multi-axis mouse (SpaceNavigator, etc.) devices[0].usUsagePage = 0x01; devices[0].usUsage = 0x08; diff --git a/src/slic3r/GUI/ParamsPanel.hpp b/src/slic3r/GUI/ParamsPanel.hpp index 0726db91d3..91bf3d2a7e 100644 --- a/src/slic3r/GUI/ParamsPanel.hpp +++ b/src/slic3r/GUI/ParamsPanel.hpp @@ -66,7 +66,6 @@ class ParamsPanel : public wxPanel { #if __WXOSX__ wxWindow* m_tmp_panel; - int m_size_move = -1; #endif // __WXOSX__ private: diff --git a/src/slic3r/GUI/PartPlate.cpp b/src/slic3r/GUI/PartPlate.cpp index 90e2c96ab6..9826498fbd 100644 --- a/src/slic3r/GUI/PartPlate.cpp +++ b/src/slic3r/GUI/PartPlate.cpp @@ -1112,7 +1112,7 @@ void PartPlate::show_tooltip(const std::string tooltip) { const auto scale = m_plater->get_current_canvas3D()->get_scale(); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, {6 * scale, 3 * scale}); - ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, {3 * scale}); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 3 * scale); ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BACKGROUND); ImGui::PushStyleColor(ImGuiCol_Border, {0, 0, 0, 0}); ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.00f, 1.00f, 1.00f, 1.00f)); diff --git a/src/slic3r/GUI/SendMultiMachinePage.cpp b/src/slic3r/GUI/SendMultiMachinePage.cpp index 3a52caec3f..2d1b713264 100644 --- a/src/slic3r/GUI/SendMultiMachinePage.cpp +++ b/src/slic3r/GUI/SendMultiMachinePage.cpp @@ -814,13 +814,13 @@ wxBoxSizer* SendMultiMachinePage::create_item_title(wxString title, wxWindow* pa wxBoxSizer* m_sizer_title = new wxBoxSizer(wxHORIZONTAL); auto m_title = new wxStaticText(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, 0); - m_title->SetForegroundColour(DESIGN_GRAY800_COLOR); + m_title->SetForegroundColour(SEND_DESIGN_GRAY800_COLOR); m_title->SetFont(::Label::Head_13); m_title->Wrap(-1); m_title->SetToolTip(tooltip); auto m_line = new wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL); - m_line->SetBackgroundColour(DESIGN_GRAY400_COLOR); + m_line->SetBackgroundColour(SEND_DESIGN_GRAY400_COLOR); m_sizer_title->Add(m_title, 0, wxALIGN_CENTER | wxALL, 3); m_sizer_title->Add(0, 0, 0, wxLEFT, 9); @@ -843,7 +843,7 @@ wxBoxSizer* SendMultiMachinePage::create_item_checkbox(wxString title, wxWindow* m_sizer_checkbox->Add(0, 0, 0, wxEXPAND | wxLEFT, 8); auto checkbox_title = new wxStaticText(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, 0); - checkbox_title->SetForegroundColour(DESIGN_GRAY900_COLOR); + checkbox_title->SetForegroundColour(SEND_DESIGN_GRAY900_COLOR); checkbox_title->SetFont(::Label::Body_13); auto size = checkbox_title->GetTextExtent(title); @@ -867,12 +867,12 @@ wxBoxSizer* SendMultiMachinePage::create_item_input(wxString str_before, wxStrin { wxBoxSizer* sizer_input = new wxBoxSizer(wxHORIZONTAL); auto input_title = new wxStaticText(parent, wxID_ANY, str_before); - input_title->SetForegroundColour(DESIGN_GRAY900_COLOR); + input_title->SetForegroundColour(SEND_DESIGN_GRAY900_COLOR); input_title->SetFont(::Label::Body_13); input_title->SetToolTip(tooltip); input_title->Wrap(-1); - auto input = new ::TextInput(parent, wxEmptyString, wxEmptyString, wxEmptyString, wxDefaultPosition, DESIGN_INPUT_SIZE, wxTE_PROCESS_ENTER); + auto input = new ::TextInput(parent, wxEmptyString, wxEmptyString, wxEmptyString, wxDefaultPosition, SEND_DESIGN_INPUT_SIZE, wxTE_PROCESS_ENTER); StateColor input_bg(std::pair(wxColour("#F0F0F1"), StateColor::Disabled), std::pair(*wxWHITE, StateColor::Enabled)); input->SetBackgroundColor(input_bg); input->GetTextCtrl()->SetValue(app_config->get(param)); @@ -880,7 +880,7 @@ wxBoxSizer* SendMultiMachinePage::create_item_input(wxString str_before, wxStrin input->GetTextCtrl()->SetValidator(validator); auto second_title = new wxStaticText(parent, wxID_ANY, str_after, wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END); - second_title->SetForegroundColour(DESIGN_GRAY900_COLOR); + second_title->SetForegroundColour(SEND_DESIGN_GRAY900_COLOR); second_title->SetFont(::Label::Body_13); second_title->SetToolTip(tooltip); second_title->Wrap(-1); @@ -1337,7 +1337,7 @@ wxPanel* SendMultiMachinePage::create_page() m_tip_text->SetMinSize(wxSize(FromDIP(DEVICE_ITEM_MAX_WIDTH), -1)); m_tip_text->SetMaxSize(wxSize(FromDIP(DEVICE_ITEM_MAX_WIDTH), -1)); m_tip_text->SetLabel(_L("Please select the devices you would like to manage here (up to 6 devices)")); - m_tip_text->SetForegroundColour(DESIGN_GRAY800_COLOR); + m_tip_text->SetForegroundColour(SEND_DESIGN_GRAY800_COLOR); m_tip_text->SetFont(::Label::Head_20); m_tip_text->Wrap(-1); diff --git a/src/slic3r/GUI/SendMultiMachinePage.hpp b/src/slic3r/GUI/SendMultiMachinePage.hpp index 7d77849bf3..a63bc51bb0 100644 --- a/src/slic3r/GUI/SendMultiMachinePage.hpp +++ b/src/slic3r/GUI/SendMultiMachinePage.hpp @@ -22,15 +22,15 @@ namespace GUI { #define SEND_LEFT_DEV_STATUS 250 #define SEND_LEFT_TAKS_STATUS 180 -#define DESIGN_SELECTOR_NOMORE_COLOR wxColour(248, 248, 248) -#define DESIGN_GRAY900_COLOR wxColour(38, 46, 48) -#define DESIGN_GRAY800_COLOR wxColour(50, 58, 61) -#define DESIGN_GRAY600_COLOR wxColour(144, 144, 144) -#define DESIGN_GRAY400_COLOR wxColour(166, 169, 170) -#define DESIGN_RESOUTION_PREFERENCES wxSize(FromDIP(540), -1) -#define DESIGN_COMBOBOX_SIZE wxSize(FromDIP(140), -1) -#define DESIGN_LARGE_COMBOBOX_SIZE wxSize(FromDIP(160), -1) -#define DESIGN_INPUT_SIZE wxSize(FromDIP(50), -1) +#define SEND_DESIGN_SELECTOR_NOMORE_COLOR wxColour(248, 248, 248) +#define SEND_DESIGN_GRAY900_COLOR wxColour(38, 46, 48) +#define SEND_DESIGN_GRAY800_COLOR wxColour(50, 58, 61) +#define SEND_DESIGN_GRAY600_COLOR wxColour(144, 144, 144) +#define SEND_DESIGN_GRAY400_COLOR wxColour(166, 169, 170) +#define SEND_DESIGN_RESOUTION_PREFERENCES wxSize(FromDIP(540), -1) +#define SEND_DESIGN_COMBOBOX_SIZE wxSize(FromDIP(140), -1) +#define SEND_DESIGN_LARGE_COMBOBOX_SIZE wxSize(FromDIP(160), -1) +#define SEND_DESIGN_INPUT_SIZE wxSize(FromDIP(50), -1) diff --git a/src/slic3r/Utils/MacDarkMode.mm b/src/slic3r/Utils/MacDarkMode.mm index cecd90044b..2bce7835e8 100644 --- a/src/slic3r/Utils/MacDarkMode.mm +++ b/src/slic3r/Utils/MacDarkMode.mm @@ -57,7 +57,7 @@ void set_miniaturizable(void * window) while(viewObject = (NSView *)[viewEnum nextObject]) { if([viewObject class] == [NSTextField self]) { //[(NSTextField*)viewObject setTextColor : NSColor.whiteColor]; - mainframe_text_field = viewObject; + mainframe_text_field = (NSTextField*)viewObject; } } } @@ -74,7 +74,7 @@ void set_title_colour_after_set_title(void * window) while(viewObject = (NSView *)[viewEnum nextObject]) { if([viewObject class] == [NSTextField self]) { [(NSTextField*)viewObject setTextColor : NSColor.whiteColor]; - mainframe_text_field = viewObject; + mainframe_text_field = (NSTextField*)viewObject; } } diff --git a/tests/libslic3r/test_marchingsquares.cpp b/tests/libslic3r/test_marchingsquares.cpp index 6844ecb6ac..9a11f49faa 100644 --- a/tests/libslic3r/test_marchingsquares.cpp +++ b/tests/libslic3r/test_marchingsquares.cpp @@ -1,4 +1,6 @@ +#ifndef NOMINMAX #define NOMINMAX +#endif #include #include "test_utils.hpp" From e8d35fadd45c537d578cedc4eacf548ad7a48920 Mon Sep 17 00:00:00 2001 From: Valerii Bokhan <80919135+valerii-bokhan@users.noreply.github.com> Date: Thu, 10 Sep 2026 13:03:50 +0200 Subject: [PATCH 2/7] Fix internal bridges over Hilbert Curve/Octagram Spiral sparse infill (#15206) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix internal bridges over Hilbert Curve/Octagram Spiral sparse infill For patterns with curved/turning anchor lines (Hilbert Curve, Octagram Spiral), the bridge_over_infill algorithm produced incorrect results: 1. determine_bridging_angle: sampling curved anchor orientations produced noise across all turning directions (0/90/180/270°) instead of a single dominant one, yielding unstable bridge angles with 180° spread. Fix: use the configured infill_direction + 90° directly, bypassing the noisy sampling. The old blind +0.25*PI (Hilbert) and +1/16*PI (Octagram) offsets are removed. 2. construct_anchored_polygon: curved Hilbert/Octagram anchors intersected each vertical scan line many times at wildly different Y positions, producing chaotic polygon sections — holes in random places, bridges over air, rotated bridges. Fix: replace the curved infill polylines with synthetic straight lines parallel to infill_direction, spaced at the real infill line spacing (flow_spacing / density). Lines are centered on the limiting_area bbox center so that after rotation they span the full bridged_area. Anchors are left at full bbox length (not clipped) to guarantee every scan line finds an anchor. Rectilinear and other straight-line patterns are unaffected. Known limitation: some bridge edges may still terminate over air in edge cases where the nearest synthetic anchor line is more than one infill spacing away from the bridge boundary. This will be addressed in a follow-up. * fix: anchor internal bridges to actual sparse infill Preserve real anchors across regions and align plane-path anchor origins with printed infill. Respect lower-layer rotation templates and model alignment, and sample curved bridge boundaries more finely. Add regression coverage for anchor alignment, bridge angles and region isolation, with Orca comments explaining the geometry constraints. Verified 175 FFF tests before the comment-only follow-up; preserve CRLF in modified files. * Fix internal bridge support contacts and separated infill origins Restore anchor contact after bridge smoothing and share per-body pattern origins between anchors and printed infill. Recompute origins when preparation settings change. Cover multiline counts 1, 2 and 3 and add regressions for printed bridge support, separated infill alignment and reslicing. * Add explicit standard headers to PrintObject tests * test: cover surface centering when infill settings change Verify top and bottom Archimedean Chords and Octagram Spiral paths after switching centering modes or toggling separated infills. Compare reslicing against fresh slicing and document dependent infill invalidation. * test: preserve directional surface infill when settings change * perf: index layer islands for connected-body detection * test: use public print pipeline for body centering checks --- src/libslic3r/Fill/Fill.cpp | 75 +++-- src/libslic3r/Fill/Fill.hpp | 6 + src/libslic3r/PrintObject.cpp | 304 +++++++++++------- tests/fff_print/test_fill.cpp | 66 ++++ tests/fff_print/test_printobject.cpp | 442 +++++++++++++++++++++++++++ 5 files changed, 736 insertions(+), 157 deletions(-) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index dc772580ca..f5386b085c 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -11,7 +11,7 @@ #include "AABBTreeLines.hpp" #include "ExtrusionEntity.hpp" -#include "FillBase.hpp" +#include "Fill.hpp" #include "FillRectilinear.hpp" #include "FillLightning.hpp" #include "FillConcentricInternal.hpp" @@ -1234,6 +1234,33 @@ std::vector group_fills(const Layer &layer, LockRegionParam &lock_p return surface_fills; } +// Orca: Anchors and printed infill must share the same body origin. Keep the choice +// here so per-model surface centering and separated sparse infill cannot drift apart. +static BoundingBox infill_bounding_box(const Layer &layer, const SurfaceFill &fill, const ExPolygon &expoly, BoundingBox bbox) +{ + const auto ¶ms = fill.params; + const auto &config = layer.regions()[fill.region_id]->region().config(); + const bool external = params.extrusion_role == erTopSolidInfill || params.extrusion_role == erBottomSurface; + const bool per_model = external && params.center_of_surface_pattern == CenterOfSurfacePattern::Each_Model && + (params.pattern == ipArchimedeanChords || params.pattern == ipOctagramSpiral); + const bool separate = !external && params.separated_infills && + (is_separable_infill_pattern(params.pattern) || !config.solid_infill_rotate_template.value.empty() || + !config.sparse_infill_rotate_template.value.empty()); + if (per_model || separate) { + double best_overlap = 0.; + for (size_t i = 0; i < layer.lslices.size() && i < layer.lslices_separated_component_bboxes.size(); ++i) { + const double overlap = area(intersection_ex(layer.lslices[i], expoly)); + if (overlap > best_overlap) { + best_overlap = overlap; + const Point center = layer.lslices_separated_component_bboxes[i].center(); + bbox = layer.object()->bounding_box(); + bbox.translate(center.x(), center.y()); + } + } + } + return bbox; +} + #ifdef SLIC3R_DEBUG_SLICE_PROCESSING void export_group_fills_to_svg(const char *path, const std::vector &fills) { @@ -1353,19 +1380,9 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: // Orca: Checking the filling of a centered surface by drawing for each model parts bool is_top_or_bottom = params.extrusion_role == erTopSolidInfill || params.extrusion_role == erBottomSurface; - bool is_centered_infill = surface_fill.params.pattern == ipArchimedeanChords || surface_fill.params.pattern == ipOctagramSpiral; if (is_top_or_bottom) { params.center_of_surface_pattern = surface_fill.params.center_of_surface_pattern; // Orca: center of surface pattern } - // Orca: Each_Model centers the pattern on each model part's bbox; Each_Surface / Each_Assembly - // fall through to the default (whole-object) bounding box below. - bool is_per_model_center = is_top_or_bottom && params.center_of_surface_pattern == CenterOfSurfacePattern::Each_Model && is_centered_infill; - bool is_separate_infill = !is_top_or_bottom && surface_fill.params.separated_infills && - ( - is_separable_infill_pattern(surface_fill.params.pattern) || - params.config->solid_infill_rotate_template != "" || - params.config->sparse_infill_rotate_template != "" ); - if( surface_fill.params.pattern == ipLockedZag ) { params.locked_zag = true; params.infill_lock_depth = surface_fill.params.infill_lock_depth; @@ -1389,34 +1406,8 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: params.can_reverse = false; for (ExPolygon& expoly : surface_fill.expolygons) { - // Orca: separate infill / per-model pattern centering. - // - // Center the pattern on each connected body of the object independently, so every piece - // is filled exactly as if it were sliced on its own: touching/overlapping parts merge - // into one body sharing a center, while separate parts and disconnected islands (even - // interleaved-but-not-touching ones, e.g. chain links) each get their own. The body each - // island belongs to, and its full bounding box, were resolved in 3D by PrintObject:: - // infill() (lslices_separated_component_bboxes, aligned with this layer's lslices). We - // match this fill region to the island it overlaps most, then re-use the whole-object - // bounding box (origin-centered — identical extent to the default, so coverage and cost - // are unchanged) re-centered on that body. - if (is_per_model_center || is_separate_infill) { - double best_overlap = 0.; - BoundingBox best_component; - for (size_t r = 0; r < this->lslices.size() && r < this->lslices_separated_component_bboxes.size(); ++ r) { - const double overlap = area(intersection_ex(this->lslices[r], expoly)); - if (overlap > best_overlap) { - best_overlap = overlap; - best_component = this->lslices_separated_component_bboxes[r]; - } - } - if (best_component.defined) { - const Point c = best_component.center(); - BoundingBox part_bbox = bbox; // origin-centered, whole-object extent (from above) - part_bbox.translate(c.x(), c.y()); // re-center on this body - f->set_bounding_box(part_bbox); - } - } // - End: separate infill / per-model pattern centering + // Orca: Reuse the body origin used for bridge anchoring, resetting it for each surface. + f->set_bounding_box(infill_bounding_box(*this, surface_fill, expoly, bbox)); f->no_overlap_expolygons = intersection_ex(surface_fill.no_overlap_expolygons, ExPolygons() = {expoly}, ApplySafetyOffset::Yes); if (params.symmetric_infill_y_axis) { @@ -1583,8 +1574,14 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc params.multiline = surface_fill.params.multiline; params.gyroid_optimized = surface_fill.params.gyroid_optimized; params.smooth_factor = surface_fill.params.smooth_factor; + // Orca: Match make_fills() when choosing the origin of plane-path patterns. + // Without the sparse extrusion role, the filler uses each surface's bounds + // instead of the object's bounds, so bridge anchors shift away from printed infill. + params.extrusion_role = surface_fill.params.extrusion_role; for (ExPolygon &expoly : surface_fill.expolygons) { + // Orca: Match the per-body origin of make_fills() before generating physical anchors. + f->set_bounding_box(infill_bounding_box(*this, surface_fill, expoly, bbox)); // Spacing is modified by the filler to indicate adjustments. Reset it for each expolygon. f->spacing = surface_fill.params.spacing; surface_fill.surface.expolygon = std::move(expoly); diff --git a/src/libslic3r/Fill/Fill.hpp b/src/libslic3r/Fill/Fill.hpp index e92ab2dee5..b183cf0253 100644 --- a/src/libslic3r/Fill/Fill.hpp +++ b/src/libslic3r/Fill/Fill.hpp @@ -14,6 +14,12 @@ namespace Slic3r { class ExtrusionEntityCollection; class LayerRegion; +class PrintObject; + +// Orca: Share the layer rotation calculation between infill generation and internal +// bridge angle selection so both interpret rotation templates in the same way. +double calculate_infill_rotation_angle(const PrintObject *object, size_t layer_id, + const double &fixed_infill_angle, const std::string &template_string); // An interface class to Perl, aggregating an instance of a Fill and a FillData. class Filler diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index a228bb7436..e147356ea6 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -21,9 +21,11 @@ #include "TriangleMeshSlicer.hpp" #include "Utils.hpp" #include "Fill/FillAdaptive.hpp" +#include "Fill/Fill.hpp" #include "Fill/FillLightning.hpp" #include "Format/STL.hpp" #include "format.hpp" +#include "AABBTreeIndirect.hpp" #include "AABBTreeLines.hpp" #include @@ -672,6 +674,98 @@ void PrintObject::prepare_infill() } // for each region #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ + // Orca: precompute the object's 3D connected bodies for separated infills / per-model + // centering. Two islands belong to the same body when their slices overlap on adjacent + // layers; islands that only overlap in top-down projection but never touch (e.g. interleaved + // chain links) stay separate, matching "split to objects". Each layer island then records + // the full bounding box of its body, so its infill is centered on that body as if it were + // sliced alone. Compute this before bridges so anchors and extrusion share the same origin. + bool needs_separated_components = false; + for (size_t i = 0; i < this->num_printing_regions(); ++ i) { + const PrintRegionConfig &rc = this->printing_region(i).config(); + if (rc.separated_infills || rc.center_of_surface_pattern == CenterOfSurfacePattern::Each_Model) { + needs_separated_components = true; + break; + } + } + // Orca: Fast path: the feature only changes anything when the object is made of more than one + // connected body. Detect that cheaply the same way as "Split to objects" — more than one + // model part, or a single part whose mesh is splittable (is_splittable() is cached). A single + // body already shares the object center, i.e. the default, so skip the connectivity pass. + if (needs_separated_components) { + int parts = 0; + const ModelVolume *first_part = nullptr; + for (const ModelVolume *v : this->model_object()->volumes) + if (v->is_model_part()) { ++ parts; first_part = v; } + if (parts <= 1 && ! (first_part != nullptr && first_part->is_splittable())) + needs_separated_components = false; + } + for (Layer *layer : m_layers) + layer->lslices_separated_component_bboxes.clear(); + if (needs_separated_components) { + const size_t nl = m_layers.size(); + std::vector offset(nl + 1, 0); // Orca: flat index of the first island of each layer + for (size_t i = 0; i < nl; ++ i) + offset[i + 1] = offset[i] + m_layers[i]->lslices.size(); + const size_t nreg = offset[nl]; + // Orca: Union-find over every (layer, island). + std::vector parent(nreg); + for (size_t i = 0; i < nreg; ++ i) parent[i] = i; + auto find = [&parent](size_t x) { + while (parent[x] != x) { parent[x] = parent[parent[x]]; x = parent[x]; } + return x; + }; + auto unite = [&](size_t a, size_t b) { a = find(a); b = find(b); if (a != b) parent[a] = b; }; + // Orca: Index the smaller of two consecutive layers instead of scanning every + // pair of islands. The tree prunes distant boxes on fragmented models; exact + // polygon intersections still decide connectivity for the remaining candidates. + for (size_t i = 0; i + 1 < nl; ++ i) { + m_print->throw_if_canceled(); + size_t layer_a = i, layer_b = i + 1; + if (m_layers[layer_a]->lslices.size() < m_layers[layer_b]->lslices.size()) + std::swap(layer_a, layer_b); + const Layer *la = m_layers[layer_a], *lb = m_layers[layer_b]; + if (lb->lslices.empty()) + continue; + + using IslandTree = AABBTreeIndirect::Tree<2, coord_t>; + std::vector bboxes; + bboxes.reserve(lb->lslices.size()); + for (size_t b = 0; b < lb->lslices.size(); ++ b) + bboxes.emplace_back(b, lb->lslices_bboxes[b]); + IslandTree tree; + tree.build_modify_input(bboxes); + for (size_t a = 0; a < la->lslices.size(); ++ a) { + const IslandTree::BoundingBox query(la->lslices_bboxes[a].min, la->lslices_bboxes[a].max); + AABBTreeIndirect::traverse(tree, + [&query](const IslandTree::Node &node) { return node.bbox.intersects(query); }, + [&](const IslandTree::Node &node) { + const size_t b = node.idx; + // Orca: Tree boxes include an epsilon, so retain the original box + // filter. Already-connected islands cannot change the partition + // and need no further polygon intersection. + if (la->lslices_bboxes[a].overlap(lb->lslices_bboxes[b]) && + find(offset[layer_a] + a) != find(offset[layer_b] + b) && + ! intersection_ex(la->lslices[a], lb->lslices[b]).empty()) + unite(offset[layer_a] + a, offset[layer_b] + b); + return true; + }); + } + } + // Orca: Full bounding box of each body, indexed by its union-find root. + std::vector body_bbox(nreg); + for (size_t i = 0; i < nl; ++ i) + for (size_t a = 0; a < m_layers[i]->lslices.size(); ++ a) + body_bbox[find(offset[i] + a)].merge(m_layers[i]->lslices_bboxes[a]); + // Orca: Store the body bbox for every island. + for (size_t i = 0; i < nl; ++ i) { + Layer *layer = m_layers[i]; + layer->lslices_separated_component_bboxes.resize(layer->lslices.size()); + for (size_t a = 0; a < layer->lslices.size(); ++ a) + layer->lslices_separated_component_bboxes[a] = body_bbox[find(offset[i] + a)]; + } + } + // the following step needs to be done before combination because it may need // to remove only half of the combined infill this->bridge_over_infill(); @@ -706,71 +800,6 @@ void PrintObject::infill() if (this->set_started(posInfill)) { m_print->set_status(35, L("Generating infill toolpath")); - // Orca: precompute the object's 3D connected bodies for separated infills / per-model - // centering. Two islands belong to the same body when their slices overlap on adjacent - // layers; islands that only overlap in top-down projection but never touch (e.g. interleaved - // chain links) stay separate, matching "split to objects". Each layer island then records - // the full bounding box of its body, so its infill is centered on that body as if it were - // sliced alone. Done once here, before the parallel fill, and only when a region needs it. - bool needs_separated_components = false; - for (size_t i = 0; i < this->num_printing_regions(); ++ i) { - const PrintRegionConfig &rc = this->printing_region(i).config(); - if (rc.separated_infills || rc.center_of_surface_pattern == CenterOfSurfacePattern::Each_Model) { - needs_separated_components = true; - break; - } - } - // Fast path: the feature only changes anything when the object is made of more than one - // connected body. Detect that cheaply the same way as "Split to objects" — more than one - // model part, or a single part whose mesh is splittable (is_splittable() is cached). A single - // body already shares the object center, i.e. the default, so skip the connectivity pass. - if (needs_separated_components) { - int parts = 0; - const ModelVolume *first_part = nullptr; - for (const ModelVolume *v : this->model_object()->volumes) - if (v->is_model_part()) { ++ parts; first_part = v; } - if (parts <= 1 && ! (first_part != nullptr && first_part->is_splittable())) - needs_separated_components = false; - } - for (Layer *layer : m_layers) - layer->lslices_separated_component_bboxes.clear(); - if (needs_separated_components) { - const size_t nl = m_layers.size(); - std::vector offset(nl + 1, 0); // flat index of the first island of each layer - for (size_t i = 0; i < nl; ++ i) - offset[i + 1] = offset[i] + m_layers[i]->lslices.size(); - const size_t nreg = offset[nl]; - // Union-find over every (layer, island). - std::vector parent(nreg); - for (size_t i = 0; i < nreg; ++ i) parent[i] = i; - auto find = [&parent](size_t x) { - while (parent[x] != x) { parent[x] = parent[parent[x]]; x = parent[x]; } - return x; - }; - auto unite = [&](size_t a, size_t b) { a = find(a); b = find(b); if (a != b) parent[a] = b; }; - // Join islands that overlap between two consecutive layers. - for (size_t i = 0; i + 1 < nl; ++ i) { - const Layer *la = m_layers[i], *lb = m_layers[i + 1]; - for (size_t a = 0; a < la->lslices.size(); ++ a) - for (size_t b = 0; b < lb->lslices.size(); ++ b) - if (la->lslices_bboxes[a].overlap(lb->lslices_bboxes[b]) && - ! intersection_ex(la->lslices[a], lb->lslices[b]).empty()) - unite(offset[i] + a, offset[i + 1] + b); - } - // Full bounding box of each body, indexed by its union-find root. - std::vector body_bbox(nreg); - for (size_t i = 0; i < nl; ++ i) - for (size_t a = 0; a < m_layers[i]->lslices.size(); ++ a) - body_bbox[find(offset[i] + a)].merge(m_layers[i]->lslices_bboxes[a]); - // Store the body bbox for every island. - for (size_t i = 0; i < nl; ++ i) { - Layer *layer = m_layers[i]; - layer->lslices_separated_component_bboxes.resize(layer->lslices.size()); - for (size_t a = 0; a < layer->lslices.size(); ++ a) - layer->lslices_separated_component_bboxes[a] = body_bbox[find(offset[i] + a)]; - } - } - const auto& adaptive_fill_octree = this->m_adaptive_fill_octrees.first; const auto& support_fill_octree = this->m_adaptive_fill_octrees.second; @@ -1401,8 +1430,6 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "infill_anchor_max" || opt_key == "top_surface_line_width" || opt_key == "bottom_surface_density" - || opt_key == "center_of_surface_pattern" - || opt_key == "separated_infills" || opt_key == "initial_layer_line_width" || opt_key == "small_area_infill_flow_compensation" || opt_key == "lateral_lattice_angle_1" @@ -1410,6 +1437,10 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "infill_overhang_angle") { steps.emplace_back(posInfill); } else if (opt_key == "sparse_infill_pattern" + // Orca: Body centering now also determines bridge anchors during preparation. + // Invalidating preparation also invalidates infill, including top/bottom surfaces. + || opt_key == "center_of_surface_pattern" + || opt_key == "separated_infills" || opt_key == "sparse_infill_smooth_factor" || opt_key == "symmetric_infill_y_axis" || opt_key == "infill_shift_step" @@ -3009,21 +3040,12 @@ void PrintObject::bridge_over_infill() return diff(layers_sparse_infill, not_sparse_infill); }; - // LAMBDA do determine optimal bridging angle - auto determine_bridging_angle = [](const Polygons &bridged_area, const Lines &anchors, InfillPattern dominant_pattern, double infill_direction) { + // Orca: Derive the fallback bridge direction from the supplied anchor geometry. + // Pattern-specific angle selection belongs at the call site, where the supporting + // layer and region are known; this helper must not override it with a base config angle. + auto determine_bridging_angle = [](const Polygons &bridged_area, const Lines &anchors) { AABBTreeLines::LinesDistancer lines_tree(anchors); - // Orca: since 3D Honeycomb was "fixed" by forcing coordf_t layerHeight = scale_(1.0), this is no longer needed. - // CorssHatch also does not need fixed angle. - // - // Check it the infill that require a fixed infill angle. - //switch (dominant_pattern) { - //case ip3DHoneycomb: - //case ipCrossHatch: - // return (infill_direction + 45.0) * 2.0 * M_PI / 360.; - //default: break; - //} - std::map counted_directions; for (const Polygon &p : bridged_area) { double acc_distance = 0; @@ -3089,18 +3111,15 @@ void PrintObject::bridge_over_infill() if (bridging_angle == 0) { bridging_angle = 0.001; } - switch (dominant_pattern) { - case ipHilbertCurve: bridging_angle += 0.25 * PI; break; - case ipOctagramSpiral: bridging_angle += (1.0 / 16.0) * PI; break; - default: break; - } return bridging_angle; }; - // LAMBDA that will fill given polygons with lines, exapand the lines to the nearest anchor, and reconstruct polygons from the newly - // generated lines - auto construct_anchored_polygon = [](Polygons bridged_area, Lines anchors, const Flow &bridging_flow, double bridging_angle) { + // Orca: Extend scan sections to the nearest anchors and reconstruct the bridge area. + // scan_spacing controls boundary sampling independently of the extrusion spacing; + // anchoring overlap and smoothing thresholds still use the physical bridging flow. + auto construct_anchored_polygon = [](Polygons bridged_area, Lines anchors, const Flow &bridging_flow, double bridging_angle, + coord_t scan_spacing, bool restore_anchors = false) { auto lines_rotate = [](Lines &lines, double cos_angle, double sin_angle) { for (Line &l : lines) { double ax = double(l.a.x()); @@ -3127,12 +3146,12 @@ void PrintObject::bridge_over_infill() BoundingBox bb_x = get_extents(bridged_area); BoundingBox bb_y = get_extents(anchors); - const size_t n_vlines = (bb_x.max.x() - bb_x.min.x() + bridging_flow.scaled_spacing() - 1) / bridging_flow.scaled_spacing(); + const size_t n_vlines = (bb_x.max.x() - bb_x.min.x() + scan_spacing - 1) / scan_spacing; std::vector vertical_lines(n_vlines); for (size_t i = 0; i < n_vlines; i++) { - // Orca: Make sure the line is placed in the middle of the extrusion - // coord_t x = bb_x.min.x() + i * bridging_flow.scaled_spacing(); - coord_t x = bb_x.min.x() + (i + 0.5) * bridging_flow.scaled_spacing(); + // Orca: Sample the center of each reconstructed strip. Its edges lie + // half a scan step away, even when the sampling is finer than extrusion. + coord_t x = bb_x.min.x() + (i + 0.5) * scan_spacing; coord_t y_min = bb_y.min.y() - bridging_flow.scaled_spacing(); coord_t y_max = bb_y.max.y() + bridging_flow.scaled_spacing(); vertical_lines[i].a = Point{x, y_min}; @@ -3155,7 +3174,11 @@ void PrintObject::bridge_over_infill() auto anchors_intersections = anchors_and_walls_tree.intersections_with_line(vertical_lines[i]); for (Line §ion : polygon_sections[i]) { - auto maybe_below_anchor = std::upper_bound(anchors_intersections.rbegin(), anchors_intersections.rend(), section.a, + // Orca: A repaired boundary may already overlap its anchor by one flow width. + // Include that overlap in the search so restoring rounded corners does not + // extend every already anchored section into the next sparse infill cell. + const coord_t overlap = restore_anchors ? bridging_flow.scaled_width() + SCALED_EPSILON : 0; + auto maybe_below_anchor = std::upper_bound(anchors_intersections.rbegin(), anchors_intersections.rend(), section.a + Point{0, overlap}, [](const Point &a, const std::pair &b) { return a.y() > b.first.y(); }); @@ -3164,7 +3187,7 @@ void PrintObject::bridge_over_infill() section.a.y() -= bridging_flow.scaled_width() * (0.5 + 0.5); } - auto maybe_upper_anchor = std::upper_bound(anchors_intersections.begin(), anchors_intersections.end(), section.b, + auto maybe_upper_anchor = std::upper_bound(anchors_intersections.begin(), anchors_intersections.end(), section.b - Point{0, overlap}, [](const Point &a, const std::pair &b) { return a.y() < b.first.y(); }); @@ -3194,7 +3217,9 @@ void PrintObject::bridge_over_infill() }); } - // reconstruct polygon from polygon sections + // Orca: Reconstruct the polygon from scan sections. At discontinuities and + // strip starts/ends, use half the scan step for the X offsets; using half an + // extrusion spacing would overlap the finer strips and distort curved anchors. struct TracedPoly { Points lows; @@ -3220,8 +3245,8 @@ void PrintObject::bridge_over_infill() 36.0 * double(bridging_flow.scaled_spacing()) * bridging_flow.scaled_spacing()) { traced_poly.lows.push_back(candidate->a); } else { - traced_poly.lows.push_back(traced_poly.lows.back() + Point{bridging_flow.scaled_spacing() / 2, 0}); - traced_poly.lows.push_back(candidate->a - Point{bridging_flow.scaled_spacing() / 2, 0}); + traced_poly.lows.push_back(traced_poly.lows.back() + Point{scan_spacing / 2, 0}); + traced_poly.lows.push_back(candidate->a - Point{scan_spacing / 2, 0}); traced_poly.lows.push_back(candidate->a); } @@ -3229,8 +3254,8 @@ void PrintObject::bridge_over_infill() 36.0 * double(bridging_flow.scaled_spacing()) * bridging_flow.scaled_spacing()) { traced_poly.highs.push_back(candidate->b); } else { - traced_poly.highs.push_back(traced_poly.highs.back() + Point{bridging_flow.scaled_spacing() / 2, 0}); - traced_poly.highs.push_back(candidate->b - Point{bridging_flow.scaled_spacing() / 2, 0}); + traced_poly.highs.push_back(traced_poly.highs.back() + Point{scan_spacing / 2, 0}); + traced_poly.highs.push_back(candidate->b - Point{scan_spacing / 2, 0}); traced_poly.highs.push_back(candidate->b); } segment_added = true; @@ -3238,9 +3263,9 @@ void PrintObject::bridge_over_infill() } if (!segment_added) { - // Zero overlapping segments, we just close this polygon - traced_poly.lows.push_back(traced_poly.lows.back() + Point{bridging_flow.scaled_spacing() / 2, 0}); - traced_poly.highs.push_back(traced_poly.highs.back() + Point{bridging_flow.scaled_spacing() / 2, 0}); + // Orca: No section continues this strip; close at its right edge. + traced_poly.lows.push_back(traced_poly.lows.back() + Point{scan_spacing / 2, 0}); + traced_poly.highs.push_back(traced_poly.highs.back() + Point{scan_spacing / 2, 0}); Polygon &new_poly = expanded_bridged_area.emplace_back(std::move(traced_poly.lows)); new_poly.points.insert(new_poly.points.end(), traced_poly.highs.rbegin(), traced_poly.highs.rend()); traced_poly.lows.clear(); @@ -3255,9 +3280,9 @@ void PrintObject::bridge_over_infill() for (const auto &segment : polygon_slice) { if (used_segments.find(&segment) == used_segments.end()) { TracedPoly &new_tp = current_traced_polys.emplace_back(); - new_tp.lows.push_back(segment.a - Point{bridging_flow.scaled_spacing() / 2, 0}); + new_tp.lows.push_back(segment.a - Point{scan_spacing / 2, 0}); new_tp.lows.push_back(segment.a); - new_tp.highs.push_back(segment.b - Point{bridging_flow.scaled_spacing() / 2, 0}); + new_tp.highs.push_back(segment.b - Point{scan_spacing / 2, 0}); new_tp.highs.push_back(segment.b); } } @@ -3364,7 +3389,10 @@ void PrintObject::bridge_over_infill() total_fill_area = closing(total_fill_area, float(SCALED_EPSILON)); expansion_area = closing(expansion_area, float(SCALED_EPSILON)); expansion_area = intersection(expansion_area, deep_infill_area); - Polylines anchors = intersection_pl(infill_lines[lidx - 1], shrink(expansion_area, spacing)); + // Orca: Preserve the real lower-layer anchors for every candidate in this + // layer. Replacing this shared set for one pattern also changes later regions, + // and synthetic straight lines can claim support where no infill is printed. + const Polylines anchors = intersection_pl(infill_lines[lidx - 1], shrink(expansion_area, spacing)); Polygons internal_unsupported_area = shrink(deep_infill_area, spacing * 4.5); #ifdef DEBUG_BRIDGE_OVER_INFILL @@ -3375,6 +3403,9 @@ void PrintObject::bridge_over_infill() std::vector expanded_surfaces; expanded_surfaces.reserve(surfaces_by_layer[lidx].size()); for (const CandidateSurface &candidate : surfaces_by_layer[lidx]) { + const auto ®ion_config = candidate.region->region().config(); + const bool turning_pattern = region_config.sparse_infill_pattern == ipHilbertCurve || + region_config.sparse_infill_pattern == ipOctagramSpiral; const Flow &flow = candidate.region->bridging_flow(frSolidInfill, true); Polygons area_to_be_bridge = expand(candidate.new_polys, flow.scaled_spacing()); area_to_be_bridge = intersection(area_to_be_bridge, deep_infill_area); @@ -3403,20 +3434,40 @@ void PrintObject::bridge_over_infill() to_lines(area_to_be_bridge), to_lines(boundary_plines), to_lines(anchors), to_lines(expansion_area)); #endif - double bridging_angle = 0; - if (!anchors.empty()) { - bridging_angle = determine_bridging_angle(area_to_be_bridge, to_lines(anchors), - candidate.region->region().config().sparse_infill_pattern.value, - candidate.region->region().config().infill_direction.value); - } else { - // use expansion boundaries as anchors. - // Also, use Infill pattern that is neutral for angle determination, since there are no infill lines. - bridging_angle = determine_bridging_angle(area_to_be_bridge, to_lines(boundary_plines), InfillPattern::ipLine, 0); + double bridging_angle = -1.; + if (!anchors.empty() && turning_pattern) { + // Orca: Keep adjacent bridges over Hilbert/Octagram aligned despite + // their many local turning directions. Use the lower layer's rotation, + // since that is the infill supporting the bridge, not the current layer's. + for (const LayerRegion *lower_region : layer->lower_layer->regions()) { + // Orca: Apply the configured direction only if the same region has + // sparse infill below this bridge. A height modifier may put another + // pattern underneath, requiring the geometry-based fallback below. + if (&lower_region->region() != &candidate.region->region() || + intersection(area_to_be_bridge, to_polygons(lower_region->fill_surfaces.filter_by_type(stInternal))).empty()) + continue; + bridging_angle = calculate_infill_rotation_angle(po, layer->lower_layer->id(), region_config.infill_direction.value, + region_config.sparse_infill_rotate_template.value) + 0.5 * PI; + // Orca: Apply model alignment as infill generation does, then normalize + // the undirected bridge angle to [0, PI), including negative rotations. + if (region_config.align_infill_direction_to_model) { + const auto &m = po->trafo().matrix(); + bridging_angle += std::atan2(double(m(1, 0)), double(m(0, 0))); + } + bridging_angle = std::fmod(bridging_angle, PI); + if (bridging_angle < 0.) + bridging_angle += PI; + break; + } } + // Orca: A different region below (e.g. a height modifier) needs the actual anchor + // directions. When there are no sparse anchors, use the expansion boundaries. + if (bridging_angle < 0.) + bridging_angle = determine_bridging_angle(area_to_be_bridge, to_lines(anchors.empty() ? boundary_plines : anchors)); - // ORCA: Internal bridge angle override + // Orca: Preserve the user's absolute or relative internal bridge angle + // override after automatic direction selection. if (candidate.region->region().config().internal_bridge_angle.value > 0) { - const auto ®ion_config = candidate.region->region().config(); const double custom_angle_rad = Geometry::deg2rad(region_config.internal_bridge_angle.value); if (region_config.relative_bridge_angle.value) bridging_angle += custom_angle_rad; @@ -3429,11 +3480,19 @@ void PrintObject::bridge_over_infill() } } + // Orca: Changing the bridge direction must not change its physical supports. + // Extend to actual sparse infill or the existing boundary anchors, never to + // a synthetic grid that merely has the same nominal angle and spacing. boundary_plines.insert(boundary_plines.end(), anchors.begin(), anchors.end()); if (!lightning_area.empty() && !intersection(area_to_be_bridge, lightning_area).empty()) { boundary_plines = intersection_pl(boundary_plines, expand(area_to_be_bridge, scale_(10))); } - Polygons bridging_area = construct_anchored_polygon(area_to_be_bridge, to_lines(boundary_plines), flow, bridging_angle); + // Orca: Use four samples per extrusion spacing for Hilbert/Octagram so the + // reconstructed boundary follows rounded anchors instead of cutting corners. + // Keep the original step for other patterns and at least one coordinate unit + // after integer division. This changes boundary accuracy, not infill density. + const coord_t scan_spacing = std::max(coord_t(1), flow.scaled_spacing() / (turning_pattern ? 4 : 1)); + Polygons bridging_area = construct_anchored_polygon(area_to_be_bridge, to_lines(boundary_plines), flow, bridging_angle, scan_spacing); // Check collision with other expanded surfaces { @@ -3447,7 +3506,9 @@ void PrintObject::bridge_over_infill() } } if (reconstruct) { - bridging_area = construct_anchored_polygon(area_to_be_bridge, to_lines(boundary_plines), flow, bridging_angle); + // Orca: Retain the same sampling accuracy when matching a nearby + // bridge's direction; rebuilding must not lose the curved supports. + bridging_area = construct_anchored_polygon(area_to_be_bridge, to_lines(boundary_plines), flow, bridging_angle, scan_spacing); } } @@ -3455,6 +3516,13 @@ void PrintObject::bridge_over_infill() // bridging_area = opening(bridging_area, flow.scaled_spacing()); bridging_area = opening(bridging_area, flow.scaled_spacing() * 0.75); bridging_area = closing(bridging_area, flow.scaled_spacing()); + // Orca: Opening/closing can pull rounded bridge ends away from their real + // supports. Restore those contacts after smoothing, preserving the cleaned + // area and the selected angle; do not smooth the restored contacts again. + if (turning_pattern && !bridging_area.empty()) { + bridging_area = union_(bridging_area, construct_anchored_polygon(bridging_area, to_lines(boundary_plines), flow, + bridging_angle, scan_spacing, true)); + } bridging_area = intersection(bridging_area, limiting_area); bridging_area = intersection(bridging_area, total_fill_area); bridging_area = diff(bridging_area, total_top_area); diff --git a/tests/fff_print/test_fill.cpp b/tests/fff_print/test_fill.cpp index 04e5b61831..aa81570e56 100644 --- a/tests/fff_print/test_fill.cpp +++ b/tests/fff_print/test_fill.cpp @@ -9,6 +9,7 @@ #include #include "libslic3r/ClipperUtils.hpp" +#include "libslic3r/AABBTreeLines.hpp" #include "libslic3r/Fill/Fill.hpp" #include "libslic3r/Flow.hpp" #include "libslic3r/Geometry.hpp" @@ -1229,3 +1230,68 @@ TEST_CASE("Smoothing multiline lightning infill keeps its outlines connected", " REQUIRE(smooth.point_count > sharp.point_count); REQUIRE(smooth.sharp_turns < sharp.sharp_turns); } + +TEST_CASE("Sparse plane-path anchors match the printed infill", "[Fill][InternalBridge][Regression]") +{ + // Orca: Compare generated anchors with actual extrusion across plane-path patterns, + // smoothing, multiline and rotations; an origin shift must not pass as valid support. + const std::string pattern = GENERATE("hilbertcurve", "octagramspiral", "archimedeanchords"); + const std::string smoothing = GENERATE("0%", "100%"); + const int multiline = GENERATE(1, 2); + const bool rotated = GENERATE(false, true); + const bool separated = GENERATE(false, true); + CAPTURE(pattern, smoothing, multiline, rotated, separated); + + auto config = DynamicPrintConfig::full_print_config(); + config.set_deserialize_strict({{"sparse_infill_pattern", pattern}, + {"sparse_infill_density", "15%"}, + {"sparse_infill_smooth_factor", smoothing}, + {"fill_multiline", multiline}, + {"infill_direction", 45}, + {"sparse_infill_rotate_template", rotated ? "0,25,50" : ""}, + {"align_infill_direction_to_model", rotated}, + {"separated_infills", separated}, + {"top_shell_layers", 0}, + {"bottom_shell_layers", 0}, + {"top_shell_thickness", 0}, + {"bottom_shell_thickness", 0}, + {"layer_height", 0.2}, + {"initial_layer_print_height", 0.2}, + {"resolution", 0.012}}); + Print print; + Model model; + TriangleMesh mesh = make_cube(30, 24, 1); + if (separated) { + // Orca: Two disconnected bodies in one object must each use their own infill origin. + TriangleMesh second = make_cube(30, 24, 1); + second.translate(50, 0, 0); + mesh.merge(second); + } + Slic3r::Test::init_print({mesh}, print, model, config, nullptr, false); + if (rotated) { + model.objects.front()->instances.front()->set_rotation(Vec3d(0., 0., Geometry::deg2rad(23.))); + print.apply(model, config); + } + print.process(); + + const Layer &layer = *print.objects().front()->get_layer(4); + Polylines printed; + for (const LayerRegion *region : layer.regions()) + for (const ExtrusionEntity *entity : region->fills.flatten().entities) + if (entity->role() == erInternalInfill) + entity->collect_polylines(printed); + REQUIRE_FALSE(printed.empty()); + const AABBTreeLines::LinesDistancer printed_tree(to_lines(printed)); + + // Orca: Exclude perimeter connections: anchoring and extrusion can trim those differently. + const Polylines anchors = intersection_pl(layer.generate_sparse_infill_polylines_for_anchoring(nullptr, nullptr, nullptr), + shrink(to_polygons(layer.lslices), scale_(3.))); + REQUIRE_FALSE(anchors.empty()); + double max_distance = 0.; + for (const Polyline &path : anchors) + for (const Point &point : path.equally_spaced_points(scale_(0.25))) + max_distance = std::max(max_distance, printed_tree.distance_from_lines(point)); + // Orca: Allow only the configured simplification tolerance; infill-scale offsets + // would hide anchors that no longer coincide with printed lines. + CHECK(unscale(max_distance) <= config.opt_float("resolution")); +} diff --git a/tests/fff_print/test_printobject.cpp b/tests/fff_print/test_printobject.cpp index fb7fe2c1fd..a373a1ad39 100644 --- a/tests/fff_print/test_printobject.cpp +++ b/tests/fff_print/test_printobject.cpp @@ -4,11 +4,18 @@ #include "libslic3r/Print.hpp" #include "libslic3r/Layer.hpp" #include "libslic3r/GCodeReader.hpp" +#include "libslic3r/ClipperUtils.hpp" +#include "libslic3r/AABBTreeLines.hpp" #include "test_helpers.hpp" +#include #include +#include #include +#include +#include +#include using namespace Slic3r; using namespace Slic3r::Test; @@ -130,3 +137,438 @@ TEST_CASE("Initial layer height is honored", "[PrintObject]") REQUIRE_THAT(*layer_zs.begin(), Catch::Matchers::WithinAbs(0.3, 1e-4)); REQUIRE_THAT(*std::next(layer_zs.begin()), Catch::Matchers::WithinAbs(0.5, 1e-4)); } + +static TriangleMesh internal_bridge_step() +{ + // Orca: The smaller tower leaves a shoulder whose solid skin needs internal bridges + // over the sparse infill in the base, without relying on an external model file. + TriangleMesh mesh = make_cube(30, 24, 3); + TriangleMesh tower = make_cube(14, 10, 1); + tower.translate(8, 7, 3); + mesh.merge(tower); + return mesh; +} + +static DynamicPrintConfig internal_bridge_config(const std::string &pattern, int multiline) +{ + auto config = DynamicPrintConfig::full_print_config(); + config.set_deserialize_strict({{"sparse_infill_pattern", pattern}, + {"fill_multiline", multiline}, + {"sparse_infill_density", "15%"}, + {"sparse_infill_smooth_factor", "100%"}, + {"infill_direction", 45}, + {"internal_bridge_angle", 0}, + {"thick_internal_bridges", true}, + {"top_shell_layers", 3}, + {"bottom_shell_layers", 2}, + {"top_shell_thickness", 0}, + {"bottom_shell_thickness", 0}, + {"layer_height", 0.2}, + {"initial_layer_print_height", 0.2}}); + return config; +} + +TEST_CASE("Internal bridge angles follow the lower infill layer and model rotation", "[PrintObject][InternalBridge][Regression]") +{ + const std::string pattern = GENERATE("hilbertcurve", "octagramspiral"); + // Orca: Cover both a central line (odd counts) and offset pairs (even counts). + const int multiline = GENERATE(1, 2, 3); + CAPTURE(multiline); + const double rotation = GENERATE(23., -123.); + const std::vector cycle{10., 30., 70.}; + auto config = internal_bridge_config(pattern, multiline); + config.set_deserialize_strict({{"sparse_infill_rotate_template", "10,30,70"}, + {"align_infill_direction_to_model", true}, + {"separated_infills", false}}); + Print print; + Model model; + init_print({internal_bridge_step()}, print, model, config, nullptr, false); + model.objects.front()->instances.front()->set_rotation(Vec3d(0., 0., Geometry::deg2rad(rotation))); + print.apply(model, config); + print.process(); + const PrintObject &object = *print.objects().front(); + size_t bridges = 0; + for (size_t i = 1; i < object.layer_count(); ++i) { + // Orca: The support is one layer below the bridge. Check the template and model + // rotation together, including normalization when the resulting angle is negative. + double expected = std::fmod(cycle[(i - 1) % cycle.size()] + 90. + rotation, 180.); + if (expected < 0.) expected += 180.; + for (const LayerRegion *region : object.get_layer(i)->regions()) + for (const Surface *surface : region->fill_surfaces.filter_by_type(stInternalBridge)) { + CAPTURE(pattern, rotation, i); + CHECK_THAT(Geometry::rad2deg(surface->bridge_angle), Catch::Matchers::WithinAbs(expected, 0.001)); + ++bridges; + } + } + REQUIRE(bridges > 0); +} + +TEST_CASE("Turning infill does not replace the anchors of another region", "[PrintObject][InternalBridge][Regression]") +{ + // Orca: Keep the right-hand region fixed while changing the left-hand pattern in the + // same object. Its bridge areas must be independent of a previous candidate's anchors. + const int multiline = GENERATE(1, 2, 3); + CAPTURE(multiline); + auto right_bridges = [multiline](const std::string &left_pattern) { + auto config = internal_bridge_config(left_pattern, multiline); + Print print; + Model model; + init_print({internal_bridge_step()}, print, model, config, nullptr, false); + TriangleMesh right = internal_bridge_step(); + right.translate(50, 0, 0); + ModelVolume *volume = model.objects.front()->add_volume(std::move(right)); + volume->config.set_key_value("sparse_infill_pattern", new ConfigOptionEnum(ipRectilinear)); + volume->config.set_key_value("infill_direction", new ConfigOptionFloat(17.)); + print.apply(model, config); + print.process(); + std::map result; + const PrintObject &object = *print.objects().front(); + for (size_t i = 0; i < object.layer_count(); ++i) + for (const LayerRegion *region : object.get_layer(i)->regions()) + if (region->region().config().infill_direction == 17.) + polygons_append(result[i], to_polygons(region->fill_surfaces.filter_by_type(stInternalBridge))); + return result; + }; + const auto baseline = right_bridges("rectilinear"); + const auto actual = right_bridges(GENERATE("hilbertcurve", "octagramspiral")); + REQUIRE(actual.size() == baseline.size()); + double total_area = 0.; + for (const auto &[layer, expected] : baseline) { + CAPTURE(layer); + const auto &polys = actual.at(layer); + CHECK(area(diff(expected, polys)) < scaled(1.) * scaled(1.) * 1e-6); + CHECK(area(diff(polys, expected)) < scaled(1.) * scaled(1.) * 1e-6); + total_area += area(expected); + } + REQUIRE(total_area > 0.); +} + +TEST_CASE("Rounded internal bridges end on printed support", "[PrintObject][InternalBridge][Regression]") +{ + const std::string pattern = GENERATE("hilbertcurve", "octagramspiral"); + const bool separated = GENERATE(false, true); + CAPTURE(pattern, separated); + auto config = internal_bridge_config(pattern, 1); + config.set_deserialize_strict({{"infill_wall_overlap", "0%"}, {"separated_infills", separated}}); + TriangleMesh mesh = internal_bridge_step(); + if (separated) { + TriangleMesh second = internal_bridge_step(); + second.translate(50, 0, 0); + mesh.merge(second); + } + Print print; + Model model; + init_print({mesh}, print, model, config, nullptr, false); + print.process(); + + // Orca: Check final extrusion endpoints after polygon cleanup and fill generation. + // A correct bridge angle and correct sparse anchors alone do not guarantee contact. + const PrintObject &object = *print.objects().front(); + size_t checked = 0; + for (size_t i = 1; i < object.layer_count(); ++i) { + Polygons support; + Polylines walls; + for (const LayerRegion *region : object.get_layer(i - 1)->regions()) { + region->perimeters.polygons_covered_by_width(support, 0.f); + region->fills.polygons_covered_by_width(support, 0.f); + region->perimeters.collect_polylines(walls); + } + REQUIRE_FALSE(support.empty()); + const AABBTreeLines::LinesDistancer support_tree(to_lines(union_(support))); + const AABBTreeLines::LinesDistancer wall_tree(to_lines(walls)); + for (const LayerRegion *region : object.get_layer(i)->regions()) + for (const ExtrusionEntity *entity : region->fills.flatten().entities) { + if (entity->role() != erInternalBridgeInfill) + continue; + const auto *path = dynamic_cast(entity); + REQUIRE(path != nullptr); + for (const Line &line : path->polyline.to_polyline().lines()) { + // Orca: Sample span ends, excluding short connectors and wall overlap. + if (line.length() < scale_(std::max(0.7, 3. * path->width))) + continue; + for (const Point &point : {line.a, line.b}) { + if (wall_tree.distance_from_lines(point) <= scale_(0.5)) + continue; + CAPTURE(i, point.x(), point.y()); + const double gap = unscale(support_tree.distance_from_lines(point)) - 0.5 * path->width; + CHECK(gap <= 0.1); + ++checked; + } + } + } + } + REQUIRE(checked > 0); +} + +TEST_CASE("Enabling separated infill recomputes body origins", "[PrintObject][InternalBridge][Regression]") +{ + const std::string pattern = GENERATE("hilbertcurve", "octagramspiral", "archimedeanchords"); + CAPTURE(pattern); + auto footprint = [&](bool reslice) { + auto config = internal_bridge_config(pattern, 2); + config.set_deserialize_strict({{"separated_infills", !reslice}}); + TriangleMesh mesh = internal_bridge_step(); + TriangleMesh second = internal_bridge_step(); + second.translate(50, 0, 0); + mesh.merge(second); + Print print; + Model model; + init_print({mesh}, print, model, config, nullptr, false); + print.process(); + if (reslice) { + // Orca: Enabling centering after a completed slice must rebuild the body + // origins now shared by bridge preparation and printed infill. + config.set_deserialize_strict({{"separated_infills", true}}); + print.apply(model, config); + print.process(); + } + Polygons result; + for (const LayerRegion *region : print.objects().front()->get_layer(4)->regions()) + region->fills.polygons_covered_by_width(result, 0.f); + return union_(result); + }; + const Polygons fresh = footprint(false); + const Polygons resliced = footprint(true); + REQUIRE_FALSE(fresh.empty()); + CHECK(area(diff(fresh, resliced)) < scaled(1.) * scaled(1.) * 1e-6); + CHECK(area(diff(resliced, fresh)) < scaled(1.) * scaled(1.) * 1e-6); +} + +TEST_CASE("Surface centering survives changes to separated infill settings", "[PrintObject][SurfaceInfill][Regression]") +{ + const std::string pattern = GENERATE("archimedeanchords", "octagramspiral"); + const std::string initial_center = GENERATE("each_surface", "each_model", "each_assembly"); + const std::string final_center = GENERATE("each_surface", "each_model", "each_assembly"); + const bool separated = GENERATE(false, true); + const std::string top_order = GENERATE("default", "outward", "inward"); + const std::string bottom_order = top_order == "outward" ? "inward" : top_order == "inward" ? "outward" : "default"; + const std::string density = GENERATE("80%", "100%"); + const bool change_center = initial_center != final_center; + CAPTURE(pattern, initial_center, final_center, separated, top_order, bottom_order, density); + + auto config = DynamicPrintConfig::full_print_config(); + config.set_deserialize_strict({{"top_surface_pattern", pattern}, + {"bottom_surface_pattern", pattern}, + {"top_surface_fill_order", top_order}, + {"bottom_surface_fill_order", bottom_order}, + {"top_surface_density", density}, + {"bottom_surface_density", density}, + {"center_of_surface_pattern", initial_center}, + {"separated_infills", change_center ? separated : !separated}, + {"sparse_infill_pattern", "rectilinear"}, + {"sparse_infill_density", "15%"}, + {"top_shell_layers", 2}, + {"bottom_shell_layers", 2}, + {"top_shell_thickness", 0}, + {"bottom_shell_thickness", 0}, + {"layer_height", 0.2}, + {"initial_layer_print_height", 0.2}}); + + // Orca: Two disconnected bodies exercise per-body centering. The offset tower also + // makes each-surface and each-model centering differ on the top surfaces. + TriangleMesh mesh = make_cube(30, 24, 2); + TriangleMesh tower = make_cube(12, 10, 1); + tower.translate(4, 3, 2); + mesh.merge(tower); + TriangleMesh second = mesh; + second.translate(50, 0, 0); + mesh.merge(second); + + // Orca: Equal footprints can hide reordered or reversed paths. Retain their point + // sequences and ordering protection to cover the directional surface behavior too. + struct SurfaceFillSnapshot { + std::map> paths; + bool protected_order = true; + }; + auto surface_fills = [](const Print &print) { + std::map, SurfaceFillSnapshot> result; + const PrintObject &object = *print.objects().front(); + for (size_t i = 0; i < object.layer_count(); ++i) { + auto collect = [&](const auto &self, const ExtrusionEntity &entity, bool no_sort) -> void { + if (const auto *collection = dynamic_cast(&entity)) { + for (const ExtrusionEntity *child : collection->entities) + self(self, *child, no_sort || collection->no_sort); + } else if (entity.role() == erTopSolidInfill || entity.role() == erBottomSurface) { + const auto *path = dynamic_cast(&entity); + REQUIRE(path != nullptr); + auto &snapshot = result[{i, entity.role()}]; + // Orca: The centered test model has one body on either side of X=0. + // Their traversal order may vary; preserve path order within each body. + Points points = path->polyline.to_polyline().points; + REQUIRE_FALSE(points.empty()); + snapshot.paths[points.front().x() > 0].push_back(std::move(points)); + snapshot.protected_order &= no_sort && !path->can_reverse(); + } + }; + for (const LayerRegion *region : object.get_layer(i)->regions()) + collect(collect, region->fills, false); + } + return result; + }; + + Print print; + Model model; + init_print({mesh}, print, model, config, nullptr, false); + print.process(); + const auto initial = surface_fills(print); + config.set_deserialize_strict({{"center_of_surface_pattern", final_center}, {"separated_infills", separated}}); + print.apply(model, config); + // Orca: Preparation owns the body origins, and its invalidation must also force + // regeneration of top/bottom extrusion paths, even when sparse infill is unchanged. + CHECK_FALSE(print.objects().front()->is_step_done(posPrepareInfill)); + CHECK_FALSE(print.objects().front()->is_step_done(posInfill)); + print.process(); + const auto resliced = surface_fills(print); + + Print fresh_print; + Model fresh_model; + init_print({mesh}, fresh_print, fresh_model, config, nullptr, false); + fresh_print.process(); + const auto fresh = surface_fills(fresh_print); + REQUIRE_FALSE(fresh.empty()); + REQUIRE(resliced.size() == fresh.size()); + std::set roles; + bool changed_paths = false; + for (const auto &entry : fresh) { + CAPTURE(entry.first.first, entry.first.second); + REQUIRE_FALSE(entry.second.paths.empty()); + roles.insert(entry.first.second); + REQUIRE(resliced.count(entry.first) == 1); + REQUIRE(initial.count(entry.first) == 1); + const auto &actual = resliced.at(entry.first); + const auto &expected = entry.second; + const auto &before = initial.at(entry.first); + CHECK((actual.paths == expected.paths)); + if (!change_center) + CHECK((actual.paths == before.paths)); + if (top_order != "default") { + CHECK(expected.protected_order); + CHECK(actual.protected_order); + CHECK(before.protected_order); + } + changed_paths |= expected.paths != before.paths; + } + CHECK(roles.count(erTopSolidInfill) == 1); + CHECK(roles.count(erBottomSurface) == 1); + // Orca: Guard against a vacuous comparison: changing surface centering must change + // the printed pattern, while toggling separated sparse infill must leave it alone. + CHECK(changed_paths == change_center); +} + +TEST_CASE("Separated infill keeps fragmented and nested bodies independent", "[PrintObject][SurfaceInfill][Regression]") +{ + constexpr size_t grid_size = 8; + TriangleMesh mesh; + auto add_box = [&](double x, double y, double width, double depth) { + TriangleMesh box = make_cube(width, depth, 0.6); + box.translate(x, y, 0); + mesh.merge(box); + }; + // Orca: Many small islands exercise spatial pruning and the tree's original + // island indices. A pillar inside a frame also overlaps its bounding box, + // but must remain a separate body because it lies entirely inside the hole. + for (size_t x = 0; x < grid_size; ++ x) + for (size_t y = 0; y < grid_size; ++ y) + add_box(6 * x, 6 * y, 3, 3); + add_box(54, 0, 20, 4); + add_box(54, 16, 20, 4); + add_box(54, 0, 4, 20); + add_box(70, 0, 4, 20); + add_box(62, 8, 4, 4); + + auto config = DynamicPrintConfig::full_print_config(); + config.set_deserialize_strict({{"separated_infills", true}, + {"center_of_surface_pattern", "each_surface"}, + {"layer_height", 0.2}, + {"initial_layer_print_height", 0.2}, + {"elefant_foot_compensation", 0}, + {"wall_loops", 1}}); + Print print; + Model model; + init_print({mesh}, print, model, config, nullptr, false); + // Orca: Prepare body bounds through the public pipeline, then inspect the object read-only. + print.process(); + const PrintObject &object = *print.objects().front(); + REQUIRE(object.layer_count() > 1); + for (const Layer *layer : object.layers()) { + REQUIRE(layer->lslices.size() == grid_size * grid_size + 2); + REQUIRE(layer->lslices_separated_component_bboxes.size() == layer->lslices.size()); + size_t holes = 0; + for (size_t i = 0; i < layer->lslices.size(); ++ i) { + const BoundingBox &body = layer->lslices_separated_component_bboxes[i]; + const BoundingBox &island = layer->lslices_bboxes[i]; + CHECK(body.min == island.min); + CHECK(body.max == island.max); + holes += layer->lslices[i].holes.size(); + } + CHECK(holes == 1); + } +} + +TEST_CASE("Body centering survives islands merging and splitting between layers", "[PrintObject][SurfaceInfill][Regression]") +{ + const bool separated = GENERATE(false, true); + CAPTURE(separated); + // Orca: Four posts join through horizontal then vertical rails, creating a + // cycle of overlaps before splitting into four islands again. This exercises + // redundant connections and indexing either adjacent layer. A fifth post + // stays separate at every height. + TriangleMesh mesh; + for (int x : {0, 8}) + for (int y : {0, 8}) { + TriangleMesh post = make_cube(4, 4, 1); + post.translate(x, y, 0); + mesh.merge(post); + } + for (int y : {0, 8}) { + TriangleMesh rail = make_cube(12, 4, 0.2); + rail.translate(0, y, 0.2); + mesh.merge(rail); + } + for (int x : {0, 8}) { + TriangleMesh rail = make_cube(4, 12, 0.2); + rail.translate(x, 0, 0.4); + mesh.merge(rail); + } + TriangleMesh isolated = make_cube(4, 4, 1); + isolated.translate(20, 0, 0); + mesh.merge(isolated); + + auto config = DynamicPrintConfig::full_print_config(); + config.set_deserialize_strict({{"separated_infills", separated}, + {"center_of_surface_pattern", separated ? "each_surface" : "each_model"}, + {"layer_height", 0.2}, + {"initial_layer_print_height", 0.2}, + {"elefant_foot_compensation", 0}, + {"wall_loops", 1}}); + Print print; + Model model; + init_print({mesh}, print, model, config, nullptr, false); + // Orca: Prepare body bounds through the public pipeline, then inspect the object read-only. + print.process(); + const PrintObject &object = *print.objects().front(); + REQUIRE(object.layer_count() == 5); + REQUIRE(object.get_layer(0)->lslices.size() == 5); + REQUIRE(object.get_layer(1)->lslices.size() == 3); + REQUIRE(object.get_layer(2)->lslices.size() == 3); + REQUIRE(object.get_layer(4)->lslices.size() == 5); + + BoundingBox isolated_bbox = object.get_layer(0)->lslices_bboxes.front(); + for (const BoundingBox &bbox : object.get_layer(0)->lslices_bboxes) + if (bbox.min.x() > isolated_bbox.min.x()) + isolated_bbox = bbox; + BoundingBox connected_bbox; + for (const Layer *layer : object.layers()) + for (const BoundingBox &bbox : layer->lslices_bboxes) + if (bbox.min.x() < isolated_bbox.min.x()) + connected_bbox.merge(bbox); + for (const Layer *layer : object.layers()) { + REQUIRE(layer->lslices_separated_component_bboxes.size() == layer->lslices.size()); + for (size_t i = 0; i < layer->lslices.size(); ++ i) { + const BoundingBox &expected = layer->lslices_bboxes[i].min.x() < isolated_bbox.min.x() ? connected_bbox : isolated_bbox; + const BoundingBox &actual = layer->lslices_separated_component_bboxes[i]; + CHECK(actual.min == expected.min); + CHECK(actual.max == expected.max); + } + } +} From a93c6ea67b11376ed27c80acea7878b9fbfbf270 Mon Sep 17 00:00:00 2001 From: Ian Chua Date: Thu, 10 Sep 2026 19:29:58 +0800 Subject: [PATCH 3/7] hotfix: system bundles being copied from resources folder on every startup --- src/slic3r/Utils/PresetUpdater.cpp | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/slic3r/Utils/PresetUpdater.cpp b/src/slic3r/Utils/PresetUpdater.cpp index b328c43cca..23957f6500 100644 --- a/src/slic3r/Utils/PresetUpdater.cpp +++ b/src/slic3r/Utils/PresetUpdater.cpp @@ -1106,8 +1106,8 @@ void PresetUpdater::priv::check_installed_vendor_profiles() const const auto is_vendor_enabled = (vendor_name == PresetBundle::ORCA_DEFAULT_BUNDLE) // always update configs from resource to vendor for ORCA_DEFAULT_BUNDLE || (enabled_vendors.find(vendor_name) != enabled_vendors.end()); - if (enabled_config_update) { - if (is_vendor_installed(vendor_name)) { + if (is_vendor_installed(vendor_name)) { + if (enabled_config_update) { if (is_vendor_enabled) { // Orca: whichever form of the vendor resources ships at the newer // version is the one installing lays down, and the one to judge @@ -1122,17 +1122,12 @@ void PresetUpdater::priv::check_installed_vendor_profiles() const << resource_ver.to_string() << " from resource, old version " << vendor_ver.to_string(); bundles.insert(vendor_name); } - } - else { - //need to be removed because not installed + } else { + // need to be removed because not installed remove_installed_vendor(vendor_name); } } - else if (is_vendor_enabled) { - bundles.insert(vendor_name); - } - } - else if (is_vendor_enabled) { + } else if (is_vendor_enabled) { bundles.insert(vendor_name); } } From d97dea2c41d554db3fd115886ce6b67e5beb146c Mon Sep 17 00:00:00 2001 From: Kris Austin Date: Thu, 10 Sep 2026 16:08:54 -0500 Subject: [PATCH 4/7] build: clear 10 driver warnings from CGAL's fp flag pair under clang-cl (#15629) --- src/libslic3r/CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index e860f50280..ffc6b5cee6 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -558,6 +558,12 @@ if (_opts) target_compile_options(libslic3r_cgal PRIVATE "${_opts_bad}") endif() +if (IS_CLANG_CL) + # CGAL passes /fp:strict /fp:except-. clang-cl reports the second as overriding part of + # the first; the settings cc1 receives are the same ones MSVC produces from that pair. + target_compile_options(libslic3r_cgal PRIVATE -Wno-overriding-option) +endif () + target_link_libraries(libslic3r_cgal PRIVATE ${_cgal_tgt} admesh libigl mcut boost_libs) if (MSVC AND "${CMAKE_SIZEOF_VOID_P}" STREQUAL "4") # 32 bit MSVC workaround From 0a630738f1e6467f7602b4948a901b8381daab78 Mon Sep 17 00:00:00 2001 From: Kiss Lorand <50251547+kisslorand@users.noreply.github.com> Date: Fri, 11 Sep 2026 00:15:06 +0300 Subject: [PATCH 5/7] Fix untranslated language dialog captions (#15600) --- localization/i18n/OrcaSlicer.pot | 4 - localization/i18n/ca/OrcaSlicer_ca.po | 4 - localization/i18n/cs/OrcaSlicer_cs.po | 4 - localization/i18n/de/OrcaSlicer_de.po | 4 - localization/i18n/en/OrcaSlicer_en.po | 4 - localization/i18n/es/OrcaSlicer_es.po | 4 - localization/i18n/eu/OrcaSlicer_eu.po | 4 - localization/i18n/fr/OrcaSlicer_fr.po | 4 - localization/i18n/hu/OrcaSlicer_hu.po | 4 - localization/i18n/it/OrcaSlicer_it.po | 4 - localization/i18n/ja/OrcaSlicer_ja.po | 4 - localization/i18n/ko/OrcaSlicer_ko.po | 4 - localization/i18n/lt/OrcaSlicer_lt.po | 4 - localization/i18n/nl/OrcaSlicer_nl.po | 4 - localization/i18n/pl/OrcaSlicer_pl.po | 4 - localization/i18n/pt_BR/OrcaSlicer_pt_BR.po | 4 - localization/i18n/ru/OrcaSlicer_ru.po | 4 - localization/i18n/sv/OrcaSlicer_sv.po | 4 - localization/i18n/th/OrcaSlicer_th.po | 4 - localization/i18n/tr/OrcaSlicer_tr.po | 4 - localization/i18n/uk/OrcaSlicer_uk.po | 4 - localization/i18n/vi/OrcaSlicer_vi.po | 4 - localization/i18n/zh_CN/OrcaSlicer_zh_CN.po | 4 - localization/i18n/zh_TW/OrcaSlicer_zh_TW.po | 4 - src/slic3r/GUI/GUI_App.cpp | 221 -------------------- src/slic3r/GUI/GUI_App.hpp | 3 - src/slic3r/GUI/MainFrame.cpp | 89 -------- src/slic3r/GUI/Preferences.cpp | 23 +- src/slic3r/GUI/Widgets/FanControl.cpp | 2 +- 29 files changed, 4 insertions(+), 430 deletions(-) diff --git a/localization/i18n/OrcaSlicer.pot b/localization/i18n/OrcaSlicer.pot index 92b25f26c0..bbdc0e59be 100644 --- a/localization/i18n/OrcaSlicer.pot +++ b/localization/i18n/OrcaSlicer.pot @@ -2293,8 +2293,6 @@ msgstr "" msgid "%s has been removed." msgstr "" -msgid "Switching application language" -msgstr "" msgid "Select the language" msgstr "" @@ -8294,8 +8292,6 @@ msgstr "" msgid "Language selection" msgstr "" -msgid "Switching application language while some presets are modified." -msgstr "" msgid "Asia-Pacific" msgstr "" diff --git a/localization/i18n/ca/OrcaSlicer_ca.po b/localization/i18n/ca/OrcaSlicer_ca.po index 91ba0e2f2a..373eb6e9fd 100644 --- a/localization/i18n/ca/OrcaSlicer_ca.po +++ b/localization/i18n/ca/OrcaSlicer_ca.po @@ -2522,8 +2522,6 @@ msgstr "Hi ha una actualització disponible. Obriu el quadre de diàleg del paqu msgid "%s has been removed." msgstr "%s s'ha eliminat." -msgid "Switching application language" -msgstr "Canvi d'idioma de l'aplicació" msgid "Select the language" msgstr "Seleccioneu l'idioma" @@ -8924,8 +8922,6 @@ msgstr "Voleu continuar?" msgid "Language selection" msgstr "Selecció d'idiomes" -msgid "Switching application language while some presets are modified." -msgstr "Canviant l'idioma de l'aplicació mentre es modifiquen alguns perfils." msgid "Asia-Pacific" msgstr "Àsia-Pacífic" diff --git a/localization/i18n/cs/OrcaSlicer_cs.po b/localization/i18n/cs/OrcaSlicer_cs.po index 9884888723..b0d64c8005 100644 --- a/localization/i18n/cs/OrcaSlicer_cs.po +++ b/localization/i18n/cs/OrcaSlicer_cs.po @@ -2482,8 +2482,6 @@ msgstr "Je k dispozici aktualizace. Otevřete dialog balíčku předvoleb a prov msgid "%s has been removed." msgstr "%s bylo odstraněno." -msgid "Switching application language" -msgstr "Přepnutí jazyka aplikace" msgid "Select the language" msgstr "Zvolte jazyk" @@ -8882,8 +8880,6 @@ msgstr "Chcete pokračovat?" msgid "Language selection" msgstr "Výběr jazyka" -msgid "Switching application language while some presets are modified." -msgstr "Přepnutí jazyka aplikace, když jsou některé předvolby upraveny." msgid "Asia-Pacific" msgstr "Asie-Pacifik" diff --git a/localization/i18n/de/OrcaSlicer_de.po b/localization/i18n/de/OrcaSlicer_de.po index bb096c0a23..bd25405cc3 100644 --- a/localization/i18n/de/OrcaSlicer_de.po +++ b/localization/i18n/de/OrcaSlicer_de.po @@ -2430,8 +2430,6 @@ msgstr "Es ist ein Update verfügbar. Öffnen Sie den Profilbündel-Dialog, um e msgid "%s has been removed." msgstr "%s wurde entfernt." -msgid "Switching application language" -msgstr "Wechsel der Sprache" msgid "Select the language" msgstr "Sprache wählen" @@ -8754,8 +8752,6 @@ msgstr "Möchten Sie fortfahren?" msgid "Language selection" msgstr "Sprachauswahl" -msgid "Switching application language while some presets are modified." -msgstr "Umschalten der Anwendungssprache, während einige Profile geändert werden." msgid "Asia-Pacific" msgstr "Asien-Pazifik" diff --git a/localization/i18n/en/OrcaSlicer_en.po b/localization/i18n/en/OrcaSlicer_en.po index b949aea730..35c7f8a87a 100644 --- a/localization/i18n/en/OrcaSlicer_en.po +++ b/localization/i18n/en/OrcaSlicer_en.po @@ -2289,8 +2289,6 @@ msgstr "" msgid "%s has been removed." msgstr "" -msgid "Switching application language" -msgstr "" msgid "Select the language" msgstr "" @@ -8290,8 +8288,6 @@ msgstr "" msgid "Language selection" msgstr "" -msgid "Switching application language while some presets are modified." -msgstr "" msgid "Asia-Pacific" msgstr "" diff --git a/localization/i18n/es/OrcaSlicer_es.po b/localization/i18n/es/OrcaSlicer_es.po index e1d86c2fe8..efe4c7dbcd 100644 --- a/localization/i18n/es/OrcaSlicer_es.po +++ b/localization/i18n/es/OrcaSlicer_es.po @@ -2356,8 +2356,6 @@ msgstr "Hay una actualización disponible. Abra el cuadro de diálogo del paquet msgid "%s has been removed." msgstr "Se ha eliminado %s." -msgid "Switching application language" -msgstr "Cambiando el idioma de la aplicación" msgid "Select the language" msgstr "Seleccionar el idioma" @@ -8529,8 +8527,6 @@ msgstr "¿Quieres continuar?" msgid "Language selection" msgstr "Selección de idiomas" -msgid "Switching application language while some presets are modified." -msgstr "Cambiando idioma de la aplicación mientras se modifican algunos perfiles." msgid "Asia-Pacific" msgstr "Asia-Pacífico" diff --git a/localization/i18n/eu/OrcaSlicer_eu.po b/localization/i18n/eu/OrcaSlicer_eu.po index a475d76ab2..698941018e 100644 --- a/localization/i18n/eu/OrcaSlicer_eu.po +++ b/localization/i18n/eu/OrcaSlicer_eu.po @@ -2390,8 +2390,6 @@ msgstr "Eguneratze bat dago erabilgarri. Ireki aurrezarpen-paketeen elkarrizketa msgid "%s has been removed." msgstr "%s kendu da." -msgid "Switching application language" -msgstr "Aplikazioaren hizkuntza aldatzen" msgid "Select the language" msgstr "Hautatu hizkuntza" @@ -8610,8 +8608,6 @@ msgstr "Jarraitu nahi duzu?" msgid "Language selection" msgstr "Hizkuntza-hautaketa" -msgid "Switching application language while some presets are modified." -msgstr "Aplikazioaren hizkuntza aldatzen ari da aurrezarpen batzuk aldatuta dauden bitartean." msgid "Asia-Pacific" msgstr "Asia-Pazifikoa" diff --git a/localization/i18n/fr/OrcaSlicer_fr.po b/localization/i18n/fr/OrcaSlicer_fr.po index de356edd7d..6344696234 100644 --- a/localization/i18n/fr/OrcaSlicer_fr.po +++ b/localization/i18n/fr/OrcaSlicer_fr.po @@ -2414,8 +2414,6 @@ msgstr "Une mise à jour est disponible. Ouvrez la boîte de dialogue du paquet msgid "%s has been removed." msgstr "%s a été supprimé." -msgid "Switching application language" -msgstr "Changer la langue de l'application" msgid "Select the language" msgstr "Sélectionner la langue" @@ -8678,8 +8676,6 @@ msgstr "Voulez-vous continuer ?" msgid "Language selection" msgstr "Sélection de la langue" -msgid "Switching application language while some presets are modified." -msgstr "Changement de langue de l’application alors que certains préréglages sont modifiés." msgid "Asia-Pacific" msgstr "Asie-Pacifique" diff --git a/localization/i18n/hu/OrcaSlicer_hu.po b/localization/i18n/hu/OrcaSlicer_hu.po index 2c00949e15..78c06dd4c0 100644 --- a/localization/i18n/hu/OrcaSlicer_hu.po +++ b/localization/i18n/hu/OrcaSlicer_hu.po @@ -2460,8 +2460,6 @@ msgstr "Frissítés érhető el. Nyisd meg a beállításcsomag párbeszédablak msgid "%s has been removed." msgstr "%s eltávolítva." -msgid "Switching application language" -msgstr "Alkalmazás nyelvének váltása" msgid "Select the language" msgstr "Válaszd ki a nyelvet" @@ -8806,8 +8804,6 @@ msgstr "Szeretnéd folytatni?" msgid "Language selection" msgstr "Nyelv kiválasztása" -msgid "Switching application language while some presets are modified." -msgstr "Alkalmazás nyelvének átváltása, miközben egyes beállítások módosultak." msgid "Asia-Pacific" msgstr "Ázsia-Csendes-óceáni térség" diff --git a/localization/i18n/it/OrcaSlicer_it.po b/localization/i18n/it/OrcaSlicer_it.po index 1aa2f15701..cf5099bca3 100644 --- a/localization/i18n/it/OrcaSlicer_it.po +++ b/localization/i18n/it/OrcaSlicer_it.po @@ -2466,8 +2466,6 @@ msgstr "È disponibile un aggiornamento. Apri la finestra di dialogo del bundle msgid "%s has been removed." msgstr "%s è stato rimosso." -msgid "Switching application language" -msgstr "Cambio lingua applicazione" msgid "Select the language" msgstr "Seleziona la lingua" @@ -8807,8 +8805,6 @@ msgstr "Vuoi continuare?" msgid "Language selection" msgstr "Selezione lingua" -msgid "Switching application language while some presets are modified." -msgstr "Cambio lingua applicazione durante la modifica di alcuni profili." msgid "Asia-Pacific" msgstr "Asia-Pacifico" diff --git a/localization/i18n/ja/OrcaSlicer_ja.po b/localization/i18n/ja/OrcaSlicer_ja.po index 9067928361..99cd0d0a83 100644 --- a/localization/i18n/ja/OrcaSlicer_ja.po +++ b/localization/i18n/ja/OrcaSlicer_ja.po @@ -2473,8 +2473,6 @@ msgstr "アップデートが利用可能です。プリセットバンドルの msgid "%s has been removed." msgstr "%sを削除しました。" -msgid "Switching application language" -msgstr "アプリケーション言語の切り替え" msgid "Select the language" msgstr "言語を選択" @@ -8825,8 +8823,6 @@ msgstr "続行しますか?" msgid "Language selection" msgstr "言語選択" -msgid "Switching application language while some presets are modified." -msgstr "アプリケーション言語を切り替える時に、プリセットの変更があります" msgid "Asia-Pacific" msgstr "アジア太平洋地域" diff --git a/localization/i18n/ko/OrcaSlicer_ko.po b/localization/i18n/ko/OrcaSlicer_ko.po index fef0a09398..8d12c90228 100644 --- a/localization/i18n/ko/OrcaSlicer_ko.po +++ b/localization/i18n/ko/OrcaSlicer_ko.po @@ -2481,8 +2481,6 @@ msgstr "사용 가능한 업데이트가 있습니다. 사전 설정 번들 대 msgid "%s has been removed." msgstr "%s이(가) 제거되었습니다." -msgid "Switching application language" -msgstr "응용 프로그램 언어 전환" msgid "Select the language" msgstr "언어 선택" @@ -8860,8 +8858,6 @@ msgstr "계속하시겠습니까?" msgid "Language selection" msgstr "언어 선택" -msgid "Switching application language while some presets are modified." -msgstr "일부 사전 설정이 수정되는 동안 응용 프로그램 언어를 전환합니다." msgid "Asia-Pacific" msgstr "아시아 태평양" diff --git a/localization/i18n/lt/OrcaSlicer_lt.po b/localization/i18n/lt/OrcaSlicer_lt.po index 80273427f0..67d02bef6d 100644 --- a/localization/i18n/lt/OrcaSlicer_lt.po +++ b/localization/i18n/lt/OrcaSlicer_lt.po @@ -2449,8 +2449,6 @@ msgstr "Yra prieinamas atnaujinimas. Atidarykite profilių paketo dialogo langą msgid "%s has been removed." msgstr "%s buvo pašalintas." -msgid "Switching application language" -msgstr "Perjungiama programos kalba" msgid "Select the language" msgstr "Pasirinkite kalbą" @@ -8797,8 +8795,6 @@ msgstr "Ar norite tęsti?" msgid "Language selection" msgstr "Kalbos pasirinkimas" -msgid "Switching application language while some presets are modified." -msgstr "Keičiama programos kalba, kai yra pakeistų profilių." msgid "Asia-Pacific" msgstr "Azija-Ramusis vandenynas" diff --git a/localization/i18n/nl/OrcaSlicer_nl.po b/localization/i18n/nl/OrcaSlicer_nl.po index 720f4ba6f5..eff0fdc6b0 100644 --- a/localization/i18n/nl/OrcaSlicer_nl.po +++ b/localization/i18n/nl/OrcaSlicer_nl.po @@ -2679,8 +2679,6 @@ msgstr "Er is een update beschikbaar. Open het dialoogvenster voor de voorinstel msgid "%s has been removed." msgstr "%s is verwijderd." -msgid "Switching application language" -msgstr "De taal van de applicatie wordt aangepast" msgid "Select the language" msgstr "Kies de taal" @@ -9602,8 +9600,6 @@ msgstr "Wilt u doorgaan?" msgid "Language selection" msgstr "Taal selectie" -msgid "Switching application language while some presets are modified." -msgstr "De taal van de toepassing aanpaasen terwijl sommige voorinstellingen zijn aangepast." msgid "Asia-Pacific" msgstr "Azië-Pacific" diff --git a/localization/i18n/pl/OrcaSlicer_pl.po b/localization/i18n/pl/OrcaSlicer_pl.po index 3d7fb083ef..6f74f4b602 100644 --- a/localization/i18n/pl/OrcaSlicer_pl.po +++ b/localization/i18n/pl/OrcaSlicer_pl.po @@ -2512,8 +2512,6 @@ msgstr "Dostępna jest aktualizacja. Otwórz okno pakietu profili, aby ją zains msgid "%s has been removed." msgstr "%s został usunięty." -msgid "Switching application language" -msgstr "Zmiana języka aplikacji" msgid "Select the language" msgstr "Wybierz język" @@ -9016,8 +9014,6 @@ msgstr "Czy kontynuować?" msgid "Language selection" msgstr "Wybór języka" -msgid "Switching application language while some presets are modified." -msgstr "Zmiana języka aplikacji przy jednoczesnym istniejących zmodyfikowanych ustawieniach." msgid "Asia-Pacific" msgstr "Azja i Pacyfik" diff --git a/localization/i18n/pt_BR/OrcaSlicer_pt_BR.po b/localization/i18n/pt_BR/OrcaSlicer_pt_BR.po index 066b8b310b..6f7bf473c0 100644 --- a/localization/i18n/pt_BR/OrcaSlicer_pt_BR.po +++ b/localization/i18n/pt_BR/OrcaSlicer_pt_BR.po @@ -2356,8 +2356,6 @@ msgstr "Há uma atualização disponível. Abra a caixa de diálogo do pacote de msgid "%s has been removed." msgstr "%s foi removido." -msgid "Switching application language" -msgstr "Alternando o idioma do aplicativo" msgid "Select the language" msgstr "Selecione o idioma" @@ -8572,8 +8570,6 @@ msgstr "Você deseja continuar?" msgid "Language selection" msgstr "Seleção de idioma" -msgid "Switching application language while some presets are modified." -msgstr "Alternando idioma do aplicativo enquanto algumas predefinições são modificadas." msgid "Asia-Pacific" msgstr "Ásia-Pacífico" diff --git a/localization/i18n/ru/OrcaSlicer_ru.po b/localization/i18n/ru/OrcaSlicer_ru.po index b5e6baffe0..2e33546ae8 100644 --- a/localization/i18n/ru/OrcaSlicer_ru.po +++ b/localization/i18n/ru/OrcaSlicer_ru.po @@ -2431,8 +2431,6 @@ msgstr "Доступно обновление. Проверьте меню па msgid "%s has been removed." msgstr "%s был удалён." -msgid "Switching application language" -msgstr "Изменение языка приложения" msgid "Select the language" msgstr "Выбор языка" @@ -8852,8 +8850,6 @@ msgstr "Хотите продолжить?" msgid "Language selection" msgstr "Выбор языка" -msgid "Switching application language while some presets are modified." -msgstr "Смена языка приложения при изменении некоторых профилей." msgid "Asia-Pacific" msgstr "Азиатско-Тихоокеанский" diff --git a/localization/i18n/sv/OrcaSlicer_sv.po b/localization/i18n/sv/OrcaSlicer_sv.po index b4e3bf51bc..b9aa481d4c 100644 --- a/localization/i18n/sv/OrcaSlicer_sv.po +++ b/localization/i18n/sv/OrcaSlicer_sv.po @@ -2767,8 +2767,6 @@ msgstr "Det finns en uppdatering tillgänglig. Öppna dialogrutan för förinst msgid "%s has been removed." msgstr "%s har tagits bort." -msgid "Switching application language" -msgstr "Byt applikationsspråk" msgid "Select the language" msgstr "Välj språk" @@ -9694,8 +9692,6 @@ msgstr "Fortsätta?" msgid "Language selection" msgstr "Språkval" -msgid "Switching application language while some presets are modified." -msgstr "Byter språk medans inställningarna ändras." msgid "Asia-Pacific" msgstr "Asien-Stillahavsområdet" diff --git a/localization/i18n/th/OrcaSlicer_th.po b/localization/i18n/th/OrcaSlicer_th.po index 935bb83a3b..ee7430015e 100644 --- a/localization/i18n/th/OrcaSlicer_th.po +++ b/localization/i18n/th/OrcaSlicer_th.po @@ -2456,8 +2456,6 @@ msgstr "มีอัปเดตพร้อมใช้งาน เปิด msgid "%s has been removed." msgstr "ลบ %s แล้ว" -msgid "Switching application language" -msgstr "การเปลี่ยนภาษาของแอปพลิเคชัน" msgid "Select the language" msgstr "เลือกภาษา" @@ -8758,8 +8756,6 @@ msgstr "ต้องการดำเนินการต่อหรือไ msgid "Language selection" msgstr "การเลือกภาษา" -msgid "Switching application language while some presets are modified." -msgstr "การสลับภาษาของแอปพลิเคชันในขณะที่มีการแก้ไขค่าที่ตั้งไว้บางส่วน" msgid "Asia-Pacific" msgstr "เอเชียแปซิฟิก" diff --git a/localization/i18n/tr/OrcaSlicer_tr.po b/localization/i18n/tr/OrcaSlicer_tr.po index 02d54aa39f..0cf9d57412 100644 --- a/localization/i18n/tr/OrcaSlicer_tr.po +++ b/localization/i18n/tr/OrcaSlicer_tr.po @@ -2480,8 +2480,6 @@ msgstr "Kullanılabilir bir güncelleme var. Güncellemek için ön ayar paketi msgid "%s has been removed." msgstr "%s kaldırıldı." -msgid "Switching application language" -msgstr "Uygulama dilini değiştirme" msgid "Select the language" msgstr "Dili seçin" @@ -8860,8 +8858,6 @@ msgstr "Devam etmek istiyor musun?" msgid "Language selection" msgstr "Dil seçimi" -msgid "Switching application language while some presets are modified." -msgstr "Bazı ön ayarlar değiştirilirken uygulama dilinin değiştirilmesi." msgid "Asia-Pacific" msgstr "Asya Pasifik" diff --git a/localization/i18n/uk/OrcaSlicer_uk.po b/localization/i18n/uk/OrcaSlicer_uk.po index d72a1f9792..ec9e97bae0 100644 --- a/localization/i18n/uk/OrcaSlicer_uk.po +++ b/localization/i18n/uk/OrcaSlicer_uk.po @@ -2424,8 +2424,6 @@ msgstr "Доступне оновлення. Відкрийте вікно на msgid "%s has been removed." msgstr "%s вилучено." -msgid "Switching application language" -msgstr "Зміна мови програми" msgid "Select the language" msgstr "Вибрати мову" @@ -8874,8 +8872,6 @@ msgstr "Ви хочете продовжувати?" msgid "Language selection" msgstr "Вибір мови" -msgid "Switching application language while some presets are modified." -msgstr "Зміна мови програми при зміні деяких профілів." msgid "Asia-Pacific" msgstr "Азіатсько-Тихоокеанський регіон" diff --git a/localization/i18n/vi/OrcaSlicer_vi.po b/localization/i18n/vi/OrcaSlicer_vi.po index a0758d51ad..a80f47cfc1 100644 --- a/localization/i18n/vi/OrcaSlicer_vi.po +++ b/localization/i18n/vi/OrcaSlicer_vi.po @@ -2568,8 +2568,6 @@ msgstr "Có bản cập nhật khả dụng. Hãy mở hộp thoại gói cài msgid "%s has been removed." msgstr "%s đã bị xóa." -msgid "Switching application language" -msgstr "Đang chuyển ngôn ngữ ứng dụng" msgid "Select the language" msgstr "Chọn ngôn ngữ" @@ -9309,8 +9307,6 @@ msgstr "Bạn có muốn tiếp tục?" msgid "Language selection" msgstr "Chọn ngôn ngữ" -msgid "Switching application language while some presets are modified." -msgstr "Đang chuyển đổi ngôn ngữ ứng dụng trong khi một số preset đã được chỉnh sửa." msgid "Asia-Pacific" msgstr "Châu Á-Thái Bình Dương" diff --git a/localization/i18n/zh_CN/OrcaSlicer_zh_CN.po b/localization/i18n/zh_CN/OrcaSlicer_zh_CN.po index 7c474031c9..faa3419ae5 100644 --- a/localization/i18n/zh_CN/OrcaSlicer_zh_CN.po +++ b/localization/i18n/zh_CN/OrcaSlicer_zh_CN.po @@ -2361,8 +2361,6 @@ msgstr "有更新可用。打开预设包对话框进行更新。" msgid "%s has been removed." msgstr "%s 已被移除。" -msgid "Switching application language" -msgstr "切换应用程序语言" msgid "Select the language" msgstr "选择语言" @@ -8587,8 +8585,6 @@ msgstr "是否继续?" msgid "Language selection" msgstr "语言选择" -msgid "Switching application language while some presets are modified." -msgstr "在切换应用语言之前发现某些参数预设有更改。" msgid "Asia-Pacific" msgstr "亚太" diff --git a/localization/i18n/zh_TW/OrcaSlicer_zh_TW.po b/localization/i18n/zh_TW/OrcaSlicer_zh_TW.po index 83b22ca029..8f17cfdc5d 100644 --- a/localization/i18n/zh_TW/OrcaSlicer_zh_TW.po +++ b/localization/i18n/zh_TW/OrcaSlicer_zh_TW.po @@ -2425,8 +2425,6 @@ msgstr "有可用的更新。請開啟預設組合對話框進行更新。" msgid "%s has been removed." msgstr "%s 已移除。" -msgid "Switching application language" -msgstr "切換應用程式語言" msgid "Select the language" msgstr "選擇語言" @@ -8753,8 +8751,6 @@ msgstr "是否繼續?" msgid "Language selection" msgstr "語言選擇" -msgid "Switching application language while some presets are modified." -msgstr "在切換應用程式語言之前發現某些參數預設有更改。" msgid "Asia-Pacific" msgstr "亞太" diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 6d08f052da..df2d1fccc0 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -7769,21 +7769,6 @@ void GUI_App::stop_http_server() m_http_server.stop(); } -void GUI_App::switch_staff_pick(bool on) -{ - mainframe->m_webview->SendDesignStaffpick(on); -} - -bool GUI_App::switch_language() -{ - if (select_language()) { - recreate_GUI(_L("Switching application language") + dots); - return true; - } else { - return false; - } -} - #ifdef __linux__ static const wxLanguageInfo* linux_get_existing_locale_language(const wxLanguageInfo* language, const wxLanguageInfo* system_language) @@ -7878,72 +7863,6 @@ int GUI_App::GetSingleChoiceIndex(const wxString& message, #endif } -// select language from the list of installed languages -bool GUI_App::select_language() -{ - wxArrayString translations = wxTranslations::Get()->GetAvailableTranslations(SLIC3R_APP_KEY); - std::vector language_infos; - language_infos.emplace_back(wxLocale::GetLanguageInfo(wxLANGUAGE_ENGLISH)); - for (size_t i = 0; i < translations.GetCount(); ++ i) { - const wxLanguageInfo *langinfo = wxLocale::FindLanguageInfo(translations[i]); - if (langinfo != nullptr) - language_infos.emplace_back(langinfo); - } - sort_remove_duplicates(language_infos); - std::sort(language_infos.begin(), language_infos.end(), [](const wxLanguageInfo* l, const wxLanguageInfo* r) { return l->Description < r->Description; }); - - wxArrayString names; - names.Alloc(language_infos.size()); - - // Some valid language should be selected since the application start up. - const wxString active_language_code = current_language_code(); - const wxLanguageInfo* active_language_info = wxLocale::FindLanguageInfo(active_language_code); - const wxLanguage current_language = active_language_info != nullptr ? wxLanguage(active_language_info->Language) : wxLanguage(m_wxLocale->GetLanguage()); - const wxString active_lang_prefix = active_language_code.BeforeFirst('_'); - int init_selection = -1; - int init_selection_alt = -1; - int init_selection_default = -1; - for (size_t i = 0; i < language_infos.size(); ++ i) { - if (wxLanguage(language_infos[i]->Language) == current_language) - // The dictionary matches the active language and country. - init_selection = i; - else if ((language_infos[i]->CanonicalName.BeforeFirst('_') == active_lang_prefix) || - // if the active language is Slovak, mark the Czech language as active. - (language_infos[i]->CanonicalName.BeforeFirst('_') == "cs" && active_lang_prefix == "sk")) - // The dictionary matches the active language, it does not necessarily match the country. - init_selection_alt = i; - if (language_infos[i]->CanonicalName.BeforeFirst('_') == "en") - // This will be the default selection if the active language does not match any dictionary. - init_selection_default = i; - names.Add(language_infos[i]->Description); - } - if (init_selection == -1) - // This is the dictionary matching the active language. - init_selection = init_selection_alt; - if (init_selection != -1) - // This is the language to highlight in the choice dialog initially. - init_selection_default = init_selection; - - const long index = GetSingleChoiceIndex(_L("Select the language"), _L("Language"), names, init_selection_default); - // Try to load a new language. - if (index != -1 && (init_selection == -1 || init_selection != index)) { - const wxLanguageInfo *new_language_info = language_infos[index]; - if (this->load_language(new_language_info->CanonicalName, false)) { - // Save language at application config. - // Which language to save as the selected dictionary language? - // 1) Hopefully the language set to wxTranslations by this->load_language(), but that API is weird and we don't want to rely on its - // stability in the future: - // wxTranslations::Get()->GetBestTranslation(SLIC3R_APP_KEY, wxLANGUAGE_ENGLISH); - // 2) Current locale language may not match the dictionary name, see GH issue #3901 - // m_wxLocale->GetCanonicalName() - // 3) new_language_info->CanonicalName is a safe bet. It points to a valid dictionary name. - app_config->set("language", new_language_info->CanonicalName.ToUTF8().data()); - return true; - } - } - - return false; -} // Load gettext translation files and activate them at the start of the application, // based on the "language" key stored in the application config. @@ -8330,146 +8249,6 @@ void GUI_App::show_ip_address_enter_dialog_handler(wxCommandEvent& evt) show_modal_ip_address_enter_dialog(mode == -1?false:true, title); } -//void GUI_App::add_config_menu(wxMenuBar *menu) -//void GUI_App::add_config_menu(wxMenu *menu) -//{ -// auto local_menu = new wxMenu(); -// wxWindowID config_id_base = wxWindow::NewControlId(int(ConfigMenuCnt)); -// -// const auto config_wizard_name = _(ConfigWizard::name(true)); -// const auto config_wizard_tooltip = from_u8((boost::format(_utf8(L("Open %s"))) % config_wizard_name).str()); -// // Cmd+, is standard on OS X - what about other operating systems? -// if (is_editor()) { -// local_menu->Append(config_id_base + ConfigMenuWizard, config_wizard_name + dots, config_wizard_tooltip); -// local_menu->Append(config_id_base + ConfigMenuUpdate, _L("Check for Configuration Updates"), _L("Check for configuration updates")); -// local_menu->AppendSeparator(); -// } -// local_menu->Append(config_id_base + ConfigMenuPreferences, _L("Preferences") + dots + -//#ifdef __APPLE__ -// "\tCtrl+,", -//#else -// "\tCtrl+P", -//#endif -// _L("Application preferences")); -// wxMenu* mode_menu = nullptr; -// if (is_editor()) { -// local_menu->AppendSeparator(); -// mode_menu = new wxMenu(); -// mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeSimple, _L("Simple"), _L("Simple Mode")); -// mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeAdvanced, _L("Advanced"), _L("Advanced Mode")); -// Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { if (get_mode() == comSimple) evt.Check(true); }, config_id_base + ConfigMenuModeSimple); -// Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { if (get_mode() == comAdvanced) evt.Check(true); }, config_id_base + ConfigMenuModeAdvanced); -// -// local_menu->AppendSubMenu(mode_menu, _L("Mode"), wxString::Format(_L("%s Mode"), SLIC3R_APP_NAME)); -// } -// local_menu->AppendSeparator(); -// local_menu->Append(config_id_base + ConfigMenuLanguage, _L("Language")); -// if (is_editor()) { -// local_menu->AppendSeparator(); -// } -// -// local_menu->Bind(wxEVT_MENU, [this, config_id_base](wxEvent &event) { -// switch (event.GetId() - config_id_base) { -// case ConfigMenuWizard: -// run_wizard(ConfigWizard::RR_USER); -// break; -// case ConfigMenuUpdate: -// check_updates(true); -// break; -//#ifdef __linux__ -// case ConfigMenuDesktopIntegration: -// show_desktop_integration_dialog(); -// break; -//#endif -// case ConfigMenuSnapshots: -// //BBS do not support task snapshot -// break; -// case ConfigMenuPreferences: -// { -// //BBS GUI refactor: remove unuse layout logic -// //bool app_layout_changed = false; -// { -// // the dialog needs to be destroyed before the call to recreate_GUI() -// // or sometimes the application crashes into wxDialogBase() destructor -// // so we put it into an inner scope -// PreferencesDialog dlg(mainframe); -// dlg.ShowModal(); -// //BBS GUI refactor: remove unuse layout logic -// //app_layout_changed = dlg.settings_layout_changed(); -// if (dlg.seq_top_layer_only_changed()) -// this->plater_->refresh_print(); -// -// if (dlg.recreate_GUI()) { -// recreate_GUI(_L("Restart application") + dots); -// return; -// } -//#ifdef _WIN32 -// if (is_editor()) { -// if (app_config->get("associate_3mf") == "true") -// associate_3mf_files(); -// if (app_config->get("associate_stl") == "true") -// associate_stl_files(); -// } -// else { -// if (app_config->get("associate_gcode") == "true") -// associate_gcode_files(); -// } -//#endif // _WIN32 -// } -// //BBS GUI refactor: remove unuse layout logic -// /*if (app_layout_changed) { -// // hide full main_sizer for mainFrame -// mainframe->GetSizer()->Show(false); -// mainframe->update_layout(); -// mainframe->select_tab(size_t(0)); -// }*/ -// break; -// } -// case ConfigMenuLanguage: -// { -// /* Before change application language, let's check unsaved changes on 3D-Scene -// * and draw user's attention to the application restarting after a language change -// */ -// { -// // the dialog needs to be destroyed before the call to switch_language() -// // or sometimes the application crashes into wxDialogBase() destructor -// // so we put it into an inner scope -// wxString title = is_editor() ? wxString(SLIC3R_APP_NAME) : wxString(GCODEVIEWER_APP_NAME); -// title += " - " + _L("Choose language"); -// //wxMessageDialog dialog(nullptr, -// MessageDialog dialog(nullptr, -// _L("Switching the language requires application restart.\n") + "\n\n" + -// _L("Do you want to continue?"), -// title, -// wxICON_QUESTION | wxOK | wxCANCEL); -// if (dialog.ShowModal() == wxID_CANCEL) -// return; -// } -// -// switch_language(); -// break; -// } -// case ConfigMenuFlashFirmware: -// //BBS FirmwareDialog::run(mainframe); -// break; -// default: -// break; -// } -// }); -// -// using std::placeholders::_1; -// -// if (mode_menu != nullptr) { -// auto modfn = [this](int mode, wxCommandEvent&) { if (get_mode() != mode) save_mode(mode); }; -// mode_menu->Bind(wxEVT_MENU, std::bind(modfn, comSimple, _1), config_id_base + ConfigMenuModeSimple); -// mode_menu->Bind(wxEVT_MENU, std::bind(modfn, comAdvanced, _1), config_id_base + ConfigMenuModeAdvanced); -// } -// -// // BBS -// //menu->Append(local_menu, _L("Configuration")); -// menu->AppendSubMenu(local_menu, _L("Configuration")); -//} - void GUI_App::open_presetbundledialog(size_t open_on_tab, const std::string& highlight_option) { bool app_layout_changed = false; diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 8bf32df64c..2569e10271 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -569,7 +569,6 @@ public: void start_http_server(const std::string& provider = ORCA_CLOUD_PROVIDER); void start_http_server(int port, const std::string& provider = ORCA_CLOUD_PROVIDER); void stop_http_server(); - void switch_staff_pick(bool on); void on_show_check_privacy_dlg(int online_login = 0, const std::string& provider = ORCA_CLOUD_PROVIDER); void show_check_privacy_dlg(wxCommandEvent& evt); @@ -583,7 +582,6 @@ public: void persist_window_geometry(wxTopLevelWindow *window, bool default_maximized = false); void update_ui_from_settings(); - bool switch_language(); bool load_language(wxString language, bool initial); Tab* get_tab(Preset::Type type); @@ -801,7 +799,6 @@ private: bool window_pos_restore(wxTopLevelWindow* window, const std::string &name, bool default_maximized = false); void window_pos_sanitize(wxTopLevelWindow* window); void window_pos_center(wxTopLevelWindow *window); - bool select_language(); // Dynamic printer agent selection - internal helpers for switch_printer_agent // and the plugin load/unload callbacks (init_plugin_gui_wiring). diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index c734804c22..5f36323d6e 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -3275,98 +3275,9 @@ void MainFrame::init_menubar_as_editor() auto preference_item = new wxMenuItem(parent_menu, ConfigMenuPreferences + config_id_base, _L("Preferences") + "\t" + ctrl + "P", ""); #endif - //auto printer_item = new wxMenuItem(parent_menu, ConfigMenuPrinter + config_id_base, _L("Printer"), ""); - //auto language_item = new wxMenuItem(parent_menu, ConfigMenuLanguage + config_id_base, _L("Switch Language"), ""); -// parent_menu->Bind(wxEVT_MENU, [this, config_id_base](wxEvent& event) { -// switch (event.GetId() - config_id_base) { -// //case ConfigMenuLanguage: -// //{ -// // /* Before change application language, let's check unsaved changes on 3D-Scene -// // * and draw user's attention to the application restarting after a language change -// // */ -// // { -// // // the dialog needs to be destroyed before the call to switch_language() -// // // or sometimes the application crashes into wxDialogBase() destructor -// // // so we put it into an inner scope -// // wxString title = _L("Language selection"); -// // wxMessageDialog dialog(nullptr, -// // _L("Switching the language requires application restart.\n") + "\n\n" + -// // _L("Do you want to continue?"), -// // title, -// // wxICON_QUESTION | wxOK | wxCANCEL); -// // if (dialog.ShowModal() == wxID_CANCEL) -// // return; -// // } -// -// // wxGetApp().switch_language(); -// // break; -// //} -// //case ConfigMenuWizard: -// //{ -// // wxGetApp().run_wizard(ConfigWizard::RR_USER); -// // break; -// //} -// case ConfigMenuPrinter: -// { -// wxGetApp().params_dialog()->Popup(); -// wxGetApp().get_tab(Preset::TYPE_PRINTER)->restore_last_select_item(); -// break; -// } -// case ConfigMenuPreferences: -// { -// CallAfter([this] { -// PreferencesDialog dlg(this); -// dlg.ShowModal(); -//#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER -// if (dlg.seq_top_layer_only_changed() || dlg.seq_seq_top_gcode_indices_changed()) -//#else -// if (dlg.seq_top_layer_only_changed()) -//#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER -// plater()->refresh_print(); -//#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN -//#ifdef _WIN32 -// /* -// if (wxGetApp().app_config()->get("associate_3mf") == "true") -// wxGetApp().associate_3mf_files(); -// if (wxGetApp().app_config()->get("associate_stl") == "true") -// wxGetApp().associate_stl_files(); -// /*if (wxGetApp().app_config()->get("associate_step") == "true") -// wxGetApp().associate_step_files();*/ -//#endif // _WIN32 -//#endif -// }); -// break; -// } -// default: -// break; -// } -// }); #ifdef __APPLE__ wxString about_title = wxString::Format(_L("&About %s"), SLIC3R_APP_FULL_NAME); - //auto about_item = new wxMenuItem(parent_menu, OrcaSlicerMenuAbout + bambu_studio_id_base, about_title, ""); - //parent_menu->Bind(wxEVT_MENU, [this, bambu_studio_id_base](wxEvent& event) { - // switch (event.GetId() - bambu_studio_id_base) { - // case OrcaSlicerMenuAbout: - // Slic3r::GUI::about(); - // break; - // case OrcaSlicerMenuPreferences: - // CallAfter([this] { - // PreferencesDialog dlg(this); - // dlg.ShowModal(); - //#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER - // if (dlg.seq_top_layer_only_changed() || dlg.seq_seq_top_gcode_indices_changed()) - //#else - // if (dlg.seq_top_layer_only_changed()) - //#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER - // plater()->refresh_print(); - // }); - // break; - // default: - // break; - // } - //}); - //parent_menu->Insert(0, about_item); append_menu_item( parent_menu, wxID_ANY, _L(about_title), "", [](wxCommandEvent &) { Slic3r::GUI::about();}, diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 08483f3100..7d80147efa 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -507,26 +507,14 @@ wxBoxSizer *PreferencesDialog::create_item_language_combobox(wxString title, wxS } } - - // the dialog needs to be destroyed before the call to switch_language() - // or sometimes the application crashes into wxDialogBase() destructor - // so we put it into an inner scope - MessageDialog msg_wingow(nullptr, _L("Switching languages requires the application to restart.\n") + "\n" + _L("Do you want to continue?"), - L("Language selection"), wxICON_QUESTION | wxOK | wxCANCEL); - if (msg_wingow.ShowModal() == wxID_CANCEL) { + MessageDialog msg_window(nullptr, _L("Switching languages requires the application to restart.\n") + "\n" + _L("Do you want to continue?"), + _L("Language selection"), wxICON_QUESTION | wxOK | wxCANCEL); + if (msg_window.ShowModal() == wxID_CANCEL) { combobox->SetSelection(m_current_language_selected); return; } } - auto check = [](bool yes_or_no) { - // if (yes_or_no) - // return true; - int act_btns = ActionButtons::SAVE; - return wxGetApp().check_and_keep_current_preset_changes(_L("Switching application language"), - _L("Switching application language while some presets are modified."), act_btns); - }; - m_current_language_selected = combobox->GetSelection(); if (m_current_language_selected >= 0 && m_current_language_selected < vlist.size()) { m_pending_language = vlist[m_current_language_selected]->CanonicalName.ToUTF8().data(); @@ -1031,11 +1019,6 @@ wxBoxSizer *PreferencesDialog::create_item_checkbox(wxString title, wxString too app_config->set_bool(param, checkbox->GetValue()); app_config->save(); - // if (param == "staff_pick_switch") { - // bool pbool = app_config->get("staff_pick_switch") == "true"; - // wxGetApp().switch_staff_pick(pbool); - // } - if (param == "sync_user_preset") { bool sync = app_config->get("sync_user_preset") == "true" ? true : false; if (sync) { diff --git a/src/slic3r/GUI/Widgets/FanControl.cpp b/src/slic3r/GUI/Widgets/FanControl.cpp index f10553814e..7efdfabf07 100644 --- a/src/slic3r/GUI/Widgets/FanControl.cpp +++ b/src/slic3r/GUI/Widgets/FanControl.cpp @@ -995,7 +995,7 @@ void FanControlPopupNew::init_names(MachineObject* obj) { radio_btn_name[AIR_DUCT::AIR_DUCT_HEATING_INTERNAL_FILT] = _L("Heating"); radio_btn_name[AIR_DUCT::AIR_DUCT_EXHAUST] = _L("Exhaust"); radio_btn_name[AIR_DUCT::AIR_DUCT_FULL_COOLING] = _L("Full Cooling"); - radio_btn_name[AIR_DUCT::AIR_DUCT_INIT] = L("Init"); + radio_btn_name[AIR_DUCT::AIR_DUCT_INIT] = _L("Init"); air_door_func_name[AIR_DOOR::AIR_DOOR_FUNC_CHAMBER] = _L("Chamber"); air_door_func_name[AIR_DOOR::AIR_DOOR_FUNC_INNERLOOP] = _L("Innerloop"); From d127db4d9927021ade3bf9c122fd55aa6c01dac7 Mon Sep 17 00:00:00 2001 From: Kris Austin Date: Thu, 10 Sep 2026 16:15:39 -0500 Subject: [PATCH 6/7] build: clear 5 warning categories across 19 sites (#15628) --- src/libslic3r/Emboss.cpp | 10 +++++----- src/libslic3r/Fill/FillAdaptive.cpp | 4 ++-- src/libslic3r/GCode/ToolOrderUtils.cpp | 2 +- src/libslic3r/Line.cpp | 4 ++-- src/libslic3r/PrintObject.cpp | 4 +++- src/libslic3r/SLAPrint.cpp | 4 +++- src/slic3r/GUI/DeviceCore/DevMapping.cpp | 4 +++- src/slic3r/GUI/MeshUtils.cpp | 4 ++-- src/slic3r/GUI/Printer/PrinterFileSystem.cpp | 2 +- src/slic3r/Utils/BBLNetworkPlugin.cpp | 2 +- src/slic3r/Utils/BBLPrinterAgent.cpp | 21 +++++++++++++++----- src/slic3r/Utils/PresetUpdater.cpp | 2 +- 12 files changed, 40 insertions(+), 23 deletions(-) diff --git a/src/libslic3r/Emboss.cpp b/src/libslic3r/Emboss.cpp index ef144b48d3..34d9a93590 100644 --- a/src/libslic3r/Emboss.cpp +++ b/src/libslic3r/Emboss.cpp @@ -968,10 +968,10 @@ EmbossStyles Emboss::get_font_list_by_register() { } // TODO: Fix global function -bool CALLBACK EnumFamCallBack(LPLOGFONT lplf, - LPNEWTEXTMETRIC lpntm, - DWORD FontType, - LPVOID aFontList) +int CALLBACK EnumFamCallBack(const LOGFONT *lplf, + const TEXTMETRIC *lpntm, + DWORD FontType, + LPARAM aFontList) { std::vector *fontList = (std::vector *) (aFontList); @@ -988,7 +988,7 @@ EmbossStyles Emboss::get_font_list_by_enumeration() { HDC hDC = GetDC(NULL); std::vector font_names; - EnumFontFamilies(hDC, (LPCTSTR) NULL, (FONTENUMPROC) EnumFamCallBack, + EnumFontFamilies(hDC, (LPCTSTR) NULL, EnumFamCallBack, (LPARAM) &font_names); EmbossStyles font_list; diff --git a/src/libslic3r/Fill/FillAdaptive.cpp b/src/libslic3r/Fill/FillAdaptive.cpp index 344bb529f0..dbaa1f2ac9 100644 --- a/src/libslic3r/Fill/FillAdaptive.cpp +++ b/src/libslic3r/Fill/FillAdaptive.cpp @@ -1395,8 +1395,8 @@ void Filler::_fill_surface_single( } #endif /* ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT */ - const auto hook_length = coordf_t(std::min(std::numeric_limits::max(), scale_(params.anchor_length))); - const auto hook_length_max = coordf_t(std::min(std::numeric_limits::max(), scale_(params.anchor_length_max))); + const auto hook_length = coordf_t(scale_(params.anchor_length)); + const auto hook_length_max = coordf_t(scale_(params.anchor_length_max)); Polylines all_polylines_with_hooks = all_polylines.size() > 1 ? connect_lines_using_hooks(std::move(all_polylines), expolygon, this->spacing, hook_length, hook_length_max) : std::move(all_polylines); diff --git a/src/libslic3r/GCode/ToolOrderUtils.cpp b/src/libslic3r/GCode/ToolOrderUtils.cpp index 4e2934d967..4a67477d24 100644 --- a/src/libslic3r/GCode/ToolOrderUtils.cpp +++ b/src/libslic3r/GCode/ToolOrderUtils.cpp @@ -910,7 +910,7 @@ namespace Slic3r unsigned int iterations = (1 << all_extruders.size()); unsigned int final_state = iterations - 1; - std::vector>cache(iterations, std::vector(all_extruders.size(), 0x7fffffff)); + std::vector>cache(iterations, std::vector(all_extruders.size(), std::numeric_limits::max())); std::vector>prev(iterations, std::vector(all_extruders.size(), -1)); cache[1][0] = 0.; for (unsigned int state = 0; state < iterations; ++state) { diff --git a/src/libslic3r/Line.cpp b/src/libslic3r/Line.cpp index c74df3aa59..94453e18f7 100644 --- a/src/libslic3r/Line.cpp +++ b/src/libslic3r/Line.cpp @@ -30,8 +30,8 @@ bool Line::intersection_infinite(const Line &other, Point* point) const return false; double t1 = cross2(v12, v2) / denom; Vec2d result = (a1 + t1 * v1); - if (result.x() > std::numeric_limits::max() || result.x() < std::numeric_limits::lowest() || - result.y() > std::numeric_limits::max() || result.y() < std::numeric_limits::lowest()) { + if (result.x() > double(std::numeric_limits::max()) || result.x() < double(std::numeric_limits::lowest()) || + result.y() > double(std::numeric_limits::max()) || result.y() < double(std::numeric_limits::lowest())) { // Intersection has at least one of the coordinates much bigger (or smaller) than coord_t maximum value (or minimum). // So it can not be stored into the Point without integer overflows. That could mean that input lines are parallel or near parallel. return false; diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index e147356ea6..720a2cdade 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1635,7 +1635,9 @@ bool PrintObject::invalidate_step(PrintObjectStep step) bool PrintObject::invalidate_all_steps() { // First call the "invalidate" functions, which may cancel background processing. - bool result = Inherited::invalidate_all_steps() | m_print->invalidate_all_steps(); + const bool inherited_invalidated = Inherited::invalidate_all_steps(); + const bool print_invalidated = m_print->invalidate_all_steps(); + bool result = inherited_invalidated || print_invalidated; // Then reset some of the depending values. m_slicing_params.valid = false; return result; diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index cdefd3e10e..eb37ca578c 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1007,7 +1007,9 @@ bool SLAPrintObject::invalidate_step(SLAPrintObjectStep step) bool SLAPrintObject::invalidate_all_steps() { - return Inherited::invalidate_all_steps() | m_print->invalidate_all_steps(); + const bool inherited_invalidated = Inherited::invalidate_all_steps(); + const bool print_invalidated = m_print->invalidate_all_steps(); + return inherited_invalidated || print_invalidated; } double SLAPrintObject::get_elevation() const { diff --git a/src/slic3r/GUI/DeviceCore/DevMapping.cpp b/src/slic3r/GUI/DeviceCore/DevMapping.cpp index 165492c9f6..0040bb05f2 100644 --- a/src/slic3r/GUI/DeviceCore/DevMapping.cpp +++ b/src/slic3r/GUI/DeviceCore/DevMapping.cpp @@ -1,3 +1,5 @@ +#include + #include #include "DevMapping.h" #include "DevFilaSystem.h" @@ -270,7 +272,7 @@ namespace Slic3r std::set picked_tar; for (int k = 0; k < distance_map.size(); k++) { - float min_val = INT_MAX; + float min_val = std::numeric_limits::max(); int picked_src_idx = -1; int picked_tar_idx = -1; for (int i = 0; i < distance_map.size(); i++) diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index bc6c60a360..173c5d2f13 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -297,7 +297,7 @@ void MeshClipper::recalculate_triangles() // it so it lies on our line. This will be the figure to subtract // from the cut. The coordinates must not overflow after the transform, // make the rectangle a bit smaller. - const coord_t size = (std::numeric_limits::max()/2 - scale_(std::max(std::abs(e * a), std::abs(e * b)))) / 4; + const coord_t size = (double(std::numeric_limits::max()/2) - scale_(std::max(std::abs(e * a), std::abs(e * b)))) / 4; Polygons ep {Polygon({Point(-size, 0), Point(size, 0), Point(size, 2*size), Point(-size, 2*size)})}; ep.front().rotate(angle); ep.front().translate(scale_(-e * a), scale_(-e * b)); @@ -352,7 +352,7 @@ void MeshClipper::recalculate_triangles() // To prevent overflow after scaling, downscale the input if needed: double extra_scale = 1.; - coord_t limit = coord_t(std::min(std::numeric_limits::max() / (2. * std::max(1., scale_x)), std::numeric_limits::max() / (2. * std::max(1., scale_y)))); + coord_t limit = coord_t(std::min(double(std::numeric_limits::max()) / (2. * std::max(1., scale_x)), double(std::numeric_limits::max()) / (2. * std::max(1., scale_y)))); coord_t max_coord = 0; for (const Point& pt : exp.contour) max_coord = std::max(max_coord, std::max(std::abs(pt.x()), std::abs(pt.y()))); diff --git a/src/slic3r/GUI/Printer/PrinterFileSystem.cpp b/src/slic3r/GUI/Printer/PrinterFileSystem.cpp index 8ec6909c8c..9aa35e2cff 100644 --- a/src/slic3r/GUI/Printer/PrinterFileSystem.cpp +++ b/src/slic3r/GUI/Printer/PrinterFileSystem.cpp @@ -1803,7 +1803,7 @@ static void* get_function(const char* name) return function; #if defined(_MSC_VER) || defined(_WIN32) - function = GetProcAddress(module, name); + function = reinterpret_cast(GetProcAddress(module, name)); #else function = dlsym(module, name); #endif diff --git a/src/slic3r/Utils/BBLNetworkPlugin.cpp b/src/slic3r/Utils/BBLNetworkPlugin.cpp index 607e7d16d1..d795abf354 100644 --- a/src/slic3r/Utils/BBLNetworkPlugin.cpp +++ b/src/slic3r/Utils/BBLNetworkPlugin.cpp @@ -349,7 +349,7 @@ void* BBLNetworkPlugin::get_function(const char* name) return function; #if defined(_MSC_VER) || defined(_WIN32) - function = GetProcAddress(m_networking_module, name); + function = reinterpret_cast(GetProcAddress(m_networking_module, name)); #else function = dlsym(m_networking_module, name); #endif diff --git a/src/slic3r/Utils/BBLPrinterAgent.cpp b/src/slic3r/Utils/BBLPrinterAgent.cpp index 5e73edf84c..0c6225cc55 100644 --- a/src/slic3r/Utils/BBLPrinterAgent.cpp +++ b/src/slic3r/Utils/BBLPrinterAgent.cpp @@ -8,6 +8,7 @@ #include using json = nlohmann::json; +#include #include namespace Slic3r { @@ -90,6 +91,16 @@ OnMessageFn to_orca_messages(OnMessageFn fn) return [fn = std::move(fn)](std::string dev_id, std::string msg) { fn(std::move(dev_id), BBLPrinterAgent::to_orca_payload(std::move(msg))); }; } +// Retypes a plug-in entry point for an older plug-in generation. The detour through the +// generic function pointer marks the signature change as deliberate, which a direct cast +// between two signatures does not. +template +To as_abi(From fn) +{ + static_assert(std::is_function_v>, "as_abi retypes a function pointer"); + return reinterpret_cast(reinterpret_cast(fn)); +} + } // namespace std::string BBLPrinterAgent::to_orca_filament_id(const std::string& printer_filament_id) const @@ -141,7 +152,7 @@ int BBLPrinterAgent::send_message(std::string dev_id, std::string json_str, int // series through the legacy form would silently drop MessageFlag sign/encrypt. switch (plugin.network_abi()) { case NetworkAbi::Legacy: { - auto legacy_func = reinterpret_cast(func); + auto legacy_func = as_abi(func); return legacy_func(agent, std::move(dev_id), std::move(json_str), qos); } case NetworkAbi::V0203: @@ -185,7 +196,7 @@ int BBLPrinterAgent::send_message_to_printer(std::string dev_id, std::string jso if (func && agent) { switch (plugin.network_abi()) { case NetworkAbi::Legacy: { - auto legacy_func = reinterpret_cast(func); + auto legacy_func = as_abi(func); return legacy_func(agent, std::move(dev_id), std::move(json_str), qos); } case NetworkAbi::V0203: @@ -275,7 +286,7 @@ int BBLPrinterAgent::bind(std::string dev_ip, std::string dev_id, std::string de switch (plugin.network_abi()) { case NetworkAbi::Legacy: case NetworkAbi::V0203: { - auto older_func = reinterpret_cast(func); + auto older_func = as_abi(func); return older_func(agent, dev_ip, dev_id, sec_link, timezone, improved, update_fn); } case NetworkAbi::Current: @@ -436,9 +447,9 @@ int dispatch_start(CurrentFn func, PrintParams& params, const CallbackFns&... ca params.ams_mapping_info = BBLPrinterAgent::from_orca_payload(std::move(params.ams_mapping_info)); switch (plugin.network_abi()) { case NetworkAbi::Legacy: - return reinterpret_cast(func)(agent, BBLNetworkPlugin::as_legacy(params), callbacks...); + return as_abi(func)(agent, BBLNetworkPlugin::as_legacy(params), callbacks...); case NetworkAbi::V0203: - return reinterpret_cast(func)(agent, BBLNetworkPlugin::as_0203(params), callbacks...); + return as_abi(func)(agent, BBLNetworkPlugin::as_0203(params), callbacks...); case NetworkAbi::Current: return func(agent, std::move(params), callbacks...); default: diff --git a/src/slic3r/Utils/PresetUpdater.cpp b/src/slic3r/Utils/PresetUpdater.cpp index 23957f6500..032f9dbf7a 100644 --- a/src/slic3r/Utils/PresetUpdater.cpp +++ b/src/slic3r/Utils/PresetUpdater.cpp @@ -1620,7 +1620,7 @@ void PresetUpdater::priv::check_new_vendors(const std::set& system_ Http::get(download_url_str) .timeout_connect(5) .on_progress(check_cancel) - .on_error([&vendor_id, &retry_count, max_retries](std::string body, std::string error, unsigned http_status) { + .on_error([&vendor_id, &retry_count](std::string body, std::string error, unsigned http_status) { BOOST_LOG_TRIVIAL(warning) << "[Orca Updater] download failed for new vendor " << vendor_id << " (attempt " << retry_count << "/" << max_retries << "): " << error; }) From a49b8927088cde075c8ccfc7dcaf1bedc3b52af9 Mon Sep 17 00:00:00 2001 From: Kiss Lorand <50251547+kisslorand@users.noreply.github.com> Date: Fri, 11 Sep 2026 00:23:31 +0300 Subject: [PATCH 7/7] Fix bridge flow invalidation for zero-gap supports (#15626) --- src/libslic3r/PrintObject.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 720a2cdade..54378b3b16 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1511,13 +1511,9 @@ bool PrintObject::invalidate_state_by_config_options( steps.emplace_back(posPerimeters); steps.emplace_back(posSupportMaterial); } else if (opt_key == "bridge_flow" || opt_key == "internal_bridge_flow") { - if (m_config.support_top_z_distance > 0.) { - // Only invalidate due to bridging if bridging is enabled. - // If later "support_top_z_distance" is modified, the complete PrintObject is invalidated anyway. - steps.emplace_back(posPerimeters); - steps.emplace_back(posInfill); - steps.emplace_back(posSupportMaterial); - } + steps.emplace_back(posPerimeters); + steps.emplace_back(posInfill); + steps.emplace_back(posSupportMaterial); } else if ( opt_key == "wall_generator" || opt_key == "wall_transition_length"