From fc5aec425e538b584637a70f85743227cf2fac94 Mon Sep 17 00:00:00 2001 From: Kiss Lorand <50251547+kisslorand@users.noreply.github.com> Date: Sun, 26 Jul 2026 00:08:12 +0300 Subject: [PATCH 1/8] Fix stale plate membership during plate grid rearrangement (#14850) --- src/slic3r/GUI/PartPlate.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/slic3r/GUI/PartPlate.cpp b/src/slic3r/GUI/PartPlate.cpp index a1211f4692..4b52592f2d 100644 --- a/src/slic3r/GUI/PartPlate.cpp +++ b/src/slic3r/GUI/PartPlate.cpp @@ -4552,6 +4552,9 @@ int PartPlateList::create_plate(bool adjust_position) return -1; int cols = compute_colum_count(new_index + 1); int old_cols = compute_colum_count(new_index); + // Orca: Rebuild plate membership before moving instances during a grid reflow. + if (adjust_position && old_cols != cols) + reload_all_objects(); origin = compute_origin(new_index, cols); plate = new PartPlate(this, origin, m_plate_width, m_plate_depth, m_plate_height, m_plater, m_model, true, printer_technology); @@ -5041,6 +5044,9 @@ int PartPlateList::move_plate_to_index(int old_index, int new_index) return -1; } + // Orca: Rebuild plate membership before moving the plates. + reload_all_objects(); + if (old_index < new_index) { delta = 1; From 68ce4da19fa881f6d49d39b22c910c564ca7318c Mon Sep 17 00:00:00 2001 From: Kiss Lorand <50251547+kisslorand@users.noreply.github.com> Date: Sun, 26 Jul 2026 00:38:21 +0300 Subject: [PATCH 2/8] Fix overhang fan speed bugs (#14788) --- src/libslic3r/GCode.cpp | 21 ++++++++++++++++++--- src/libslic3r/GCode/CoolingBuffer.cpp | 9 +++++++-- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index a08554654a..f45d807d43 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -5681,10 +5681,17 @@ LayerResult GCode::process_layer( for (const auto &layer_to_print : layers) { if (layer_to_print.object_layer) { const auto& regions = layer_to_print.object_layer->regions(); - const bool enable_overhang_speed = std::any_of(regions.begin(), regions.end(), [this](const LayerRegion* r) { + const bool has_extrusions = std::any_of(regions.begin(), regions.end(), [](const LayerRegion* r) { + return r->has_extrusions(); + }); + const bool enable_overhang_speed = std::any_of(regions.begin(), regions.end(), [this](const LayerRegion* r) { return r->has_extrusions() && r->region().config().enable_overhang_speed.get_at(get_nozzle_config_index(m_writer.filament()->id())); }); - if (enable_overhang_speed) { + const bool enable_overhang_fan = m_enable_cooling_markers && has_extrusions && + std::any_of(m_config.enable_overhang_bridge_fan.values.begin(), + m_config.enable_overhang_bridge_fan.values.end(), + [](unsigned char value) { return value != 0; }); + if (enable_overhang_speed || enable_overhang_fan) { m_extrusion_quality_estimator.prepare_for_new_layer(layer_to_print.original_object, layer_to_print.object_layer); } @@ -7523,7 +7530,10 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, bool variable_speed = false; std::vector new_points {}; - if (NOZZLE_CONFIG(enable_overhang_speed) && !this->on_first_layer() && !object_layer_over_raft() && + const bool need_overhang_detection = NOZZLE_CONFIG(enable_overhang_speed) || + (FILAMENT_CONFIG(enable_overhang_bridge_fan) && m_enable_cooling_markers); + + if (need_overhang_detection && !this->on_first_layer() && !object_layer_over_raft() && (is_bridge(path.role()) || is_perimeter(path.role()))) { bool is_external = is_external_perimeter(path.role()); double ref_speed = is_external ? NOZZLE_CONFIG(outer_wall_speed) : NOZZLE_CONFIG(inner_wall_speed); @@ -7582,6 +7592,11 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, } variable_speed = std::any_of(new_points.begin(), new_points.end(), [speed](const ProcessedPoint &p) { return fabs(double(p.speed) - speed) > 1; }); // Ignore small speed variations (under 1mm/sec) + if (!NOZZLE_CONFIG(enable_overhang_speed) && FILAMENT_CONFIG(enable_overhang_bridge_fan) && m_enable_cooling_markers) { + for (ProcessedPoint &point : new_points) + point.speed = speed; + variable_speed = new_points.size() > 1; + } } double F = speed * 60; // convert mm/sec to mm/min diff --git a/src/libslic3r/GCode/CoolingBuffer.cpp b/src/libslic3r/GCode/CoolingBuffer.cpp index c0c79be466..3bca1f8df9 100644 --- a/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/src/libslic3r/GCode/CoolingBuffer.cpp @@ -844,7 +844,10 @@ std::string CoolingBuffer::apply_layer_cooldown( ironing_fan_control = false; // ORCA: Add support for ironing fan speed control ironing_fan_speed = 0; // ORCA: Add support for ironing fan speed control } - if (fan_speed_new != m_fan_speed) { + // A tool change may keep the same configured base fan speed while the physical fan is + // still running at the previous filament's overhang speed. Restore the base speed before + // emitting G-code for the new tool in that case. + if (fan_speed_new != m_fan_speed || (immediately_apply && m_current_fan_speed != fan_speed_new)) { m_fan_speed = fan_speed_new; m_current_fan_speed = fan_speed_new; if (immediately_apply) @@ -1040,8 +1043,10 @@ std::string CoolingBuffer::apply_layer_cooldown( new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_current_fan_speed, part_cooling_fan_min_pwm); fan_speed_change_requests[CoolingLine::TYPE_FORCE_RESUME_FAN] = false; } - else + else { new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_fan_speed, part_cooling_fan_min_pwm); + m_current_fan_speed = m_fan_speed; + } need_set_fan = false; } pos = line_end; From 11fdb472d6193312bc6c78b7703ad2c1222502b7 Mon Sep 17 00:00:00 2001 From: Kris Austin Date: Sat, 25 Jul 2026 16:46:55 -0500 Subject: [PATCH 3/8] fix: hide the extruder switch on single-variant printers (#14930) --- src/slic3r/GUI/Tab.cpp | 4 ++- .../test_config_variant_expansion.cpp | 34 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index fa235f69dd..ef830b7f35 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -8000,7 +8000,9 @@ void Tab::update_extruder_variants(int extruder_id, bool reload) m_actual_nozzle_volumes.resize(extruder_nums, NozzleVolumeType::nvtStandard); for (int i = 0; i < extruder_nums; i++) m_actual_nozzle_volumes[i] = (NozzleVolumeType)nozzle_volumes->values[i]; - if (extruder_nums == 2) { + // Orca: a non-Bambu dual-nozzle printer has two extruders but a single variant column, so + // the nozzle switch and sync button have nothing to act on. Only enable with real variants. + if (extruder_nums == 2 && m_preset_bundle->support_different_extruders()) { auto options = generate_extruder_options(); m_extruder_switch->SetOptions(options); diff --git a/tests/libslic3r/test_config_variant_expansion.cpp b/tests/libslic3r/test_config_variant_expansion.cpp index 59f311eae5..14675fd4dc 100644 --- a/tests/libslic3r/test_config_variant_expansion.cpp +++ b/tests/libslic3r/test_config_variant_expansion.cpp @@ -58,6 +58,40 @@ TEST_CASE("apply_override fills nil entries from the 0-based default index", "[C } } +TEST_CASE("support_different_extruders is true only when the printer defines more than one variant column", "[Config]") +{ + int extruder_count = 0; + + SECTION("a non-Bambu dual-nozzle printer with one variant column reports false") { + DynamicPrintConfig config; + config.option("nozzle_diameter", true)->values = {0.4, 0.4}; + // Both extruders resolve to the same default variant, so there is only one column. + config.option("extruder_variant_list", true)->values = {"Direct Drive Standard", + "Direct Drive Standard"}; + REQUIRE(config.support_different_extruders(extruder_count) == false); + REQUIRE(extruder_count == 2); + } + + SECTION("a Bambu H2D-style printer with distinct variants reports true") { + DynamicPrintConfig config; + config.option("nozzle_diameter", true)->values = {0.4, 0.4}; + config.option("extruder_variant_list", true)->values = { + "Direct Drive Standard,Direct Drive High Flow", + "Direct Drive Standard,Direct Drive High Flow,Direct Drive TPU High Flow"}; + REQUIRE(config.support_different_extruders(extruder_count) == true); + REQUIRE(extruder_count == 2); + } + + SECTION("a many-toolhead printer that never opts into variants reports false") { + // A Snapmaker U1 has four identical toolheads and never defines extruder_variant_list, + // so the config falls back to a single default variant token. + DynamicPrintConfig config; + config.option("nozzle_diameter", true)->values = {0.4, 0.4, 0.4, 0.4}; + REQUIRE(config.support_different_extruders(extruder_count) == false); + REQUIRE(extruder_count == 4); + } +} + TEST_CASE("get_config_index_base resolves (volume type, extruder type, id) to a slot", "[Config]") { const std::vector variant_list = {"Direct Drive Standard", "Direct Drive High Flow", From 63044b76611a106b4625cc2f7019141d4521b167 Mon Sep 17 00:00:00 2001 From: Noisyfox Date: Sun, 26 Jul 2026 20:25:25 +0800 Subject: [PATCH 4/8] feat: Add layout debugging/inspecting tool (#14919) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add wxInspector dep * Initial intergration of wxInspector * docs: add wxInspector plugins design spec Design spec for two wxInspector plugins (DPIAware + CustomWidgets) that expose OrcaSlicer's custom control properties in the inspector property grid. Covers: DPIAware scale-factor properties, Button, CheckBox, TextInput, SwitchButton, ProgressBar, Label, and LabeledStaticBox. * docs: add wxInspector plugins implementation plan 6-task plan covering: source changes to existing widget headers, DPIAwarePlugin, CustomWidgetsPlugin, registration helper, MainFrame/CMake wiring, and build verification. * feat: add getters/setters for wxInspector plugin access Add minimal public accessors to DPIAware (set_scale_factor, set_prev_scale_factor, set_em_unit, force_rescale), Button (GetStyle, GetType, IsSelected), CheckBox (IsHalfChecked), TextInput (GetCornerRadius), and LabeledStaticBox (GetCornerRadius, GetBorderWidth, GetBorderColor, GetScale). * feat: add wxInspector plugin registration helper Add RegisterOrcaInspectorPlugins() inline function that creates and registers the DPIAwarePlugin and CustomWidgetsPlugin as static instances (matching wxInspector's built-in pattern). * feat: add DPIAware wxInspector plugin Exposes DPI scaling properties (scale_factor, prev_scale_factor, em_unit, normal_font, force_rescale) on DPIFrame and DPIDialog widgets. Uses dynamic_cast for detection and a template helper to capture the correct static type for lambda accessors. * feat: add OrcaCustomWidgets wxInspector plugin Exposes Orca-specific properties on 7 widget types: - Button: Style, Type, Selected - CheckBox: Half Checked - TextInput: Label, Text Value, Corner Radius - SwitchButton: Value - ProgressBar: Proportion, Show Number - Label: Is Hyperlink, Font Point Size - LabeledStaticBox: Corner Radius, Border Width, Border Color, Scale Each widget type uses dynamic_cast for safe detection. * feat: wire wxInspector plugins into MainFrame and build Call RegisterOrcaInspectorPlugins() in MainFrame constructor after SetupInspectorAccelerator(). Add all 5 plugin source files to SLIC3R_GUI_SOURCES in CMakeLists.txt. * fix: move plugin registration to GUI_App::on_init_inner Register plugins once in app init rather than in MainFrame constructor, which may be recreated during the application lifetime. * fix: include plugin headers in Registration.hpp for complete types Static locals require complete type. Include DPIAwarePlugin.hpp and CustomWidgetsPlugin.hpp instead of forward-declaring. Also remove unused include from MainFrame.cpp (registration moved to GUI_App). * fix: qualify DPIFrame/DPIDialog with Slic3r::GUI namespace * Make DPIDialog inspectable. For other dialogs, we will add them if necessary later. * docs: add spec for moving wxInspectable into DPIAware template Move wxInspector::wxInspectable base class from DPIDialog and MainFrame into the common DPIAware

template, making all DPIAware widgets automatically visible in the inspector tree. Co-Authored-By: Claude * docs: add implementation plan for moving wxInspectable into DPIAware Co-Authored-By: Claude * docs: update spec/plan — move SetupInspectorAccelerator into DPIAware too Co-Authored-By: Claude * refactor: move wxInspectable and SetupInspectorAccelerator into DPIAware DPIAware

now inherits wxInspector::wxInspectable and calls SetupInspectorAccelerator in its constructor, making all DPIAware widgets automatically appear in the inspector tree with the Ctrl+Shift+I shortcut. DPIDialog now uses 'using' to inherit the constructor. Remove redundant wxInspectable inheritance and SetupInspectorAccelerator calls from DPIDialog and MainFrame. Co-Authored-By: Claude * fix: use LB_HYPERLINK constant instead of magic number 0x0020 Co-Authored-By: Claude * Clean up * Fix Linux build * Don't build wxInspector sample * Use shallow clone * Try fix flatpak build * Attempt to fix build again * Fix build failure caused by https://github.com/wxWidgets/wxWidgets/commit/436c16135ec7ddf580f44624bf74c592aae43b66 * wxWidgets build only download required submodules * This should fix build on Windows on ARM * Enable PIC * Disable layout inspector by default for public release * Use wxInspector 1.0.0 release --------- Co-authored-by: Claude --- deps/CMakeLists.txt | 2 + deps/wxInspector/wxInspector.cmake | 13 + deps/wxWidgets/wxWidgets.cmake | 1 + ...6-07-23-wx-inspectable-on-dpiaware-plan.md | 111 +++ .../2026-07-23-wx-inspector-plugins-plan.md | 753 ++++++++++++++++++ ...07-23-wx-inspectable-on-dpiaware-design.md | 102 +++ .../2026-07-23-wx-inspector-plugins-design.md | 244 ++++++ scripts/flatpak/com.orcaslicer.OrcaSlicer.yml | 6 + src/CMakeLists.txt | 4 + src/libslic3r/Technologies.hpp | 5 + src/slic3r/CMakeLists.txt | 5 + src/slic3r/GUI/DeviceCore/DevUtil.h | 1 + src/slic3r/GUI/GUI_App.cpp | 4 + src/slic3r/GUI/GUI_Utils.hpp | 9 +- src/slic3r/GUI/MainFrame.cpp | 2 +- src/slic3r/GUI/Widgets/Button.hpp | 5 + src/slic3r/GUI/Widgets/CheckBox.hpp | 3 + src/slic3r/GUI/Widgets/LabeledStaticBox.hpp | 6 + src/slic3r/GUI/Widgets/TextInput.hpp | 3 + .../CustomWidgetsPlugin.cpp | 274 +++++++ .../CustomWidgetsPlugin.hpp | 30 + .../wxInspectorPlugins/DPIAwarePlugin.cpp | 82 ++ .../wxInspectorPlugins/DPIAwarePlugin.hpp | 14 + .../Utils/wxInspectorPlugins/Registration.hpp | 14 + 24 files changed, 1691 insertions(+), 2 deletions(-) create mode 100644 deps/wxInspector/wxInspector.cmake create mode 100644 docs/superpowers/plans/2026-07-23-wx-inspectable-on-dpiaware-plan.md create mode 100644 docs/superpowers/plans/2026-07-23-wx-inspector-plugins-plan.md create mode 100644 docs/superpowers/specs/2026-07-23-wx-inspectable-on-dpiaware-design.md create mode 100644 docs/superpowers/specs/2026-07-23-wx-inspector-plugins-design.md create mode 100644 src/slic3r/Utils/wxInspectorPlugins/CustomWidgetsPlugin.cpp create mode 100644 src/slic3r/Utils/wxInspectorPlugins/CustomWidgetsPlugin.hpp create mode 100644 src/slic3r/Utils/wxInspectorPlugins/DPIAwarePlugin.cpp create mode 100644 src/slic3r/Utils/wxInspectorPlugins/DPIAwarePlugin.hpp create mode 100644 src/slic3r/Utils/wxInspectorPlugins/Registration.hpp diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 9720cebcc9..39cc5de182 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -423,6 +423,7 @@ endif () include(OCCT/OCCT.cmake) include(OpenCV/OpenCV.cmake) include(python3/python3.cmake) +include(wxInspector/wxInspector.cmake) set(_dep_list dep_Boost @@ -446,6 +447,7 @@ set(_dep_list ${EXPAT_PKG} dep_libnoise dep_python3 + dep_wxInspector ) if (MSVC) diff --git a/deps/wxInspector/wxInspector.cmake b/deps/wxInspector/wxInspector.cmake new file mode 100644 index 0000000000..97c3810809 --- /dev/null +++ b/deps/wxInspector/wxInspector.cmake @@ -0,0 +1,13 @@ +orcaslicer_add_cmake_project( + wxInspector + URL https://github.com/Noisyfox/wxInspector/archive/refs/tags/v1.0.0.zip + URL_HASH SHA256=0ba163956f2d468b19a91b96c5aba66ee9610843ea41dda628ea44cdafde7db7 + DEPENDS ${WXWIDGETS_PKG} + CMAKE_ARGS + -DCMAKE_CXX_FLAGS="-DwxDEBUG_LEVEL=0" + -DCMAKE_POSITION_INDEPENDENT_CODE=ON +) + +if (MSVC) + add_debug_dep(dep_wxInspector) +endif () diff --git a/deps/wxWidgets/wxWidgets.cmake b/deps/wxWidgets/wxWidgets.cmake index 682b28bac4..1e2cc85f78 100644 --- a/deps/wxWidgets/wxWidgets.cmake +++ b/deps/wxWidgets/wxWidgets.cmake @@ -26,6 +26,7 @@ orcaslicer_add_cmake_project( GIT_REPOSITORY "https://github.com/SoftFever/Orca-deps-wxWidgets" GIT_TAG v3.3.2 GIT_SHALLOW ON + GIT_SUBMODULES 3rdparty/catch 3rdparty/pcre 3rdparty/libwebp DEPENDS ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} ${JPEG_PKG} CMAKE_ARGS -DwxBUILD_PRECOMP=ON diff --git a/docs/superpowers/plans/2026-07-23-wx-inspectable-on-dpiaware-plan.md b/docs/superpowers/plans/2026-07-23-wx-inspectable-on-dpiaware-plan.md new file mode 100644 index 0000000000..5b3669ace4 --- /dev/null +++ b/docs/superpowers/plans/2026-07-23-wx-inspectable-on-dpiaware-plan.md @@ -0,0 +1,111 @@ +# Move `wxInspectable` into `DPIAware` — Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Move `wxInspector::wxInspectable` from individual leaf classes into the common `DPIAware

` template so every DPIAware widget is automatically inspectable and gets the inspector keyboard shortcut. + +**Architecture:** `DPIAware

` gains `wxInspector::wxInspectable` as a second base class and calls `SetupInspectorAccelerator(this)` in its constructor. `DPIDialog` and `MainFrame` drop their now-redundant `wxInspectable` inheritance and `SetupInspectorAccelerator` calls. + +**Tech Stack:** C++17, wxWidgets, wxInspector + +## Global Constraints + +- Build with `D:\VisualStudio\2026\Community\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe` +- Use `--config RelWithDebInfo` for all builds +- Cross-platform: must compile on Windows, macOS, and Linux +- Match existing code style: PascalCase classes, `#pragma once` +- Do NOT commit files under `.superpowers/` +- Do NOT commit `task.md` + +--- + +### Task 1: Move `wxInspectable` and `SetupInspectorAccelerator` into `DPIAware

` + +**Files:** +- Modify: `src/slic3r/GUI/GUI_Utils.hpp:92` (DPIAware template — add wxInspectable base + SetupInspectorAccelerator call) +- Modify: `src/slic3r/GUI/GUI_Utils.hpp:276` (DPIDialog — drop wxInspectable + SetupInspectorAccelerator) +- Modify: `src/slic3r/GUI/MainFrame.hpp:96` (MainFrame — drop wxInspectable) +- Modify: `src/slic3r/GUI/MainFrame.cpp:304` (MainFrame constructor — drop SetupInspectorAccelerator) + +**Interfaces:** +- Consumes: Nothing (standalone refactor) +- Produces: All DPIAware widgets automatically inherit `wxInspector::wxInspectable` and get Ctrl+Shift+I accelerator + +- [ ] **Step 1: Add `wxInspectable` to `DPIAware

` and call `SetupInspectorAccelerator`** + +In `src/slic3r/GUI/GUI_Utils.hpp`, line 92, change the base class: + +```cpp +// Before: +template class DPIAware : public P +// After: +template class DPIAware : public P, public wxInspector::wxInspectable +``` + +In the constructor body of `DPIAware

`, after `this->CenterOnParent();` (currently line 110), add: + +```cpp +SetupInspectorAccelerator(this); +``` + +(`` is already included at line 23.) + +- [ ] **Step 2: Remove redundant `wxInspectable` and `SetupInspectorAccelerator` from `DPIDialog`** + +In `src/slic3r/GUI/GUI_Utils.hpp`, line 276, change: + +```cpp +// Before: +class DPIDialog : public DPIAware, public wxInspector::wxInspectable +// After: +class DPIDialog : public DPIAware +``` + +In the `DPIDialog` constructor body, remove the `SetupInspectorAccelerator(this);` line (currently line 286). The rest of the constructor stays. + +- [ ] **Step 3: Remove redundant `wxInspectable` from `MainFrame`** + +In `src/slic3r/GUI/MainFrame.hpp`, line 96, change: + +```cpp +// Before: +class MainFrame : public DPIFrame, public wxInspector::wxInspectable +// After: +class MainFrame : public DPIFrame +``` + +`MainFrame` now gets `wxInspectable` through `DPIFrame` → `DPIAware`. + +- [ ] **Step 4: Remove redundant `SetupInspectorAccelerator` from `MainFrame` constructor** + +In `src/slic3r/GUI/MainFrame.cpp`, line 304, remove the line: + +```cpp +SetupInspectorAccelerator(this); +``` + +It is now called automatically by the `DPIAware` constructor. + +- [ ] **Step 5: Build to verify compilation** + +```powershell +$cmakePath = "D:\VisualStudio\2026\Community\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe" +& $cmakePath --build . --config RelWithDebInfo --target ALL_BUILD -- -m +``` + +Expected: Build succeeds with zero new errors or warnings. + +- [ ] **Step 6: Commit** + +```bash +git add src/slic3r/GUI/GUI_Utils.hpp src/slic3r/GUI/MainFrame.hpp src/slic3r/GUI/MainFrame.cpp +git commit -m "refactor: move wxInspectable and SetupInspectorAccelerator into DPIAware + +DPIAware

now inherits wxInspector::wxInspectable and calls +SetupInspectorAccelerator in its constructor, making all DPIAware +widgets automatically appear in the inspector tree with the +Ctrl+Shift+I shortcut. Remove redundant wxInspectable inheritance +and SetupInspectorAccelerator calls from DPIDialog and MainFrame. + +Co-Authored-By: Claude " +``` diff --git a/docs/superpowers/plans/2026-07-23-wx-inspector-plugins-plan.md b/docs/superpowers/plans/2026-07-23-wx-inspector-plugins-plan.md new file mode 100644 index 0000000000..c3061b223d --- /dev/null +++ b/docs/superpowers/plans/2026-07-23-wx-inspector-plugins-plan.md @@ -0,0 +1,753 @@ +# wxInspector Plugins for OrcaSlicer — Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Build two wxInspector plugins (DPIAware + CustomWidgets) that expose OrcaSlicer custom control properties in the inspector's property grid. + +**Architecture:** Two plugins in a shared folder under `src/slic3r/Utils/wxInspectorPlugins/`. DPIAwarePlugin uses `dynamic_cast/` for detection; CustomWidgetsPlugin uses per-type `dynamic_cast`. Both registered as static singletons via a single inline function in `Registration.hpp`, called from `MainFrame` constructor. + +**Tech Stack:** C++17, wxWidgets, wxInspector plugin API (`wx/inspector/plugin.h`, `wx/inspector/inspector.h`), OrcaSlicer custom widget headers + +## Global Constraints + +- Plugins placed under `src/slic3r/Utils/wxInspectorPlugins/` +- Build with `D:\VisualStudio\2026\Community\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe` +- Minimal source changes: only trivial (one-line) getters/setters added to existing classes +- Cross-platform: must compile on Windows, macOS, and Linux +- Match existing code style: PascalCase classes, snake_case functions, `#pragma once` + +--- + +### Task 1: Add getters/setters to existing Orca widget headers + +**Files:** +- Modify: `src/slic3r/GUI/GUI_Utils.hpp` (DPIAware template — add 4 methods) +- Modify: `src/slic3r/GUI/Widgets/Button.hpp` (add 3 getters) +- Modify: `src/slic3r/GUI/Widgets/CheckBox.hpp` (add 1 getter) +- Modify: `src/slic3r/GUI/Widgets/TextInput.hpp` (add 1 getter) +- Modify: `src/slic3r/GUI/Widgets/LabeledStaticBox.hpp` (add 4 getter declarations) +- Modify: `src/slic3r/GUI/Widgets/LabeledStaticBox.cpp` (add 4 getter implementations) + +**Interfaces:** +- Consumes: Nothing (prerequisite for all other tasks) +- Produces: + - `DPIAware

::set_scale_factor(float)`, `DPIAware

::set_prev_scale_factor(float)`, `DPIAware

::set_em_unit(int)`, `DPIAware

::force_rescale() const` + - `Button::GetStyle()`, `Button::GetType()`, `Button::IsSelected()` + - `CheckBox::IsHalfChecked()` + - `TextInput::GetCornerRadius()` + - `LabeledStaticBox::GetCornerRadius()`, `LabeledStaticBox::GetBorderWidth()`, `LabeledStaticBox::GetBorderColor()`, `LabeledStaticBox::GetScale()` + +- [ ] **Step 1: Add DPIAware setters/getter in GUI_Utils.hpp** + +After line 184 (`float prev_scale_factor() const { return m_prev_scale_factor; }`), add: + +```cpp +void set_scale_factor(float v) { m_scale_factor = v; } +void set_prev_scale_factor(float v) { m_prev_scale_factor = v; } +void set_em_unit(int v) { m_em_unit = v; } +bool force_rescale() const { return m_force_rescale; } +``` + +- [ ] **Step 2: Add Button getters in Button.hpp** + +After line 79 (`void SetSelected(bool selected = true) { m_selected = selected; }`), add: + +```cpp +ButtonStyle GetStyle() const { return m_style; } +ButtonType GetType() const { return m_type; } +bool IsSelected() const { return m_selected; } +``` + +- [ ] **Step 3: Add CheckBox getter in CheckBox.hpp** + +After line 16 (`void SetHalfChecked(bool value = true);`), add: + +```cpp +bool IsHalfChecked() const { return m_half_checked; } +``` + +- [ ] **Step 4: Add TextInput getter in TextInput.hpp** + +After line 44 (`void SetCornerRadius(double radius);`), add: + +```cpp +int GetCornerRadius() const { return static_cast(radius); } +``` + +(Note: `radius` is inherited from `StaticBox` which has it as a protected `double` member.) + +- [ ] **Step 5: Add LabeledStaticBox getter declarations in LabeledStaticBox.hpp** + +After line 46 (`bool Enable(bool enable) override;`), add: + +```cpp +int GetCornerRadius() const { return m_radius; } +int GetBorderWidth() const { return m_border_width; } +StateColor GetBorderColor() const { return border_color; } +float GetScale() const { return m_scale; } +``` + +(Note: all of `m_radius`, `m_border_width`, `border_color`, `m_scale` are protected members, accessible to inline methods.) + +- [ ] **Step 6: Commit** + +```bash +git add src/slic3r/GUI/GUI_Utils.hpp src/slic3r/GUI/Widgets/Button.hpp src/slic3r/GUI/Widgets/CheckBox.hpp src/slic3r/GUI/Widgets/TextInput.hpp src/slic3r/GUI/Widgets/LabeledStaticBox.hpp +git commit -m "feat: add getters/setters for wxInspector plugin access + +Add minimal public accessors to DPIAware (set_scale_factor, +set_prev_scale_factor, set_em_unit, force_rescale), Button +(GetStyle, GetType, IsSelected), CheckBox (IsHalfChecked), +TextInput (GetCornerRadius), and LabeledStaticBox +(GetCornerRadius, GetBorderWidth, GetBorderColor, GetScale)." +``` + +--- + +### Task 2: Create Registration helper header + +**Files:** +- Create: `src/slic3r/Utils/wxInspectorPlugins/Registration.hpp` + +**Interfaces:** +- Consumes: Nothing (forward-declares plugin classes) +- Produces: `RegisterOrcaInspectorPlugins()` + +- [ ] **Step 1: Create directory** + +```bash +mkdir -p src/slic3r/Utils/wxInspectorPlugins +``` + +- [ ] **Step 2: Write Registration.hpp** + +```cpp +#pragma once + +namespace wxInspector { +class wxInspectorPlugin; +void RegisterPlugin(wxInspectorPlugin* plugin); +} + +// Forward declare our plugins +class DPIAwarePlugin; +class CustomWidgetsPlugin; + +inline void RegisterOrcaInspectorPlugins() +{ + static DPIAwarePlugin dpiaware; + static CustomWidgetsPlugin customWidgets; + wxInspector::RegisterPlugin(&dpiaware); + wxInspector::RegisterPlugin(&customWidgets); +} +``` + +- [ ] **Step 3: Commit** + +```bash +git add src/slic3r/Utils/wxInspectorPlugins/Registration.hpp +git commit -m "feat: add wxInspector plugin registration helper + +Add RegisterOrcaInspectorPlugins() inline function that creates +and registers the DPIAwarePlugin and CustomWidgetsPlugin as +static instances (matching wxInspector's built-in pattern)." +``` + +--- + +### Task 3: Create DPIAwarePlugin + +**Files:** +- Create: `src/slic3r/Utils/wxInspectorPlugins/DPIAwarePlugin.hpp` +- Create: `src/slic3r/Utils/wxInspectorPlugins/DPIAwarePlugin.cpp` + +**Interfaces:** +- Consumes: Task 1 (DPIAware getters/setters), Task 2 (registration pattern) +- Produces: `class DPIAwarePlugin : public wxInspector::wxInspectorPlugin` + +- [ ] **Step 1: Write DPIAwarePlugin.hpp** + +```cpp +#pragma once + +#include + +class DPIAwarePlugin : public wxInspector::wxInspectorPlugin +{ +public: + wxString GetName() const override; + + bool CanProvideProperties(wxClassInfo* info) override; + + wxVector GetProperties( + wxInspector::InspectableObject& obj) override; +}; +``` + +- [ ] **Step 2: Write DPIAwarePlugin.cpp** + +```cpp +#include "DPIAwarePlugin.hpp" + +#include "slic3r/GUI/GUI_Utils.hpp" // DPIFrame, DPIDialog, DPIAware

+ +#include + +namespace { + +template +void addDPIProps(T* dpi, wxVector& props) +{ + using namespace wxInspector; + + props.push_back({"Scale Factor", "DPI Scaling", PropertyType::String, + wxString::Format("%.2f", dpi->scale_factor()), false, {}, + [dpi]() { return wxString::Format("%.2f", dpi->scale_factor()); }, + [dpi](const wxString& v) { + double val; + if (wxSscanf(v, "%lf", &val) != 1) return false; + dpi->set_scale_factor((float) val); + return true; + }}); + + props.push_back({"Prev Scale Factor", "DPI Scaling", PropertyType::String, + wxString::Format("%.2f", dpi->prev_scale_factor()), false, {}, + [dpi]() { return wxString::Format("%.2f", dpi->prev_scale_factor()); }, + [dpi](const wxString& v) { + double val; + if (wxSscanf(v, "%lf", &val) != 1) return false; + dpi->set_prev_scale_factor((float) val); + return true; + }}); + + props.push_back({"EM Unit", "DPI Scaling", PropertyType::Integer, + wxString::Format("%d", dpi->em_unit()), false, {}, + [dpi]() { return wxString::Format("%d", dpi->em_unit()); }, + [dpi](const wxString& v) { + long val; + if (!v.ToLong(&val)) return false; + dpi->set_em_unit((int) val); + return true; + }}); + + props.push_back({"Normal Font", "DPI Scaling", PropertyType::ReadOnly, + dpi->normal_font().GetNativeFontInfoDesc(), true, {}, + [dpi]() { return dpi->normal_font().GetNativeFontInfoDesc(); }, + nullptr}); + + props.push_back({"Force Rescale", "DPI Scaling", PropertyType::Boolean, + dpi->force_rescale() ? "true" : "false", true, {}, + [dpi]() { return dpi->force_rescale() ? "true" : "false"; }, + nullptr}); +} + +} // anonymous namespace + +wxString DPIAwarePlugin::GetName() const +{ + return "OrcaDPIAware"; +} + +bool DPIAwarePlugin::CanProvideProperties(wxClassInfo* info) +{ + return info->IsKindOf(CLASSINFO(wxWindow)); +} + +wxVector DPIAwarePlugin::GetProperties( + wxInspector::InspectableObject& obj) +{ + wxVector props; + wxWindow* win = obj.AsWindow(); + if (!win) return props; + + if (auto* frame = dynamic_cast(win)) { + addDPIProps(frame, props); + } else if (auto* dlg = dynamic_cast(win)) { + addDPIProps(dlg, props); + } + + return props; +} +``` + +- [ ] **Step 3: Commit** + +```bash +git add src/slic3r/Utils/wxInspectorPlugins/DPIAwarePlugin.hpp src/slic3r/Utils/wxInspectorPlugins/DPIAwarePlugin.cpp +git commit -m "feat: add DPIAware wxInspector plugin + +Exposes DPI scaling properties (scale_factor, prev_scale_factor, +em_unit, normal_font, force_rescale) on DPIFrame and DPIDialog +widgets. Uses dynamic_cast for detection and a template helper +to capture the correct static type for lambda accessors." +``` + +--- + +### Task 4: Create CustomWidgetsPlugin + +**Files:** +- Create: `src/slic3r/Utils/wxInspectorPlugins/CustomWidgetsPlugin.hpp` +- Create: `src/slic3r/Utils/wxInspectorPlugins/CustomWidgetsPlugin.cpp` + +**Interfaces:** +- Consumes: Task 1 (all widget getters), Task 2 (registration pattern) +- Produces: `class CustomWidgetsPlugin : public wxInspector::wxInspectorPlugin` + +- [ ] **Step 1: Write CustomWidgetsPlugin.hpp** + +```cpp +#pragma once + +#include + +class CustomWidgetsPlugin : public wxInspector::wxInspectorPlugin +{ +public: + wxString GetName() const override; + + bool CanProvideProperties(wxClassInfo* info) override; + + wxVector GetProperties( + wxInspector::InspectableObject& obj) override; + +private: + void addButtonProps(class Button* btn, + wxVector& props); + void addCheckBoxProps(class CheckBox* cb, + wxVector& props); + void addTextInputProps(class TextInput* ti, + wxVector& props); + void addSwitchButtonProps(class SwitchButton* sb, + wxVector& props); + void addProgressBarProps(class ProgressBar* pb, + wxVector& props); + void addLabelProps(class Label* lbl, + wxVector& props); + void addLabeledStaticBoxProps(class LabeledStaticBox* lsb, + wxVector& props); +}; +``` + +- [ ] **Step 2: Write CustomWidgetsPlugin.cpp — includes and GetName/CanProvideProperties** + +```cpp +#include "CustomWidgetsPlugin.hpp" + +#include "slic3r/GUI/Widgets/Button.hpp" +#include "slic3r/GUI/Widgets/CheckBox.hpp" +#include "slic3r/GUI/Widgets/TextInput.hpp" +#include "slic3r/GUI/Widgets/SwitchButton.hpp" +#include "slic3r/GUI/Widgets/ProgressBar.hpp" +#include "slic3r/GUI/Widgets/Label.hpp" +#include "slic3r/GUI/Widgets/LabeledStaticBox.hpp" + +#include +#include + +wxString CustomWidgetsPlugin::GetName() const +{ + return "OrcaCustomWidgets"; +} + +bool CustomWidgetsPlugin::CanProvideProperties(wxClassInfo* info) +{ + return info->IsKindOf(CLASSINFO(wxWindow)); +} + +wxVector CustomWidgetsPlugin::GetProperties( + wxInspector::InspectableObject& obj) +{ + wxVector props; + wxWindow* win = obj.AsWindow(); + if (!win) return props; + + if (auto* btn = dynamic_cast(win)) + addButtonProps(btn, props); + if (auto* cb = dynamic_cast(win)) + addCheckBoxProps(cb, props); + if (auto* ti = dynamic_cast(win)) + addTextInputProps(ti, props); + if (auto* sb = dynamic_cast(win)) + addSwitchButtonProps(sb, props); + if (auto* pb = dynamic_cast(win)) + addProgressBarProps(pb, props); + if (auto* lbl = dynamic_cast(win)) + addLabelProps(lbl, props); + if (auto* lsb = dynamic_cast(win)) + addLabeledStaticBoxProps(lsb, props); + + return props; +} +``` + +- [ ] **Step 3: Write CustomWidgetsPlugin.cpp — addButtonProps** + +```cpp +void CustomWidgetsPlugin::addButtonProps(Button* btn, + wxVector& props) +{ + using namespace wxInspector; + + wxVector styleChoices; + styleChoices.push_back("Regular"); + styleChoices.push_back("Confirm"); + styleChoices.push_back("Alert"); + styleChoices.push_back("Disabled"); + + auto styleToStr = [](ButtonStyle s) -> wxString { + switch (s) { + case ButtonStyle::Regular: return "Regular"; + case ButtonStyle::Confirm: return "Confirm"; + case ButtonStyle::Alert: return "Alert"; + case ButtonStyle::Disabled: return "Disabled"; + } + return "Regular"; + }; + + props.push_back({"Button Style", "Orca Button", PropertyType::Choice, + styleToStr(btn->GetStyle()), false, styleChoices, + [btn, styleToStr]() { return styleToStr(btn->GetStyle()); }, + [btn](const wxString& v) { + ButtonStyle s = ButtonStyle::Regular; + if (v == "Confirm") s = ButtonStyle::Confirm; + else if (v == "Alert") s = ButtonStyle::Alert; + else if (v == "Disabled") s = ButtonStyle::Disabled; + btn->SetStyle(s, btn->GetType()); + return true; + }}); + + wxVector typeChoices; + typeChoices.push_back("Compact"); + typeChoices.push_back("Window"); + typeChoices.push_back("Choice"); + typeChoices.push_back("Parameter"); + typeChoices.push_back("Icon"); + typeChoices.push_back("Expanded"); + + auto typeToStr = [](ButtonType t) -> wxString { + switch (t) { + case ButtonType::Compact: return "Compact"; + case ButtonType::Window: return "Window"; + case ButtonType::Choice: return "Choice"; + case ButtonType::Parameter: return "Parameter"; + case ButtonType::Icon: return "Icon"; + case ButtonType::Expanded: return "Expanded"; + } + return "Compact"; + }; + + props.push_back({"Button Type", "Orca Button", PropertyType::Choice, + typeToStr(btn->GetType()), false, typeChoices, + [btn, typeToStr]() { return typeToStr(btn->GetType()); }, + [btn](const wxString& v) { + ButtonType t = ButtonType::Compact; + if (v == "Window") t = ButtonType::Window; + else if (v == "Choice") t = ButtonType::Choice; + else if (v == "Parameter") t = ButtonType::Parameter; + else if (v == "Icon") t = ButtonType::Icon; + else if (v == "Expanded") t = ButtonType::Expanded; + btn->SetStyle(btn->GetStyle(), t); + return true; + }}); + + props.push_back({"Selected", "Orca Button", PropertyType::Boolean, + btn->IsSelected() ? "true" : "false", false, {}, + [btn]() { return btn->IsSelected() ? "true" : "false"; }, + [btn](const wxString& v) { + btn->SetSelected(v == "true"); + btn->Refresh(); + return true; + }}); +} +``` + +- [ ] **Step 4: Write CustomWidgetsPlugin.cpp — addCheckBoxProps** + +```cpp +void CustomWidgetsPlugin::addCheckBoxProps(CheckBox* cb, + wxVector& props) +{ + using namespace wxInspector; + + props.push_back({"Half Checked", "Orca CheckBox", PropertyType::Boolean, + cb->IsHalfChecked() ? "true" : "false", false, {}, + [cb]() { return cb->IsHalfChecked() ? "true" : "false"; }, + [cb](const wxString& v) { + cb->SetHalfChecked(v == "true"); + return true; + }}); +} +``` + +- [ ] **Step 5: Write CustomWidgetsPlugin.cpp — addTextInputProps** + +```cpp +void CustomWidgetsPlugin::addTextInputProps(TextInput* ti, + wxVector& props) +{ + using namespace wxInspector; + + props.push_back({"Label", "Orca TextInput", PropertyType::String, + ti->GetLabel(), false, {}, + [ti]() { return ti->GetLabel(); }, + [ti](const wxString& v) { ti->SetLabel(v); return true; }}); + + props.push_back({"Text Value", "Orca TextInput", PropertyType::String, + ti->GetTextCtrl()->GetValue(), false, {}, + [ti]() { return ti->GetTextCtrl()->GetValue(); }, + [ti](const wxString& v) { ti->GetTextCtrl()->SetValue(v); return true; }}); + + props.push_back({"Corner Radius", "Orca TextInput", PropertyType::Integer, + wxString::Format("%d", ti->GetCornerRadius()), false, {}, + [ti]() { return wxString::Format("%d", ti->GetCornerRadius()); }, + [ti](const wxString& v) { + long val; + if (!v.ToLong(&val)) return false; + ti->SetCornerRadius((double) val); + ti->Refresh(); + return true; + }}); +} +``` + +- [ ] **Step 6: Write CustomWidgetsPlugin.cpp — addSwitchButtonProps** + +```cpp +void CustomWidgetsPlugin::addSwitchButtonProps(SwitchButton* sb, + wxVector& props) +{ + using namespace wxInspector; + + props.push_back({"Value", "Orca SwitchButton", PropertyType::Boolean, + sb->GetValue() ? "true" : "false", false, {}, + [sb]() { return sb->GetValue() ? "true" : "false"; }, + [sb](const wxString& v) { + sb->SetValue(v == "true"); + return true; + }}); +} +``` + +(Note: `GetValue()` and `SetValue()` are inherited from `wxBitmapToggleButton` → `wxToggleButton`.) + +- [ ] **Step 7: Write CustomWidgetsPlugin.cpp — addProgressBarProps** + +```cpp +void CustomWidgetsPlugin::addProgressBarProps(ProgressBar* pb, + wxVector& props) +{ + using namespace wxInspector; + + props.push_back({"Proportion", "Orca ProgressBar", PropertyType::String, + wxString::Format("%.2f", pb->m_proportion), false, {}, + [pb]() { return wxString::Format("%.2f", pb->m_proportion); }, + [pb](const wxString& v) { + double val; + if (wxSscanf(v, "%lf", &val) != 1) return false; + pb->m_proportion = val; + pb->Refresh(); + return true; + }}); + + props.push_back({"Show Number", "Orca ProgressBar", PropertyType::Boolean, + pb->m_shownumber ? "true" : "false", false, {}, + [pb]() { return pb->m_shownumber ? "true" : "false"; }, + [pb](const wxString& v) { + pb->m_shownumber = (v == "true"); + pb->Refresh(); + return true; + }}); +} +``` + +(Note: `m_proportion` and `m_shownumber` are public members on `ProgressBar`.) + +- [ ] **Step 8: Write CustomWidgetsPlugin.cpp — addLabelProps** + +```cpp +void CustomWidgetsPlugin::addLabelProps(Label* lbl, + wxVector& props) +{ + using namespace wxInspector; + + bool isHyperlink = (lbl->GetWindowStyleFlag() & 0x0020) != 0; // LB_HYPERLINK + + props.push_back({"Is Hyperlink", "Orca Label", PropertyType::Boolean, + isHyperlink ? "true" : "false", true, {}, + [lbl]() { + return (lbl->GetWindowStyleFlag() & 0x0020) ? "true" : "false"; + }, + nullptr}); + + props.push_back({"Font Point Size", "Orca Label", PropertyType::ReadOnly, + wxString::Format("%d", lbl->GetFont().GetPointSize()), true, {}, + [lbl]() { + return wxString::Format("%d", lbl->GetFont().GetPointSize()); + }, + nullptr}); +} +``` + +- [ ] **Step 9: Write CustomWidgetsPlugin.cpp — addLabeledStaticBoxProps** + +```cpp +void CustomWidgetsPlugin::addLabeledStaticBoxProps(LabeledStaticBox* lsb, + wxVector& props) +{ + using namespace wxInspector; + + props.push_back({"Corner Radius", "LabeledStaticBox", PropertyType::Integer, + wxString::Format("%d", lsb->GetCornerRadius()), false, {}, + [lsb]() { return wxString::Format("%d", lsb->GetCornerRadius()); }, + [lsb](const wxString& v) { + long val; + if (!v.ToLong(&val)) return false; + lsb->SetCornerRadius((int) val); + return true; + }}); + + props.push_back({"Border Width", "LabeledStaticBox", PropertyType::Integer, + wxString::Format("%d", lsb->GetBorderWidth()), false, {}, + [lsb]() { return wxString::Format("%d", lsb->GetBorderWidth()); }, + [lsb](const wxString& v) { + long val; + if (!v.ToLong(&val)) return false; + lsb->SetBorderWidth((int) val); + return true; + }}); + + // Border Color: display as hex string + wxColour bc = lsb->GetBorderColor().colorForStates(0); + props.push_back({"Border Color", "LabeledStaticBox", PropertyType::String, + bc.GetAsString(wxC2S_HTML_SYNTAX), false, {}, + [lsb]() { + return lsb->GetBorderColor() + .colorForStates(0) + .GetAsString(wxC2S_HTML_SYNTAX); + }, + [lsb](const wxString& v) { + wxColour c(v); + if (!c.IsOk()) return false; + lsb->SetBorderColor(StateColor(c)); + return true; + }}); + + props.push_back({"Scale", "LabeledStaticBox", PropertyType::ReadOnly, + wxString::Format("%.2f", lsb->GetScale()), true, {}, + [lsb]() { return wxString::Format("%.2f", lsb->GetScale()); }, + nullptr}); +} +``` + +- [ ] **Step 10: Commit** + +```bash +git add src/slic3r/Utils/wxInspectorPlugins/CustomWidgetsPlugin.hpp src/slic3r/Utils/wxInspectorPlugins/CustomWidgetsPlugin.cpp +git commit -m "feat: add OrcaCustomWidgets wxInspector plugin + +Exposes Orca-specific properties on 7 widget types: +- Button: Style, Type, Selected +- CheckBox: Half Checked +- TextInput: Label, Text Value, Corner Radius +- SwitchButton: Value +- ProgressBar: Proportion, Show Number +- Label: Is Hyperlink, Font Point Size +- LabeledStaticBox: Corner Radius, Border Width, Border Color, Scale + +Each widget type uses dynamic_cast for safe detection." +``` + +--- + +### Task 5: Wire plugins into MainFrame and CMakeLists + +**Files:** +- Modify: `src/slic3r/GUI/MainFrame.cpp` (add include + registration call) +- Modify: `src/slic3r/CMakeLists.txt` (add 4 source files) + +**Interfaces:** +- Consumes: Tasks 1-4 (all plugins and registration helper) +- Produces: Registered plugins available at runtime, buildable project + +- [ ] **Step 1: Add include in MainFrame.cpp** + +After the existing includes (around line 30, near the other Utils includes), add: + +```cpp +#include "slic3r/Utils/wxInspectorPlugins/Registration.hpp" +``` + +- [ ] **Step 2: Add registration call in MainFrame constructor** + +After `SetupInspectorAccelerator(this);` (currently line ~303), add: + +```cpp +RegisterOrcaInspectorPlugins(); +``` + +- [ ] **Step 3: Add source files to CMakeLists.txt** + +Find the `SLIC3R_GUI_SOURCES` list in `src/slic3r/CMakeLists.txt`. After the existing `Utils/*.cpp` entries (around line 650-754), add: + +```cmake +Utils/wxInspectorPlugins/DPIAwarePlugin.hpp +Utils/wxInspectorPlugins/DPIAwarePlugin.cpp +Utils/wxInspectorPlugins/CustomWidgetsPlugin.hpp +Utils/wxInspectorPlugins/CustomWidgetsPlugin.cpp +Utils/wxInspectorPlugins/Registration.hpp +``` + +(Note: Add all 5 files — 2 .hpp + 2 .cpp + 1 Registration.hpp. wxWidgets cmake needs headers listed too for the resource system.) + +- [ ] **Step 4: Commit** + +```bash +git add src/slic3r/GUI/MainFrame.cpp src/slic3r/CMakeLists.txt +git commit -m "feat: wire wxInspector plugins into MainFrame and build + +- Call RegisterOrcaInspectorPlugins() after SetupInspectorAccelerator +- Add all plugin source files to SLIC3R_GUI_SOURCES" +``` + +--- + +### Task 6: Build and verify + +**Files:** +- None modified (verification only) + +- [ ] **Step 1: Configure the build** + +```powershell +$cmakePath = "D:\VisualStudio\2026\Community\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe" +& $cmakePath --build . --config Debug --target ALL_BUILD -- -m +``` + +Expected: Build succeeds with zero errors and zero warnings from our new files. + +- [ ] **Step 2: Fix any compilation errors** + +If the build fails: +- Check that `#include` paths resolve (the `slic3r/GUI/…` relative paths use `src/` as the include root — verify this is set up in CMake via `include_directories`) +- Check that `ButtonStyle` and `ButtonType` enums are visible (they're defined in `Button.hpp`) +- Check that `StateColor` constructor from `wxColour` is valid (it has `StateColor(wxColour const&)`) +- Check that `LabeledStaticBox::GetBorderColor()` returns by value (StateColor copy is fine) +- On macOS: static box margin removal call needs `#ifdef __WXOSX__` guard + +- [ ] **Step 3: Launch OrcaSlicer and verify inspector** + +Launch the built OrcaSlicer, press Ctrl+Shift+I to open the inspector: +1. Select the MainFrame in the tree — verify "DPI Scaling" category appears with Scale Factor, Prev Scale Factor, EM Unit, Normal Font, Force Rescale +2. Select an Orca Button — verify "Orca Button" category appears +3. Select an Orca CheckBox — verify "Orca CheckBox" category appears +4. Edit a property value (e.g., Scale Factor) — verify the setter applies correctly +5. Select a LabeledStaticBox — verify corner radius, border width, border color, scale appear + +- [ ] **Step 5: Commit (if fixes were needed) or mark complete** + +```bash +git status +``` + +If clean: verification complete. If changes were made: `git add` and commit with fix message. diff --git a/docs/superpowers/specs/2026-07-23-wx-inspectable-on-dpiaware-design.md b/docs/superpowers/specs/2026-07-23-wx-inspectable-on-dpiaware-design.md new file mode 100644 index 0000000000..3a4e873af6 --- /dev/null +++ b/docs/superpowers/specs/2026-07-23-wx-inspectable-on-dpiaware-design.md @@ -0,0 +1,102 @@ +# Move `wxInspectable` into `DPIAware` — Design Spec + +Date: 2026-07-23 +Branch: `dev/layout-inspector` + +## Overview + +Move the `wxInspector::wxInspectable` base class from individual leaf classes (`DPIDialog`, `MainFrame`) into the common `DPIAware

` template. This makes every DPIAware widget automatically visible in the inspector tree without requiring each subclass to opt in. + +## Motivation + +Currently, only `DPIDialog` and `MainFrame` explicitly inherit `wxInspectable`. `DPIFrame` (which `MainFrame` inherits from) does not — `MainFrame` adds it manually. This means: + +- Any `DPIAware` widget that isn't `DPIDialog` or `MainFrame` is invisible in the inspector tree +- `DPIFrame` subclasses (`BaseTransparentDPIFrame`, `ImageDPIFrame`, `ModelMallDialog`, `MediaFileFrame`, `SecondaryCheckDialog`, `PrintErrorDialog`, etc.) don't appear +- Adding a new DPIAware widget type requires remembering to also inherit `wxInspectable` + +Moving `wxInspectable` to `DPIAware` fixes this for all current and future DPIAware widgets at once. + +## Design + +### Change 1: `GUI_Utils.hpp` — `DPIAware

` + +Add `wxInspector::wxInspectable` as a second base class, and call `SetupInspectorAccelerator(this)` in the constructor (after `this->CenterOnParent()`): + +```cpp +// Before: +template class DPIAware : public P + +// After: +template class DPIAware : public P, public wxInspector::wxInspectable +``` + +Add in the constructor body (after `this->CenterOnParent()` at line 110): +```cpp +SetupInspectorAccelerator(this); +``` + +This gives every `DPIAware` widget both inspectability and the Ctrl+Shift+I keyboard shortcut automatically. `#include ` is already present in the file. + +### Change 2: `GUI_Utils.hpp` — `DPIDialog` + +Remove the now-redundant `wxInspector::wxInspectable` and the `SetupInspectorAccelerator(this)` call: + +```cpp +// Before: +class DPIDialog : public DPIAware, public wxInspector::wxInspectable +// ... + SetupInspectorAccelerator(this); + +// After: +class DPIDialog : public DPIAware +// (SetupInspectorAccelerator call removed — now done in DPIAware constructor) +``` + +`DPIDialog` gets `wxInspectable` and the accelerator through `DPIAware` now. + +### Change 3: `MainFrame.hpp` — `MainFrame` + +Remove the now-redundant `wxInspector::wxInspectable`: + +```cpp +// Before: +class MainFrame : public DPIFrame, public wxInspector::wxInspectable + +// After: +class MainFrame : public DPIFrame +``` + +`MainFrame` gets `wxInspectable` through `DPIFrame` → `DPIAware`. + +### Change 4: `MainFrame.cpp` — `MainFrame` constructor + +Remove the now-redundant `SetupInspectorAccelerator(this)` call (line 304). It will be called automatically by the `DPIAware` constructor. + +## Impact + +| Widget | Before | After | +|--------|--------|-------| +| `DPIDialog` subclasses (~80) | ✓ inspectable | ✓ inspectable (transitive) | +| `MainFrame` | ✓ inspectable | ✓ inspectable (transitive) | +| `DPIFrame` subclasses (8 others) | ✗ invisible | ✓ inspectable | +| Future `DPIAware` | ✗ invisible | ✓ inspectable | + +## Files Modified + +| File | Change | +|------|--------| +| `src/slic3r/GUI/GUI_Utils.hpp` | `DPIAware

` gains `wxInspector::wxInspectable` + `SetupInspectorAccelerator(this)` call; `DPIDialog` drops redundant `wxInspector::wxInspectable` and `SetupInspectorAccelerator(this)` | +| `src/slic3r/GUI/MainFrame.hpp` | `MainFrame` drops redundant `wxInspector::wxInspectable` | +| `src/slic3r/GUI/MainFrame.cpp` | Remove redundant `SetupInspectorAccelerator(this)` from MainFrame constructor | + +## Non-Goals + +- The `DPIAwarePlugin` detection logic (`dynamic_cast` / `dynamic_cast`) is unchanged +- No new DPI properties — this is purely about tree visibility and accelerator setup + +## Risk Assessment + +- **Multiple inheritance**: `DPIAware

` already has a vtable (virtual destructor). Adding `wxInspectable` adds a second base but no additional data members. The `wxInspector::wxInspectable` class is expected to be a lightweight marker interface. +- **Build**: No new includes needed; `` is already included in `GUI_Utils.hpp`. +- **Cross-platform**: The change is standard C++ multiple inheritance — no platform-specific concerns. diff --git a/docs/superpowers/specs/2026-07-23-wx-inspector-plugins-design.md b/docs/superpowers/specs/2026-07-23-wx-inspector-plugins-design.md new file mode 100644 index 0000000000..5715635969 --- /dev/null +++ b/docs/superpowers/specs/2026-07-23-wx-inspector-plugins-design.md @@ -0,0 +1,244 @@ +# wxInspector Plugins for OrcaSlicer Custom Controls — Design Spec + +Date: 2026-07-23 +Branch: `dev/layout-inspector` + +## Overview + +Create wxInspector plugins that expose OrcaSlicer's custom widget properties in the inspector's property grid. Without these plugins, the inspector shows only generic wxWidgets properties — missing all DPI-awareness data, custom styling, and Orca-specific control state. + +## Goals + +1. **DPIAware properties** — Inspect and update `scale_factor`, `prev_scale_factor`, `em_unit`, and `normal_font` on any DPIAware-derived widget +2. **Custom widget properties** — Surface Orca-specific properties on `Button`, `CheckBox`, `TextInput`, `SwitchButton`, `ProgressBar`, `Label`, and `LabeledStaticBox` +3. **Minimal source changes** — Only add trivial (one-line) getters/setters to existing classes; no architectural refactoring of Orca's widget hierarchy + +## Non-Goals + +- Custom inspector panels or AUI tabs (use the existing property grid and method invoker) +- Python-plugin integration (this is C++ wxInspector, not Orca's Python plugin system) +- Event logging customization (the built-in event logger already works) + +## Architecture + +### Two Plugins + +| Plugin | Class | Files | +|--------|-------|-------| +| DPIAware plugin | `DPIAwarePlugin` | `DPIAwarePlugin.hpp`, `DPIAwarePlugin.cpp` | +| Custom widgets plugin | `CustomWidgetsPlugin` | `CustomWidgetsPlugin.hpp`, `CustomWidgetsPlugin.cpp` | +| Registration helper | inline function | `Registration.hpp` | + +All files live under `src/slic3r/Utils/wxInspectorPlugins/`. + +### Plugin Detection Strategy + +**DPIAware plugin**: Uses `dynamic_cast` and `dynamic_cast` as detection gates. `DPIFrame` = `DPIAware`, `DPIDialog` = `DPIAware`. Since these are concrete typedefs, `dynamic_cast` works at runtime. This covers `MainFrame`, `SettingsDialog`, and all 8 calibration dialogs (which inherit `DPIDialog`). + +**Custom widgets plugin**: Gates broadly on `CLASSINFO(wxWindow)`, then uses per-type `dynamic_cast` inside `GetProperties` to check each Orca-specific type. Only matching types append properties. + +### Registration + +A single `RegisterOrcaInspectorPlugins()` inline function in `Registration.hpp` creates both plugins as function-local statics (matching the wxInspector built-in provider pattern) and registers them via `wxInspector::RegisterPlugin()`. + +Called once from `MainFrame::MainFrame()` after `SetupInspectorAccelerator(this)`. + +### Why Separate Plugins? + +- DPIAware is a C++ template concept (not a wxClassInfo-isKindOf check), so it needs its own detection logic +- Custom widgets use standard wxClassInfo-based detection, matching the built-in provider pattern +- Two focused files are easier to review and maintain than one monolithic plugin +- Compile-time failure isolation: if a widget header changes, only one plugin breaks + +## DPIAware Plugin — Property Specification + +### Source Changes (GUI_Utils.hpp) + +Four one-liner methods added to the `DPIAware

` template class (public section): + +```cpp +float scale_factor() const { return m_scale_factor; } // already exists +float prev_scale_factor() const { return m_prev_scale_factor; } // already exists +int em_unit() const { return m_em_unit; } // already exists +void set_scale_factor(float v) { m_scale_factor = v; } // NEW +void set_prev_scale_factor(float v) { m_prev_scale_factor = v; } // NEW +void set_em_unit(int v) { m_em_unit = v; } // NEW +bool force_rescale() const { return m_force_rescale; } // NEW +// m_normal_font getter already exists: normal_font() +``` + +### Detection + +```cpp +bool CanProvideProperties(wxClassInfo* info) override { + // Gated in GetProperties via dynamic_cast on the window itself + return info->IsKindOf(CLASSINFO(wxWindow)); +} +``` + +In `GetProperties`: +```cpp +auto* win = obj.AsWindow(); +bool isDPI = dynamic_cast(win) || dynamic_cast(win); +if (!isDPI) return props; +``` + +### Property Table (category: "DPI Scaling") + +| Name | Type | Editable | Getter | Setter | +|------|------|----------|--------|--------| +| Scale Factor | String (float) | Yes | `dpi->scale_factor()` | `dpi->set_scale_factor(v)` | +| Prev Scale Factor | String (float) | Yes | `dpi->prev_scale_factor()` | `dpi->set_prev_scale_factor(v)` | +| EM Unit | Integer | Yes | `dpi->em_unit()` | `dpi->set_em_unit(v)` | +| Normal Font | ReadOnly | No | `dpi->normal_font().GetNativeFontInfoDesc()` | — | +| Force Rescale | Boolean (ReadOnly) | No | `dpi->force_rescale()` | — | + +**Note on setters**: The setters simply store values. They do NOT trigger a widget rescale/layout. To see the effect of a changed scale factor, use the inspector's Methods panel to call `Layout()` or resize the window — which triggers the DPI_CHANGED event path naturally. + +## Custom Widgets Plugin — Property Specification + +All properties are appended to the built-in wxWindow properties. Each widget type is independently detected via `dynamic_cast`. + +### Detection gates (in `GetProperties`) + +```cpp +auto* win = obj.AsWindow(); +if (auto* btn = dynamic_cast(win)) { addButtonProperties(btn, props); } +if (auto* cb = dynamic_cast(win)) { addCheckBoxProperties(cb, props); } +if (auto* ti = dynamic_cast(win)) { addTextInputProperties(ti, props); } +if (auto* sb = dynamic_cast(win)) { addSwitchButtonProperties(sb, props); } +if (auto* pb = dynamic_cast(win)) { addProgressBarProperties(pb, props); } +if (auto* lbl = dynamic_cast(win)) { addLabelProperties(lbl, props); } +if (auto* lsb = dynamic_cast(win)) { addLabeledStaticBoxProperties(lsb, props); } +``` + +### Orca Button (`Button`) — category: "Orca Button" + +| Name | Type | Editable | Getter | Setter | +|------|------|----------|--------|--------| +| Button Style | Choice | Yes | enum→string | string→enum | +| Button Type | Choice | Yes | enum→string | string→enum | +| Selected | Boolean | Yes | `m_selected` (needs getter) | `SetSelected(v)` | +| Active Icon | ReadOnly | No | icon name string | — | +| Inactive Icon | ReadOnly | No | icon name string | — | + +Choices for Button Style: `Regular`, `Confirm`, `Alert`, `Disabled` +Choices for Button Type: `Compact`, `Window`, `Choice`, `Parameter`, `Icon`, `Expanded` + +**Source changes needed**: Button's `m_selected` is private. Add one-liner getter: +```cpp +bool IsSelected() const { return m_selected; } +``` + +### Orca CheckBox (`CheckBox`) — category: "Orca CheckBox" + +| Name | Type | Editable | Getter | Setter | +|------|------|----------|--------|--------| +| Half Checked | Boolean | Yes | `m_half_checked` (needs getter) | `SetHalfChecked(v)` | + +**Source changes needed**: `m_half_checked` is private. Add one-liner getter: +```cpp +bool IsHalfChecked() const { return m_half_checked; } +``` + +### Orca TextInput (`TextInput`) — category: "Orca TextInput" + +| Name | Type | Editable | Getter | Setter | +|------|------|----------|--------|--------| +| Label | String | Yes | `GetLabel()` (inherited from wxWindow) | `SetLabel(v)` (exists) | +| Text Value | String | Yes | `GetTextCtrl()->GetValue()` (GetTextCtrl is public) | `GetTextCtrl()->SetValue(v)` | +| Corner Radius | Integer | Yes | `GetCornerRadius()` (NEW) | `SetCornerRadius(v)` (exists) | + +**Source changes needed**: Add one getter to `TextInput`: +```cpp +int GetCornerRadius() const { return static_cast(radius); } +``` +(`radius` is inherited from StaticBox. `SetCornerRadius(double)` already exists. `GetTextCtrl()` is already public.) + +### Orca SwitchButton (`SwitchButton`) — category: "Orca SwitchButton" + +| Name | Type | Editable | Getter | Setter | +|------|------|----------|--------|--------| +| Value | Boolean | Yes | existing getter | existing setter | + +### Orca ProgressBar (`ProgressBar`) — category: "Orca ProgressBar" + +| Name | Type | Editable | Getter | Setter | +|------|------|----------|--------|--------| +| Proportion | Float (0-1) | Yes | `pb->m_proportion` (public member) | `pb->m_proportion = v` | +| Show Number | Boolean | Yes | `pb->m_shownumber` (public member) | `pb->m_shownumber = v` | + +**No source changes needed**: `m_proportion` and `m_shownumber` are already public members. `SetValue(int)` and `SetProgress(int)` already exist as public methods. + +### Orca Label (`Label`) — category: "Orca Label" + +| Name | Type | Editable | Getter | Setter | +|------|------|----------|--------|--------| +| Is Hyperlink | Boolean | No | existing flag check | — | +| Font Size | ReadOnly | No | `GetFont().GetPointSize()` | — | + +### LabeledStaticBox — category: "LabeledStaticBox" + +| Name | Type | Editable | Getter | Setter | +|------|------|----------|--------|--------| +| Corner Radius | Integer | Yes | `GetCornerRadius()` (NEW) | `SetCornerRadius(v)` (exists) | +| Border Width | Integer | Yes | `GetBorderWidth()` (NEW) | `SetBorderWidth(v)` (exists) | +| Border Color | String (hex) | Yes | `GetBorderColor()` (NEW) | `SetBorderColor(v)` (exists) | +| Scale | Float (ReadOnly) | No | `m_scale` (protected, needs getter) | — | + +**Source changes needed**: Four one-liner getters added to `LabeledStaticBox`: +```cpp +int GetCornerRadius() const { return m_radius; } +int GetBorderWidth() const { return m_border_width; } +StateColor GetBorderColor() const { return border_color; } +float GetScale() const { return m_scale; } +``` + +## Files Modified (Existing Code) + +| File | Changes | +|------|---------| +| `src/slic3r/GUI/GUI_Utils.hpp` | +4 methods in `DPIAware

`: `set_scale_factor()`, `set_prev_scale_factor()`, `set_em_unit()`, `force_rescale()` | +| `src/slic3r/GUI/Widgets/LabeledStaticBox.hpp` | +4 getter declarations: `GetCornerRadius()`, `GetBorderWidth()`, `GetBorderColor()`, `GetScale()` | +| `src/slic3r/GUI/Widgets/LabeledStaticBox.cpp` | +4 getter implementations | +| `src/slic3r/GUI/Widgets/Button.hpp` | +1 getter: `IsSelected()` | +| `src/slic3r/GUI/Widgets/CheckBox.hpp` | +1 getter: `IsHalfChecked()` | +| `src/slic3r/GUI/Widgets/TextInput.hpp` | +1 getter: `GetCornerRadius()` | +| `src/slic3r/GUI/Widgets/ProgressBar.hpp` | None (public members are used directly) | +| `src/slic3r/GUI/MainFrame.cpp` | +1 `#include`, +1 call to `RegisterOrcaInspectorPlugins()` | +| `src/slic3r/CMakeLists.txt` | +4 entries in `SLIC3R_GUI_SOURCES` (the .cpp plugin files) | + +## Files Created + +``` +src/slic3r/Utils/wxInspectorPlugins/ +├── DPIAwarePlugin.hpp +├── DPIAwarePlugin.cpp +├── CustomWidgetsPlugin.hpp +├── CustomWidgetsPlugin.cpp +└── Registration.hpp +``` + +## Build & Linking + +The `wxInspector` dependency is already wired: +- `deps/wxInspector/wxInspector.cmake` fetches and builds wxInspector +- `src/CMakeLists.txt` lines 92-93 link `wxInspector::wxInspector` into `wxWidgets_LIBRARIES` +- The plugin files only need `#include ` and `#include ` — both available from the installed dependency + +No new CMake dependencies needed. Only the new source files need listing in `SLIC3R_GUI_SOURCES`. + +## Error Handling & Edge Cases + +- **Stale pointers**: Plugin lambdas capture raw pointers, regenerated on every `GetProperties` call (matching wxInspector's built-in provider pattern). Pointers live only until the next tree selection. +- **Widget destruction**: If a widget is destroyed while the inspector is showing its properties, `InspectableObject::IsValid()` returns false and properties are not displayed. The inspector won't show stale data. +- **Invalid property values**: Setters use `sscanf` / `ToLong` with validation (matching built-in patterns). Bogus input is rejected — setter returns `false`, property grid shows error state. +- **DPI drift**: Setting `scale_factor` without triggering rescale means displayed sizes don't match the new factor. This is acceptable — the inspector is a developer tool; operators know to call `Layout()` after making changes. +- **Missing widget type**: If a `dynamic_cast` fails for all types, only built-in wxWindow properties are shown. No crash, no error — just reduced info. + +## Future Work (Out of Scope) + +- **StateColor visualization**: `StateColor` is a multi-value type (maps bitmask states to colors). A full solution would need a custom property editor (e.g., a table showing each state→color pair). Keep it simple for now. +- **ScalableBitmap display**: Could show the bitmap as an inline thumbnail. Complex property editor work — deferred. +- **More widget types**: `SwitchBoard`, `MultiSwitchButton`, `StepCtrl`, `FanControl`, `DropDown`, `ComboBox`, `AMS*` widgets could all benefit. Add as needed. +- **Property refresh on tree selection**: Currently properties are static snapshots. A "refresh" button or auto-poll could keep values current for rapidly-changing widgets (progress bars, etc.). The built-in wxInspector already provides a tree-refresh button. diff --git a/scripts/flatpak/com.orcaslicer.OrcaSlicer.yml b/scripts/flatpak/com.orcaslicer.OrcaSlicer.yml index a1699f2b93..c33425f23f 100644 --- a/scripts/flatpak/com.orcaslicer.OrcaSlicer.yml +++ b/scripts/flatpak/com.orcaslicer.OrcaSlicer.yml @@ -306,6 +306,12 @@ modules: sha256: c08bc65a81971c1dd5783182826503369466c7e67374d1646519adf05207b684 dest: external-packages/python3 + # wxInspector 1.0.0 + - type: file + url: https://github.com/Noisyfox/wxInspector/archive/refs/tags/v1.0.0.zip + sha256: 0ba163956f2d468b19a91b96c5aba66ee9610843ea41dda628ea44cdafde7db7 + dest: external-packages/wxInspector + # --------------------------------------------------------------- # Fallback archives for deps normally provided by the GNOME SDK. # These are only used if find_package() fails to locate them. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 07c492e617..79b49cfd16 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -88,6 +88,10 @@ if (SLIC3R_GUI) list(FILTER wxWidgets_LIBRARIES EXCLUDE REGEX OpenGL) # list(REMOVE_ITEM wxWidgets_LIBRARIES oleacc) + + find_package(wxInspector REQUIRED) + list(APPEND wxWidgets_LIBRARIES "wxInspector::wxInspector") + message(STATUS "wx libs: ${wxWidgets_LIBRARIES}") add_subdirectory(slic3r) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 06ebbbf41d..2dee4bc780 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -51,4 +51,9 @@ // Enable extension of tool position imgui dialog to show actual speed profile #define ENABLE_ACTUAL_SPEED_DEBUG 1 +// Disable layout inspector for public release +#if BBL_RELEASE_TO_PUBLIC +#define WXINSPECTOR_DISABLE +#endif + #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 35cf96d171..b98396f943 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -752,6 +752,11 @@ set(SLIC3R_GUI_SOURCES Utils/WxFontUtils.hpp Utils/FileTransferUtils.cpp Utils/FileTransferUtils.hpp + Utils/wxInspectorPlugins/DPIAwarePlugin.hpp + Utils/wxInspectorPlugins/DPIAwarePlugin.cpp + Utils/wxInspectorPlugins/CustomWidgetsPlugin.hpp + Utils/wxInspectorPlugins/CustomWidgetsPlugin.cpp + Utils/wxInspectorPlugins/Registration.hpp ) add_subdirectory(GUI/DeviceCore) diff --git a/src/slic3r/GUI/DeviceCore/DevUtil.h b/src/slic3r/GUI/DeviceCore/DevUtil.h index 0b02dd25a9..6644b8b5b9 100644 --- a/src/slic3r/GUI/DeviceCore/DevUtil.h +++ b/src/slic3r/GUI/DeviceCore/DevUtil.h @@ -13,6 +13,7 @@ #include #include +#include #include "nlohmann/json.hpp" diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 29aad072f0..aa57f76598 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -96,6 +96,7 @@ #include "../Utils/PresetUpdater.hpp" #include "../Utils/PrintHost.hpp" #include "../Utils/Process.hpp" +#include "../Utils/wxInspectorPlugins/Registration.hpp" #include "../Utils/MacDarkMode.hpp" #include "../Utils/Http.hpp" #include "../Utils/InstanceID.hpp" @@ -2835,6 +2836,9 @@ bool GUI_App::on_init_inner() ::Label::initSysFont(); + // Register wxInspector plugins for Orca custom controls + RegisterOrcaInspectorPlugins(); + // Set initialization of image handlers before any UI actions - See GH issue #7469 wxInitAllImageHandlers(); #ifdef NDEBUG diff --git a/src/slic3r/GUI/GUI_Utils.hpp b/src/slic3r/GUI/GUI_Utils.hpp index d6767d3310..427ba9f0ad 100644 --- a/src/slic3r/GUI/GUI_Utils.hpp +++ b/src/slic3r/GUI/GUI_Utils.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include "Event.hpp" @@ -88,7 +89,7 @@ void update_dark_ui(wxWindow* window); extern std::deque dialogStack; -template class DPIAware : public P +template class DPIAware : public P, public wxInspector::wxInspectable { public: DPIAware(wxWindow *parent, wxWindowID id, const wxString &title, const wxPoint &pos=wxDefaultPosition, @@ -107,6 +108,7 @@ public: this->SetFont(m_normal_font); #endif this->CenterOnParent(); + SetupInspectorAccelerator(this); #ifdef _WIN32 update_dark_ui(this); #endif @@ -182,6 +184,11 @@ public: float scale_factor() const { return m_scale_factor; } float prev_scale_factor() const { return m_prev_scale_factor; } + // Only meant to be used by inspector, not public API + void set_scale_factor(float v) { m_scale_factor = v; } + void set_prev_scale_factor(float v) { m_prev_scale_factor = v; } + void set_em_unit(int v) { m_em_unit = v; } + bool force_rescale() const { return m_force_rescale; } int em_unit() const { return m_em_unit; } // int font_size() const { return m_font_size; } diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 85923c87fc..5d7f629075 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -739,7 +739,7 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, BORDERLESS_FRAME_ return; } - if (evt.CmdDown() && evt.GetKeyCode() == 'I') { + if (evt.CmdDown() && evt.GetKeyCode() == 'I' && !evt.ShiftDown()) { if (!can_add_models()) return; if (m_plater) { m_plater->add_file(); } return; diff --git a/src/slic3r/GUI/Widgets/Button.hpp b/src/slic3r/GUI/Widgets/Button.hpp index bc093b512a..94b245a75b 100644 --- a/src/slic3r/GUI/Widgets/Button.hpp +++ b/src/slic3r/GUI/Widgets/Button.hpp @@ -77,6 +77,11 @@ public: void SetSelected(bool selected = true) { m_selected = selected; } + // Only meant to be used by inspector, not public API + ButtonStyle GetStyle() const { return m_style; } + ButtonType GetType() const { return m_type; } + bool IsSelected() const { return m_selected; } + bool Enable(bool enable = true) override; void EnableTooltipEvenDisabled();// The tip will be shown even if the button is disabled diff --git a/src/slic3r/GUI/Widgets/CheckBox.hpp b/src/slic3r/GUI/Widgets/CheckBox.hpp index 01b801a215..abff45ecf1 100644 --- a/src/slic3r/GUI/Widgets/CheckBox.hpp +++ b/src/slic3r/GUI/Widgets/CheckBox.hpp @@ -15,6 +15,9 @@ public: void SetHalfChecked(bool value = true); + // Only meant to be used by inspector, not public API + bool IsHalfChecked() const { return m_half_checked; } + void Rescale(); #ifdef __WXOSX__ diff --git a/src/slic3r/GUI/Widgets/LabeledStaticBox.hpp b/src/slic3r/GUI/Widgets/LabeledStaticBox.hpp index 3418269e57..f42175ae05 100644 --- a/src/slic3r/GUI/Widgets/LabeledStaticBox.hpp +++ b/src/slic3r/GUI/Widgets/LabeledStaticBox.hpp @@ -46,6 +46,12 @@ public: bool Enable(bool enable) override; + // Only meant to be used by inspector, not public API + int GetCornerRadius() const { return m_radius; } + int GetBorderWidth() const { return m_border_width; } + StateColor GetBorderColor() const { return border_color; } + float GetScale() const { return m_scale; } + private: void PickDC(wxDC& dc); diff --git a/src/slic3r/GUI/Widgets/TextInput.hpp b/src/slic3r/GUI/Widgets/TextInput.hpp index 20407b5de2..9aca7037c4 100644 --- a/src/slic3r/GUI/Widgets/TextInput.hpp +++ b/src/slic3r/GUI/Widgets/TextInput.hpp @@ -43,6 +43,9 @@ public: void SetCornerRadius(double radius); + // Only meant to be used by inspector, not public API + int GetCornerRadius() const { return static_cast(radius); } + void SetLabel(const wxString& label); void SetStaticTips(const wxString& tips, const wxBitmap& bitmap); diff --git a/src/slic3r/Utils/wxInspectorPlugins/CustomWidgetsPlugin.cpp b/src/slic3r/Utils/wxInspectorPlugins/CustomWidgetsPlugin.cpp new file mode 100644 index 0000000000..df1b495bff --- /dev/null +++ b/src/slic3r/Utils/wxInspectorPlugins/CustomWidgetsPlugin.cpp @@ -0,0 +1,274 @@ +#include "CustomWidgetsPlugin.hpp" + +#include "slic3r/GUI/Widgets/Button.hpp" +#include "slic3r/GUI/Widgets/CheckBox.hpp" +#include "slic3r/GUI/Widgets/TextInput.hpp" +#include "slic3r/GUI/Widgets/SwitchButton.hpp" +#include "slic3r/GUI/Widgets/ProgressBar.hpp" +#include "slic3r/GUI/Widgets/Label.hpp" +#include "slic3r/GUI/Widgets/LabeledStaticBox.hpp" + +#include +#include + +wxString CustomWidgetsPlugin::GetName() const +{ + return "OrcaCustomWidgets"; +} + +bool CustomWidgetsPlugin::CanProvideProperties(wxClassInfo* info) +{ + return info->IsKindOf(CLASSINFO(wxWindow)); +} + +wxVector CustomWidgetsPlugin::GetProperties( + wxInspector::InspectableObject& obj) +{ + wxVector props; + wxWindow* win = obj.AsWindow(); + if (!win) return props; + + if (auto* btn = dynamic_cast(win)) + addButtonProps(btn, props); + if (auto* cb = dynamic_cast(win)) + addCheckBoxProps(cb, props); + if (auto* ti = dynamic_cast(win)) + addTextInputProps(ti, props); + if (auto* sb = dynamic_cast(win)) + addSwitchButtonProps(sb, props); + if (auto* pb = dynamic_cast(win)) + addProgressBarProps(pb, props); + if (auto* lbl = dynamic_cast(win)) + addLabelProps(lbl, props); + if (auto* lsb = dynamic_cast(win)) + addLabeledStaticBoxProps(lsb, props); + + return props; +} + +void CustomWidgetsPlugin::addButtonProps(Button* btn, + wxVector& props) +{ + using namespace wxInspector; + + wxVector styleChoices; + styleChoices.push_back("Regular"); + styleChoices.push_back("Confirm"); + styleChoices.push_back("Alert"); + styleChoices.push_back("Disabled"); + + auto styleToStr = [](ButtonStyle s) -> wxString { + switch (s) { + case ButtonStyle::Regular: return "Regular"; + case ButtonStyle::Confirm: return "Confirm"; + case ButtonStyle::Alert: return "Alert"; + case ButtonStyle::Disabled: return "Disabled"; + } + return "Regular"; + }; + + props.push_back({"Button Style", "Orca Button", PropertyType::Choice, + styleToStr(btn->GetStyle()), false, styleChoices, + [btn, styleToStr]() { return styleToStr(btn->GetStyle()); }, + [btn](const wxString& v) { + ButtonStyle s = ButtonStyle::Regular; + if (v == "Confirm") s = ButtonStyle::Confirm; + else if (v == "Alert") s = ButtonStyle::Alert; + else if (v == "Disabled") s = ButtonStyle::Disabled; + btn->SetStyle(s, btn->GetType()); + return true; + }}); + + wxVector typeChoices; + typeChoices.push_back("Compact"); + typeChoices.push_back("Window"); + typeChoices.push_back("Choice"); + typeChoices.push_back("Parameter"); + typeChoices.push_back("Icon"); + typeChoices.push_back("Expanded"); + + auto typeToStr = [](ButtonType t) -> wxString { + switch (t) { + case ButtonType::Compact: return "Compact"; + case ButtonType::Window: return "Window"; + case ButtonType::Choice: return "Choice"; + case ButtonType::Parameter: return "Parameter"; + case ButtonType::Icon: return "Icon"; + case ButtonType::Expanded: return "Expanded"; + } + return "Compact"; + }; + + props.push_back({"Button Type", "Orca Button", PropertyType::Choice, + typeToStr(btn->GetType()), false, typeChoices, + [btn, typeToStr]() { return typeToStr(btn->GetType()); }, + [btn](const wxString& v) { + ButtonType t = ButtonType::Compact; + if (v == "Window") t = ButtonType::Window; + else if (v == "Choice") t = ButtonType::Choice; + else if (v == "Parameter") t = ButtonType::Parameter; + else if (v == "Icon") t = ButtonType::Icon; + else if (v == "Expanded") t = ButtonType::Expanded; + btn->SetStyle(btn->GetStyle(), t); + return true; + }}); + + props.push_back({"Selected", "Orca Button", PropertyType::Boolean, + btn->IsSelected() ? "true" : "false", false, {}, + [btn]() { return btn->IsSelected() ? "true" : "false"; }, + [btn](const wxString& v) { + btn->SetSelected(v == "true"); + btn->Refresh(); + return true; + }}); +} + +void CustomWidgetsPlugin::addCheckBoxProps(CheckBox* cb, + wxVector& props) +{ + using namespace wxInspector; + + props.push_back({"Half Checked", "Orca CheckBox", PropertyType::Boolean, + cb->IsHalfChecked() ? "true" : "false", false, {}, + [cb]() { return cb->IsHalfChecked() ? "true" : "false"; }, + [cb](const wxString& v) { + cb->SetHalfChecked(v == "true"); + return true; + }}); +} + +void CustomWidgetsPlugin::addTextInputProps(TextInput* ti, + wxVector& props) +{ + using namespace wxInspector; + + props.push_back({"Label", "Orca TextInput", PropertyType::String, + ti->GetLabel(), false, {}, + [ti]() { return ti->GetLabel(); }, + [ti](const wxString& v) { ti->SetLabel(v); return true; }}); + + props.push_back({"Text Value", "Orca TextInput", PropertyType::String, + ti->GetTextCtrl()->GetValue(), false, {}, + [ti]() { return ti->GetTextCtrl()->GetValue(); }, + [ti](const wxString& v) { ti->GetTextCtrl()->SetValue(v); return true; }}); + + props.push_back({"Corner Radius", "Orca TextInput", PropertyType::Integer, + wxString::Format("%d", ti->GetCornerRadius()), false, {}, + [ti]() { return wxString::Format("%d", ti->GetCornerRadius()); }, + [ti](const wxString& v) { + long val; + if (!v.ToLong(&val)) return false; + ti->SetCornerRadius((double) val); + ti->Refresh(); + return true; + }}); +} + +void CustomWidgetsPlugin::addSwitchButtonProps(SwitchButton* sb, + wxVector& props) +{ + using namespace wxInspector; + + props.push_back({"Value", "Orca SwitchButton", PropertyType::Boolean, + sb->GetValue() ? "true" : "false", false, {}, + [sb]() { return sb->GetValue() ? "true" : "false"; }, + [sb](const wxString& v) { + sb->SetValue(v == "true"); + return true; + }}); +} + +void CustomWidgetsPlugin::addProgressBarProps(ProgressBar* pb, + wxVector& props) +{ + using namespace wxInspector; + + props.push_back({"Proportion", "Orca ProgressBar", PropertyType::String, + wxString::Format("%.2f", pb->m_proportion), false, {}, + [pb]() { return wxString::Format("%.2f", pb->m_proportion); }, + [pb](const wxString& v) { + double val; + if (wxSscanf(v, "%lf", &val) != 1) return false; + pb->m_proportion = val; + pb->Refresh(); + return true; + }}); + + props.push_back({"Show Number", "Orca ProgressBar", PropertyType::Boolean, + pb->m_shownumber ? "true" : "false", false, {}, + [pb]() { return pb->m_shownumber ? "true" : "false"; }, + [pb](const wxString& v) { + pb->m_shownumber = (v == "true"); + pb->Refresh(); + return true; + }}); +} + +void CustomWidgetsPlugin::addLabelProps(Label* lbl, + wxVector& props) +{ + using namespace wxInspector; + + bool isHyperlink = (lbl->GetWindowStyleFlag() & LB_HYPERLINK) != 0; + + props.push_back({"Is Hyperlink", "Orca Label", PropertyType::Boolean, + isHyperlink ? "true" : "false", true, {}, + [lbl]() { + return (lbl->GetWindowStyleFlag() & LB_HYPERLINK) ? "true" : "false"; + }, + nullptr}); + + props.push_back({"Font Point Size", "Orca Label", PropertyType::ReadOnly, + wxString::Format("%d", lbl->GetFont().GetPointSize()), true, {}, + [lbl]() { + return wxString::Format("%d", lbl->GetFont().GetPointSize()); + }, + nullptr}); +} + +void CustomWidgetsPlugin::addLabeledStaticBoxProps(LabeledStaticBox* lsb, + wxVector& props) +{ + using namespace wxInspector; + + props.push_back({"Corner Radius", "LabeledStaticBox", PropertyType::Integer, + wxString::Format("%d", lsb->GetCornerRadius()), false, {}, + [lsb]() { return wxString::Format("%d", lsb->GetCornerRadius()); }, + [lsb](const wxString& v) { + long val; + if (!v.ToLong(&val)) return false; + lsb->SetCornerRadius((int) val); + return true; + }}); + + props.push_back({"Border Width", "LabeledStaticBox", PropertyType::Integer, + wxString::Format("%d", lsb->GetBorderWidth()), false, {}, + [lsb]() { return wxString::Format("%d", lsb->GetBorderWidth()); }, + [lsb](const wxString& v) { + long val; + if (!v.ToLong(&val)) return false; + lsb->SetBorderWidth((int) val); + return true; + }}); + + // Border Color: display as hex string + wxColour bc = lsb->GetBorderColor().colorForStates(0); + props.push_back({"Border Color", "LabeledStaticBox", PropertyType::String, + bc.GetAsString(wxC2S_HTML_SYNTAX), false, {}, + [lsb]() { + return lsb->GetBorderColor() + .colorForStates(0) + .GetAsString(wxC2S_HTML_SYNTAX); + }, + [lsb](const wxString& v) { + wxColour c(v); + if (!c.IsOk()) return false; + lsb->SetBorderColor(StateColor(c)); + return true; + }}); + + props.push_back({"Scale", "LabeledStaticBox", PropertyType::ReadOnly, + wxString::Format("%.2f", lsb->GetScale()), true, {}, + [lsb]() { return wxString::Format("%.2f", lsb->GetScale()); }, + nullptr}); +} diff --git a/src/slic3r/Utils/wxInspectorPlugins/CustomWidgetsPlugin.hpp b/src/slic3r/Utils/wxInspectorPlugins/CustomWidgetsPlugin.hpp new file mode 100644 index 0000000000..083ed8a74c --- /dev/null +++ b/src/slic3r/Utils/wxInspectorPlugins/CustomWidgetsPlugin.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include + +class CustomWidgetsPlugin : public wxInspector::wxInspectorPlugin +{ +public: + wxString GetName() const override; + + bool CanProvideProperties(wxClassInfo* info) override; + + wxVector GetProperties( + wxInspector::InspectableObject& obj) override; + +private: + void addButtonProps(class Button* btn, + wxVector& props); + void addCheckBoxProps(class CheckBox* cb, + wxVector& props); + void addTextInputProps(class TextInput* ti, + wxVector& props); + void addSwitchButtonProps(class SwitchButton* sb, + wxVector& props); + void addProgressBarProps(class ProgressBar* pb, + wxVector& props); + void addLabelProps(class Label* lbl, + wxVector& props); + void addLabeledStaticBoxProps(class LabeledStaticBox* lsb, + wxVector& props); +}; diff --git a/src/slic3r/Utils/wxInspectorPlugins/DPIAwarePlugin.cpp b/src/slic3r/Utils/wxInspectorPlugins/DPIAwarePlugin.cpp new file mode 100644 index 0000000000..f52a5d4313 --- /dev/null +++ b/src/slic3r/Utils/wxInspectorPlugins/DPIAwarePlugin.cpp @@ -0,0 +1,82 @@ +#include "DPIAwarePlugin.hpp" + +#include "slic3r/GUI/GUI_Utils.hpp" // DPIFrame, DPIDialog, DPIAware

+ +#include +#include + +namespace { + +template +void addDPIProps(T* dpi, wxVector& props) +{ + using namespace wxInspector; + + props.push_back({"Scale Factor", "DPI Scaling", PropertyType::String, + wxString::Format("%.2f", dpi->scale_factor()), false, {}, + [dpi]() { return wxString::Format("%.2f", dpi->scale_factor()); }, + [dpi](const wxString& v) { + double val; + if (wxSscanf(v, "%lf", &val) != 1) return false; + dpi->set_scale_factor((float) val); + return true; + }}); + + props.push_back({"Prev Scale Factor", "DPI Scaling", PropertyType::String, + wxString::Format("%.2f", dpi->prev_scale_factor()), false, {}, + [dpi]() { return wxString::Format("%.2f", dpi->prev_scale_factor()); }, + [dpi](const wxString& v) { + double val; + if (wxSscanf(v, "%lf", &val) != 1) return false; + dpi->set_prev_scale_factor((float) val); + return true; + }}); + + props.push_back({"EM Unit", "DPI Scaling", PropertyType::Integer, + wxString::Format("%d", dpi->em_unit()), false, {}, + [dpi]() { return wxString::Format("%d", dpi->em_unit()); }, + [dpi](const wxString& v) { + long val; + if (!v.ToLong(&val)) return false; + dpi->set_em_unit((int) val); + return true; + }}); + + props.push_back({"Normal Font", "DPI Scaling", PropertyType::ReadOnly, + dpi->normal_font().GetNativeFontInfoDesc(), true, {}, + [dpi]() { return dpi->normal_font().GetNativeFontInfoDesc(); }, + nullptr}); + + props.push_back({"Force Rescale", "DPI Scaling", PropertyType::Boolean, + dpi->force_rescale() ? "true" : "false", true, {}, + [dpi]() { return dpi->force_rescale() ? "true" : "false"; }, + nullptr}); +} + +} // anonymous namespace + +wxString DPIAwarePlugin::GetName() const +{ + return "OrcaDPIAware"; +} + +bool DPIAwarePlugin::CanProvideProperties(wxClassInfo* info) +{ + return info->IsKindOf(CLASSINFO(wxWindow)); +} + +wxVector DPIAwarePlugin::GetProperties( + wxInspector::InspectableObject& obj) +{ + wxVector props; + wxWindow* win = obj.AsWindow(); + if (!win) return props; + + if (auto* frame = dynamic_cast(win)) { + addDPIProps(frame, props); + } else if (auto* dlg = dynamic_cast(win)) { + addDPIProps(dlg, props); + } + + return props; +} diff --git a/src/slic3r/Utils/wxInspectorPlugins/DPIAwarePlugin.hpp b/src/slic3r/Utils/wxInspectorPlugins/DPIAwarePlugin.hpp new file mode 100644 index 0000000000..5d78342967 --- /dev/null +++ b/src/slic3r/Utils/wxInspectorPlugins/DPIAwarePlugin.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include + +class DPIAwarePlugin : public wxInspector::wxInspectorPlugin +{ +public: + wxString GetName() const override; + + bool CanProvideProperties(wxClassInfo* info) override; + + wxVector GetProperties( + wxInspector::InspectableObject& obj) override; +}; diff --git a/src/slic3r/Utils/wxInspectorPlugins/Registration.hpp b/src/slic3r/Utils/wxInspectorPlugins/Registration.hpp new file mode 100644 index 0000000000..54bbc0dbe4 --- /dev/null +++ b/src/slic3r/Utils/wxInspectorPlugins/Registration.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include "DPIAwarePlugin.hpp" +#include "CustomWidgetsPlugin.hpp" + +#include + +inline void RegisterOrcaInspectorPlugins() +{ + static DPIAwarePlugin dpiaware; + static CustomWidgetsPlugin customWidgets; + wxInspector::RegisterPlugin(&dpiaware); + wxInspector::RegisterPlugin(&customWidgets); +} From d84a211b919aa0c92a495412b3673fe43fa95f1e Mon Sep 17 00:00:00 2001 From: SoftFever Date: Sun, 26 Jul 2026 22:15:45 +0800 Subject: [PATCH 5/8] fix crashes when switching bbl printers --- src/slic3r/GUI/wxMediaCtrl2.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/wxMediaCtrl2.cpp b/src/slic3r/GUI/wxMediaCtrl2.cpp index 2c4b789e12..f2d1d2701e 100644 --- a/src/slic3r/GUI/wxMediaCtrl2.cpp +++ b/src/slic3r/GUI/wxMediaCtrl2.cpp @@ -602,6 +602,12 @@ WXLRESULT wxMediaCtrl2::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) { + // The stream source sends WM_USER+1000 with a synchronous SendMessage from its own threads, + // so this runs re-entrantly on the UI thread at whatever message-retrieval point the player + // happens to be in - often nested inside an Orca log statement. Never BOOST_LOG_TRIVIAL here: + // boost::log is not re-entrant on one thread, and doing so corrupted its per-thread record + // state, crashing later in unrelated places (the player, the log filter, a plug-in heap free). + // Post the string out (as the stat branch does) and log it on a clean stack instead. if (nMsg == WM_USER + 1000) { wxString msg((wchar_t const *) lParam); if (wParam == 1) { @@ -619,7 +625,6 @@ WXLRESULT wxMediaCtrl2::MSWWindowProc(WXUINT nMsg, wxPostEvent(this, evt); } } - BOOST_LOG_TRIVIAL(trace) << msg.ToUTF8().data(); return 0; } return wxMediaCtrl::MSWWindowProc(nMsg, wParam, lParam); From f60e0e776e641a5f6458d66d67dff54166e6fd44 Mon Sep 17 00:00:00 2001 From: SoftFever Date: Sun, 26 Jul 2026 23:49:53 +0800 Subject: [PATCH 6/8] Add back 02.03.00 plugin to support debugging (#14966) * Add back 02.03.00 plugin to support debugging --- src/slic3r/GUI/GUI_App.cpp | 21 ++- src/slic3r/Utils/BBLNetworkPlugin.cpp | 95 ++++++++++++-- src/slic3r/Utils/BBLNetworkPlugin.hpp | 34 +++-- src/slic3r/Utils/BBLPrinterAgent.cpp | 134 ++++++++++---------- src/slic3r/Utils/bambu_networking.hpp | 129 +++++++++++++++---- tests/slic3rutils/test_network_versions.cpp | 58 ++++++++- 6 files changed, 347 insertions(+), 124 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index aa57f76598..07e99348bb 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -3708,13 +3708,13 @@ bool GUI_App::on_init_network(bool try_backup) } } else { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": on_init_network, load dll failed"; - // A failed install can leave the config naming a build that never made it to - // disk (download_plugin() adopts the downloaded version up front so that - // install_plugin() can name the library after it). If the whitelisted latest - // is still installed, fall back to it instead of dropping the user into the - // re-download flow without networking. + // A failed install can leave the config naming a build that never made it to disk; + // fall back to the installed latest instead of dropping the user into the re-download + // flow. Only when the configured library is genuinely absent, though - a pinned series + // that is on disk but failed to load once must keep its pin, not be rewritten for good. std::string latest = get_latest_network_version(); - if (config_version != latest && BBLNetworkPlugin::versioned_library_exists(latest)) { + if (config_version != latest && !BBLNetworkPlugin::versioned_library_exists(config_version) + && BBLNetworkPlugin::versioned_library_exists(latest)) { BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": falling back to installed " << latest; config_version = latest; app_config->set_network_plugin_version(latest); @@ -3818,7 +3818,14 @@ bool GUI_App::on_init_network(bool try_backup) m_user_manager = new Slic3r::UserManager(); } - if (should_load_networking_plugin && m_networking_compatible && !use_legacy_network_plugin()) { + // A version pinned to something other than the latest series is a deliberate choice, so it + // is exempt from the upgrade prompt the same way the legacy pin already is - otherwise the + // dialog reappears on every launch for as long as the pin is held. + const std::string pinned_version = app_config->get_network_plugin_version(); + const bool pinned_to_older_series = !pinned_version.empty() && + network_plugin_series(pinned_version) != network_plugin_series(get_latest_network_version()); + + if (should_load_networking_plugin && m_networking_compatible && !pinned_to_older_series) { app_config->clear_remind_network_update_later(); if (has_network_update_available()) { diff --git a/src/slic3r/Utils/BBLNetworkPlugin.cpp b/src/slic3r/Utils/BBLNetworkPlugin.cpp index b0dd277a79..607e7d16d1 100644 --- a/src/slic3r/Utils/BBLNetworkPlugin.cpp +++ b/src/slic3r/Utils/BBLNetworkPlugin.cpp @@ -17,6 +17,20 @@ namespace Slic3r { #define BAMBU_SOURCE_LIBRARY "BambuSource" +namespace { + +// Named in the load log: the bound generation is what ties a crash report to an ABI choice. +// The label is the whitelist row's series, so it can never drift from the dispatch table. +const char* network_abi_name(NetworkAbi abi) +{ + for (size_t i = 0; i < AVAILABLE_NETWORK_VERSIONS_COUNT; ++i) + if (AVAILABLE_NETWORK_VERSIONS[i].abi == abi) + return AVAILABLE_NETWORK_VERSIONS[i].version; + return "unsupported"; +} + +} // namespace + // ============================================================================ // Singleton Implementation // ============================================================================ @@ -162,19 +176,20 @@ int BBLNetworkPlugin::initialize(bool using_backup, const std::string& version) // Load all function pointers load_all_function_pointers(); - // Sync legacy network flag from loaded plugin - m_use_legacy_network = is_legacy_version(version); + // Key the generation on the library that actually loaded, not the version asked for: + // resolve_library_path() serves any same-series build. m_get_version is read directly, not + // via get_version(), which would substitute the "00.00.00.00" sentinel and pick no generation. + const std::string loaded_version = m_get_version ? m_get_version() : std::string(); + m_network_abi = network_plugin_abi(loaded_version.empty() ? version : loaded_version); - std::string loaded_version; - if (m_get_version) { - loaded_version = m_get_version(); - if (!loaded_version.empty()) { - m_use_legacy_network = is_legacy_version(loaded_version); - } + // A library reporting a series this build has no ABI for stays loaded but uncallable - + // check_networking_version() then reports it as incompatible and offers the update flow. + if (m_network_abi == NetworkAbi::Unsupported) { + BOOST_LOG_TRIVIAL(warning) << "BBLNetworkPlugin::initialize: no ABI for version " + << (loaded_version.empty() ? version : loaded_version) << ", plug-in calls are disabled"; } - BOOST_LOG_TRIVIAL(info) << "BBLNetworkPlugin::initialize: legacy_mode=" - << (m_use_legacy_network ? "true" : "false") + BOOST_LOG_TRIVIAL(info) << "BBLNetworkPlugin::initialize: abi=" << network_abi_name(m_network_abi) << ", library=" << library << ", version=" << (loaded_version.empty() ? "unknown" : loaded_version) << ", send_message=" << (m_send_message ? "loaded" : "null") @@ -217,7 +232,9 @@ int BBLNetworkPlugin::unload() clear_all_function_pointers(); - m_use_legacy_network = false; + // Safe to reset only because every pointer was nulled just above and every dispatcher is + // guarded on a non-null pointer, so no stale generation is reachable. + m_network_abi = NetworkAbi::Unsupported; return 0; } @@ -520,7 +537,7 @@ void BBLNetworkPlugin::set_load_error(const std::string& message, } // ============================================================================ -// Legacy Helper +// ABI Conversion Helpers // ============================================================================ PrintParams_Legacy BBLNetworkPlugin::as_legacy(PrintParams& param) @@ -564,6 +581,57 @@ PrintParams_Legacy BBLNetworkPlugin::as_legacy(PrintParams& param) return l; } +// Every PrintParams field except the four the 02.08.01 series added +// (task_timelapse_use_internal, extruder_cali_manual_mode, svc_context, slicer_uid). +PrintParams_0203 BBLNetworkPlugin::as_0203(PrintParams& param) +{ + PrintParams_0203 p; + + p.dev_id = std::move(param.dev_id); + p.task_name = std::move(param.task_name); + p.project_name = std::move(param.project_name); + p.preset_name = std::move(param.preset_name); + p.filename = std::move(param.filename); + p.config_filename = std::move(param.config_filename); + p.plate_index = param.plate_index; + p.ftp_folder = std::move(param.ftp_folder); + p.ftp_file = std::move(param.ftp_file); + p.ftp_file_md5 = std::move(param.ftp_file_md5); + p.nozzle_mapping = std::move(param.nozzle_mapping); + p.ams_mapping = std::move(param.ams_mapping); + p.ams_mapping2 = std::move(param.ams_mapping2); + p.ams_mapping_info = std::move(param.ams_mapping_info); + p.nozzles_info = std::move(param.nozzles_info); + p.connection_type = std::move(param.connection_type); + p.comments = std::move(param.comments); + p.origin_profile_id = param.origin_profile_id; + p.stl_design_id = param.stl_design_id; + p.origin_model_id = std::move(param.origin_model_id); + p.print_type = std::move(param.print_type); + p.dst_file = std::move(param.dst_file); + p.dev_name = std::move(param.dev_name); + p.dev_ip = std::move(param.dev_ip); + p.use_ssl_for_ftp = param.use_ssl_for_ftp; + p.use_ssl_for_mqtt = param.use_ssl_for_mqtt; + p.username = std::move(param.username); + p.password = std::move(param.password); + p.task_bed_leveling = param.task_bed_leveling; + p.task_flow_cali = param.task_flow_cali; + p.task_vibration_cali = param.task_vibration_cali; + p.task_layer_inspect = param.task_layer_inspect; + p.task_record_timelapse = param.task_record_timelapse; + p.task_use_ams = param.task_use_ams; + p.task_bed_type = std::move(param.task_bed_type); + p.extra_options = std::move(param.extra_options); + p.auto_bed_leveling = param.auto_bed_leveling; + p.auto_flow_cali = param.auto_flow_cali; + p.auto_offset_cali = param.auto_offset_cali; + p.task_ext_change_assist = param.task_ext_change_assist; + p.try_emmc_print = param.try_emmc_print; + + return p; +} + // ============================================================================ // Function Pointer Loading // ============================================================================ @@ -669,7 +737,8 @@ void BBLNetworkPlugin::load_all_function_pointers() m_get_mw_user_preference = reinterpret_cast(get_function("bambu_network_get_mw_user_preference")); m_get_mw_user_4ulist = reinterpret_cast(get_function("bambu_network_get_mw_user_4ulist")); - // Added by the 02.08.01.52 plugin ABI; resolve to null on older plugins so callers no-op. + // Bound late; anything a generation does not export resolves to null so callers no-op. + // See the typedefs for which generation introduced each of these. m_set_on_user_login_fn = reinterpret_cast(get_function("bambu_network_set_on_user_login_fn")); m_get_studio_info_url = reinterpret_cast(get_function("bambu_network_get_studio_info_url")); m_report_consent = reinterpret_cast(get_function("bambu_network_report_consent")); diff --git a/src/slic3r/Utils/BBLNetworkPlugin.hpp b/src/slic3r/Utils/BBLNetworkPlugin.hpp index ed94b23f85..3788568e37 100644 --- a/src/slic3r/Utils/BBLNetworkPlugin.hpp +++ b/src/slic3r/Utils/BBLNetworkPlugin.hpp @@ -119,7 +119,7 @@ typedef int (*func_get_model_mall_rating_result)(void *agent, int job_id, std::s typedef int (*func_get_mw_user_preference)(void *agent, std::function callback); typedef int (*func_get_mw_user_4ulist)(void *agent, int seed, int limit, std::function callback); -// Legacy function pointer types (for older DLL versions) +// Legacy function pointer types (for the 01.10.01 DLL) typedef int (*func_start_print_legacy)(void *agent, PrintParams_Legacy params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn); typedef int (*func_start_local_print_with_record_legacy)(void *agent, PrintParams_Legacy params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn); typedef int (*func_start_send_gcode_to_sdcard_legacy)(void *agent, PrintParams_Legacy params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn); @@ -128,10 +128,25 @@ typedef int (*func_start_sdcard_print_legacy)(void* agent, PrintParams_Legacy pa typedef int (*func_send_message_legacy)(void* agent, std::string dev_id, std::string json_str, int qos); typedef int (*func_send_message_to_printer_legacy)(void* agent, std::string dev_id, std::string json_str, int qos); -// Added by the 02.08.01.52 plugin ABI (null on older plugins). +// 02.03.00 function pointer types. Only PrintParams differs from the current ABI; send_message +// and send_message_to_printer already take the flag argument in this series. +typedef int (*func_start_print_0203)(void *agent, PrintParams_0203 params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn); +typedef int (*func_start_local_print_with_record_0203)(void *agent, PrintParams_0203 params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn); +typedef int (*func_start_send_gcode_to_sdcard_0203)(void *agent, PrintParams_0203 params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn); +typedef int (*func_start_local_print_0203)(void *agent, PrintParams_0203 params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn); +typedef int (*func_start_sdcard_print_0203)(void* agent, PrintParams_0203 params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn); + +// bind() gained dev_model in 02.08.01; the legacy and 02.03.00 series share the older form. +typedef int (*func_bind_pre0208)(void *agent, std::string dev_ip, std::string dev_id, std::string sec_link, std::string timezone, bool improved, OnUpdateStatusFn update_fn); + +// Exported by every supported generation, but only bound here. typedef int (*func_set_on_user_login_fn)(void *agent, OnUserLoginFn fn); typedef std::string (*func_get_studio_info_url)(void *agent); + +// Present since 02.03.00, null on the legacy plugin. typedef int (*func_report_consent)(void *agent, std::string expand); + +// Added by the 02.08.01.52 plugin ABI (null on older plugins). typedef int (*func_get_camera_url_for_golive)(void *agent, std::string dev_id, std::string sdev_id, std::function callback); typedef int (*func_get_hms_snapshot)(void *agent, std::string& dev_id, std::string& file_name, std::function callback); typedef int (*func_get_filament_spools)(void *agent, FilamentQueryParams params, std::string* http_body); @@ -279,12 +294,13 @@ public: const std::string& attempted_path); // ======================================================================== - // Legacy Network Flag + // Plug-in ABI Generation // ======================================================================== static bool is_legacy_version(const std::string& version) { return version == BAMBU_NETWORK_AGENT_VERSION_LEGACY; } - bool use_legacy_network() const { return m_use_legacy_network; } - void set_use_legacy_network(bool legacy) { m_use_legacy_network = legacy; } + // The generation the loaded library speaks - what every call must dispatch on. + NetworkAbi network_abi() const { return m_network_abi; } + bool use_legacy_network() const { return m_network_abi == NetworkAbi::Legacy; } // ======================================================================== // Function Pointer Accessors @@ -401,10 +417,12 @@ public: func_sync_ams_filaments get_sync_ams_filaments() const { return m_sync_ams_filaments; } // ======================================================================== - // Legacy Helper + // ABI Conversion Helpers // ======================================================================== + // Both move out of `param`, so convert only inside the branch that will actually run. static PrintParams_Legacy as_legacy(PrintParams& param); + static PrintParams_0203 as_0203(PrintParams& param); private: // Singleton instance pointer (heap-allocated for explicit lifetime control) @@ -431,8 +449,8 @@ private: // Load error state NetworkLibraryLoadError m_load_error; - // Legacy network compatibility flag - bool m_use_legacy_network{false}; + // ABI generation of the currently loaded library + NetworkAbi m_network_abi{NetworkAbi::Unsupported}; // Function pointers func_check_debug_consistent m_check_debug_consistent{nullptr}; diff --git a/src/slic3r/Utils/BBLPrinterAgent.cpp b/src/slic3r/Utils/BBLPrinterAgent.cpp index 57f763487c..ef85e0a1ff 100644 --- a/src/slic3r/Utils/BBLPrinterAgent.cpp +++ b/src/slic3r/Utils/BBLPrinterAgent.cpp @@ -26,11 +26,19 @@ int BBLPrinterAgent::send_message(std::string dev_id, std::string json_str, int auto agent = plugin.get_agent(); auto func = plugin.get_send_message(); if (func && agent) { - if (plugin.use_legacy_network()) { + // Only the legacy plug-in lacks `flag`; 02.03.00 already takes it, and routing that + // series through the legacy form would silently drop MessageFlag sign/encrypt. + switch (plugin.network_abi()) { + case NetworkAbi::Legacy: { auto legacy_func = reinterpret_cast(func); - return legacy_func(agent, dev_id, json_str, qos); + return legacy_func(agent, std::move(dev_id), std::move(json_str), qos); + } + case NetworkAbi::V0203: + case NetworkAbi::Current: + return func(agent, std::move(dev_id), std::move(json_str), qos, flag); + default: + return -1; } - return func(agent, dev_id, json_str, qos, flag); } return -1; } @@ -63,11 +71,17 @@ int BBLPrinterAgent::send_message_to_printer(std::string dev_id, std::string jso auto agent = plugin.get_agent(); auto func = plugin.get_send_message_to_printer(); if (func && agent) { - if (plugin.use_legacy_network()) { + switch (plugin.network_abi()) { + case NetworkAbi::Legacy: { auto legacy_func = reinterpret_cast(func); - return legacy_func(agent, dev_id, json_str, qos); + return legacy_func(agent, std::move(dev_id), std::move(json_str), qos); + } + case NetworkAbi::V0203: + case NetworkAbi::Current: + return func(agent, std::move(dev_id), std::move(json_str), qos, flag); + default: + return -1; } - return func(agent, dev_id, json_str, qos, flag); } return -1; } @@ -144,7 +158,19 @@ int BBLPrinterAgent::bind(std::string dev_ip, std::string dev_id, std::string de auto agent = plugin.get_agent(); auto func = plugin.get_bind(); if (func && agent) { - return func(agent, dev_ip, dev_id, dev_model, sec_link, timezone, improved, update_fn); + // dev_model was added in 02.08.01. Passing it to a plug-in that takes the 7-argument + // form shifts every following argument, so the older generations get the older call. + switch (plugin.network_abi()) { + case NetworkAbi::Legacy: + case NetworkAbi::V0203: { + auto older_func = reinterpret_cast(func); + return older_func(agent, dev_ip, dev_id, sec_link, timezone, improved, update_fn); + } + case NetworkAbi::Current: + return func(agent, dev_ip, dev_id, dev_model, sec_link, timezone, improved, update_fn); + default: + return -1; + } } return -1; } @@ -281,84 +307,62 @@ AgentInfo BBLPrinterAgent::get_agent_info_static() // Print Job Operations // ============================================================================ -int BBLPrinterAgent::start_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn) +namespace { + +// Shared dispatcher for the start_* operations, whose params layout differs per generation. +// The per-generation typedefs are template arguments so a swapped pair fails to compile +// (each arm's converted params must match the casted signature). Each arm converts and calls +// in one step: as_legacy()/as_0203() move out of `params` and their prvalue result lands in +// the by-value ABI argument without another copy; the Current arm moves `params` outright. +template +int dispatch_start(CurrentFn func, PrintParams& params, const CallbackFns&... callbacks) { auto& plugin = BBLNetworkPlugin::instance(); auto agent = plugin.get_agent(); - auto func = plugin.get_start_print(); - if (func && agent) { - if (plugin.use_legacy_network()) { - auto legacy_func = reinterpret_cast(func); - auto legacy_params = BBLNetworkPlugin::as_legacy(params); - return legacy_func(agent, legacy_params, update_fn, cancel_fn, wait_fn); - } - return func(agent, params, update_fn, cancel_fn, wait_fn); + if (!func || !agent) + return -1; + switch (plugin.network_abi()) { + case NetworkAbi::Legacy: + return reinterpret_cast(func)(agent, BBLNetworkPlugin::as_legacy(params), callbacks...); + case NetworkAbi::V0203: + return reinterpret_cast(func)(agent, BBLNetworkPlugin::as_0203(params), callbacks...); + case NetworkAbi::Current: + return func(agent, std::move(params), callbacks...); + default: + return -1; } - return -1; +} + +} // namespace + +int BBLPrinterAgent::start_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn) +{ + return dispatch_start( + BBLNetworkPlugin::instance().get_start_print(), params, update_fn, cancel_fn, wait_fn); } int BBLPrinterAgent::start_local_print_with_record(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn) { - auto& plugin = BBLNetworkPlugin::instance(); - auto agent = plugin.get_agent(); - auto func = plugin.get_start_local_print_with_record(); - if (func && agent) { - if (plugin.use_legacy_network()) { - auto legacy_func = reinterpret_cast(func); - auto legacy_params = BBLNetworkPlugin::as_legacy(params); - return legacy_func(agent, legacy_params, update_fn, cancel_fn, wait_fn); - } - return func(agent, params, update_fn, cancel_fn, wait_fn); - } - return -1; + return dispatch_start( + BBLNetworkPlugin::instance().get_start_local_print_with_record(), params, update_fn, cancel_fn, wait_fn); } int BBLPrinterAgent::start_send_gcode_to_sdcard(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn, OnWaitFn wait_fn) { - auto& plugin = BBLNetworkPlugin::instance(); - auto agent = plugin.get_agent(); - auto func = plugin.get_start_send_gcode_to_sdcard(); - if (func && agent) { - if (plugin.use_legacy_network()) { - auto legacy_func = reinterpret_cast(func); - auto legacy_params = BBLNetworkPlugin::as_legacy(params); - return legacy_func(agent, legacy_params, update_fn, cancel_fn, wait_fn); - } - return func(agent, params, update_fn, cancel_fn, wait_fn); - } - return -1; + return dispatch_start( + BBLNetworkPlugin::instance().get_start_send_gcode_to_sdcard(), params, update_fn, cancel_fn, wait_fn); } int BBLPrinterAgent::start_local_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn) { - auto& plugin = BBLNetworkPlugin::instance(); - auto agent = plugin.get_agent(); - auto func = plugin.get_start_local_print(); - if (func && agent) { - if (plugin.use_legacy_network()) { - auto legacy_func = reinterpret_cast(func); - auto legacy_params = BBLNetworkPlugin::as_legacy(params); - return legacy_func(agent, legacy_params, update_fn, cancel_fn); - } - return func(agent, params, update_fn, cancel_fn); - } - return -1; + return dispatch_start( + BBLNetworkPlugin::instance().get_start_local_print(), params, update_fn, cancel_fn); } int BBLPrinterAgent::start_sdcard_print(PrintParams params, OnUpdateStatusFn update_fn, WasCancelledFn cancel_fn) { - auto& plugin = BBLNetworkPlugin::instance(); - auto agent = plugin.get_agent(); - auto func = plugin.get_start_sdcard_print(); - if (func && agent) { - if (plugin.use_legacy_network()) { - auto legacy_func = reinterpret_cast(func); - auto legacy_params = BBLNetworkPlugin::as_legacy(params); - return legacy_func(agent, legacy_params, update_fn, cancel_fn); - } - return func(agent, params, update_fn, cancel_fn); - } - return -1; + return dispatch_start( + BBLNetworkPlugin::instance().get_start_sdcard_print(), params, update_fn, cancel_fn); } // ============================================================================ diff --git a/src/slic3r/Utils/bambu_networking.hpp b/src/slic3r/Utils/bambu_networking.hpp index 1093392423..c514daffff 100644 --- a/src/slic3r/Utils/bambu_networking.hpp +++ b/src/slic3r/Utils/bambu_networking.hpp @@ -230,6 +230,59 @@ struct PrintParams_Legacy { std::string extra_options; }; +/* print job, as the 02.03.00 series expects it. The 02.08.01 series inserted + task_timelapse_use_internal, extruder_cali_manual_mode, svc_context and slicer_uid into + PrintParams; two of them sit mid-struct, so an older plug-in misreads every field from + task_use_ams onwards if handed the current layout. Rebuild it with as_0203() instead. */ +struct PrintParams_0203 { + /* basic info */ + std::string dev_id; + std::string task_name; + std::string project_name; + std::string preset_name; + std::string filename; + std::string config_filename; + int plate_index; + std::string ftp_folder; + std::string ftp_file; + std::string ftp_file_md5; + std::string nozzle_mapping; + std::string ams_mapping; + std::string ams_mapping2; + std::string ams_mapping_info; + std::string nozzles_info; + std::string connection_type; + std::string comments; + int origin_profile_id = 0; + int stl_design_id = 0; + std::string origin_model_id; + std::string print_type; + std::string dst_file; + std::string dev_name; + + /* access options */ + std::string dev_ip; + bool use_ssl_for_ftp; + bool use_ssl_for_mqtt; + std::string username; + std::string password; + + /*user options */ + bool task_bed_leveling; /* bed leveling of task */ + bool task_flow_cali; /* flow calibration of task */ + bool task_vibration_cali; /* vibration calibration of task */ + bool task_layer_inspect; /* first layer inspection of task */ + bool task_record_timelapse; /* record timelapse of task */ + bool task_use_ams; + std::string task_bed_type; + std::string extra_options; + int auto_bed_leveling{ 0 }; + int auto_flow_cali{ 0 }; + int auto_offset_cali{ 0 }; + bool task_ext_change_assist; + bool try_emmc_print; +}; + /* print job*/ struct PrintParams { /* basic info */ @@ -351,21 +404,34 @@ struct CertificateInformation { std::string serial_number; }; +// The plug-in ABI generation a library speaks. Generations differ in by-value struct layouts and +// function signatures, so a call must go through the matching typedefs (see BBLPrinterAgent) - +// the wrong one corrupts the stack rather than failing cleanly. +enum class NetworkAbi { + Unsupported, // no generation in this build can call it - never dispatch through it + Legacy, // 01.10.01: PrintParams_Legacy; send_message/send_message_to_printer take no flag + V0203, // 02.03.00: PrintParams_0203; bind takes no dev_model + Current, // 02.08.01: the layouts and signatures this build declares directly +}; + struct NetworkLibraryVersion { const char* version; const char* display_name; const char* url_override; bool is_latest; const char* warning; + NetworkAbi abi; }; -// Only the latest series and the legacy build are offered/loadable: the host binds the -// modern ABI (by-value struct layouts, function signatures) of exactly one series, plus -// a dedicated shim for the legacy build. Older 02.0x series expect different layouts -// and must not be loaded - see is_supported_network_version(). +// Every row names the generation that can call it, so a series can never be offered without a +// host-side ABI for it. Series with no generation - 02.01.01, 02.00.02 and older - must stay out; +// is_supported_network_version() is the gate that keeps them from loading. static const NetworkLibraryVersion AVAILABLE_NETWORK_VERSIONS[] = { - {"02.08.01", "02.08.01", nullptr, true, nullptr}, - {BAMBU_NETWORK_AGENT_VERSION_LEGACY, BAMBU_NETWORK_AGENT_VERSION_LEGACY " (legacy)", nullptr, false, nullptr}, + {"02.08.01", "02.08.01", nullptr, true, nullptr, NetworkAbi::Current}, + {"02.03.00", "02.03.00", nullptr, false, + "An older plug-in series. Features that need newer plug-in support, such as print-failure " + "snapshots in the device error dialog, are unavailable.", NetworkAbi::V0203}, + {BAMBU_NETWORK_AGENT_VERSION_LEGACY, BAMBU_NETWORK_AGENT_VERSION_LEGACY " (legacy)", nullptr, false, nullptr, NetworkAbi::Legacy}, }; static const size_t AVAILABLE_NETWORK_VERSIONS_COUNT = sizeof(AVAILABLE_NETWORK_VERSIONS) / sizeof(AVAILABLE_NETWORK_VERSIONS[0]); @@ -378,23 +444,43 @@ inline const char* get_latest_network_version() { return AVAILABLE_NETWORK_VERSIONS[0].version; } -// True when the version can be loaded through the ABI this build was compiled against: -// an exact whitelist entry, or a build from the same AA.BB.CC series as a non-legacy -// whitelist entry (the plugin ABI is stable within a series, and the OTA sync only ever -// installs same-series updates). Anything else - in particular older 02.0x series a -// previous Orca release whitelisted - expects different by-value struct layouts and -// function signatures and must not be loaded. -inline bool is_supported_network_version(const std::string& version) { +// The AA.BB.CC series of a modern version string - the plug-in's stored identity. The 4th +// component is only which build of the series happens to be installed and is read live from +// the loaded plug-in for display. Legacy keeps its exact string (the shim matches exactly). +inline std::string network_plugin_series(const std::string& version) { + if (version.empty() || version == BAMBU_NETWORK_AGENT_VERSION_LEGACY) + return version; + return version.size() >= 8 ? version.substr(0, 8) : version; +} + +// Index of the whitelist entry that can load this version: an exact match, or a build of the same +// AA.BB.CC series as a non-legacy entry (the ABI is stable within a series, and the OTA sync only +// installs same-series updates). Legacy matches exactly only - a sibling build of that series +// would come through the modern layout. AVAILABLE_NETWORK_VERSIONS_COUNT when nothing matches. +inline size_t find_network_version_index(const std::string& version) { + const std::string series = network_plugin_series(version); for (size_t i = 0; i < AVAILABLE_NETWORK_VERSIONS_COUNT; ++i) { const std::string base = AVAILABLE_NETWORK_VERSIONS[i].version; if (version == base) - return true; + return i; if (base == BAMBU_NETWORK_AGENT_VERSION_LEGACY) continue; - if (version.size() >= 8 && base.size() >= 8 && version.compare(0, 8, base, 0, 8) == 0) - return true; + if (series == base) + return i; } - return false; + return AVAILABLE_NETWORK_VERSIONS_COUNT; +} + +// True when a whitelisted series can load the version through an ABI this build implements. +inline bool is_supported_network_version(const std::string& version) { + return find_network_version_index(version) < AVAILABLE_NETWORK_VERSIONS_COUNT; +} + +// The generation to call a loaded library through. Unsupported for anything the load gate rejects, +// so a mislabelled library reaches no plug-in call instead of a layout it does not share. +inline NetworkAbi network_plugin_abi(const std::string& version) { + const size_t i = find_network_version_index(version); + return i < AVAILABLE_NETWORK_VERSIONS_COUNT ? AVAILABLE_NETWORK_VERSIONS[i].abi : NetworkAbi::Unsupported; } struct NetworkLibraryVersionInfo { @@ -441,15 +527,6 @@ inline std::string extract_suffix(const std::string& full_version) { return (pos == std::string::npos) ? "" : full_version.substr(pos + 1); } -// The AA.BB.CC series of a modern version string - the plug-in's stored identity. The 4th -// component is only which build of the series happens to be installed and is read live from -// the loaded plug-in for display. Legacy keeps its exact string (the shim matches exactly). -inline std::string network_plugin_series(const std::string& version) { - if (version.empty() || version == BAMBU_NETWORK_AGENT_VERSION_LEGACY) - return version; - return version.size() >= 8 ? version.substr(0, 8) : version; -} - // True when the version is a pure dotted-numeric build (AA.BB.CC or AA.BB.CC.DD) whose identity // collapses to its series - the managed/OTA build. Legacy and any custom-named build // (02.08.01_custom, 02.08.01.52-dev) are NOT managed: they are genuinely distinct files kept diff --git a/tests/slic3rutils/test_network_versions.cpp b/tests/slic3rutils/test_network_versions.cpp index af8a639025..efe8b5d831 100644 --- a/tests/slic3rutils/test_network_versions.cpp +++ b/tests/slic3rutils/test_network_versions.cpp @@ -85,7 +85,8 @@ TEST_CASE_METHOD(PluginFolderFixture, "Managed builds fold into the series; cust { add_plugin("02.08.01.55"); // managed, same series -> folded into the 02.08.01 row add_plugin("02.09.00.10"); // managed, unknown series -> not listed - add_plugin("02.03.00.62"); // managed, series no longer whitelisted -> not listed + add_plugin("02.03.00.62"); // managed, older whitelisted series -> folded into 02.03.00 + add_plugin("02.01.01.52"); // managed, series with no ABI in this build -> not listed add_plugin("02.08.01_custom"); // custom, whitelisted series -> listed under it add_plugin("02.08.01.52-dev"); // custom (dash-suffixed), whitelisted series -> listed @@ -96,17 +97,24 @@ TEST_CASE_METHOD(PluginFolderFixture, "Managed builds fold into the series; cust REQUIRE(count_version(versions, "02.08.01") == 1); REQUIRE(count_version(versions, "02.09.00.10") == 0); REQUIRE(count_version(versions, "02.03.00.62") == 0); + REQUIRE(count_version(versions, "02.03.00") == 1); + REQUIRE(count_version(versions, "02.01.01.52") == 0); // Custom-named builds are distinct files kept under their own name. REQUIRE(count_version(versions, "02.08.01_custom") == 1); REQUIRE(count_version(versions, "02.08.01.52-dev") == 1); // Newest series first, its customs nested under it (suffix sort: "" < ".52-dev" < "_custom"), - // legacy last. + // then older series, legacy last. REQUIRE(versions[0].version == "02.08.01"); REQUIRE(versions[1].version == "02.08.01.52-dev"); REQUIRE(versions[2].version == "02.08.01_custom"); + REQUIRE(versions[3].version == "02.03.00"); REQUIRE(versions.back().version == BAMBU_NETWORK_AGENT_VERSION_LEGACY); + // An older whitelisted series is a flat row of its own, and never holds "(Latest)". + REQUIRE(versions[3].suffix.empty()); + REQUIRE_FALSE(versions[3].is_latest); + // Customs sort/render nested under their series (non-empty suffix, base = the series). REQUIRE(versions[1].base_version == "02.08.01"); REQUIRE_FALSE(versions[1].suffix.empty()); @@ -137,6 +145,16 @@ TEST_CASE_METHOD(PluginFolderFixture, "Only the loaded series is marked installe REQUIRE(marked == 1); } + // An older series is marked the same way, and never bleeds onto the latest row. + { + add_plugin("02.03.00.62"); + auto versions = get_all_available_versions("02.03.00.62"); + int marked = 0; + for (const auto& info : versions) + if (info.is_loaded) { ++marked; REQUIRE(info.version == "02.03.00"); } + REQUIRE(marked == 1); + } + // A loaded custom build matches its own row, never the bare series. { auto versions = get_all_available_versions("02.08.01_custom"); @@ -153,19 +171,25 @@ TEST_CASE_METHOD(PluginFolderFixture, "Only the loaded series is marked installe TEST_CASE("Only whitelisted series pass the load gate", "[NetworkVersions]") { - // The whitelisted series, its builds, and custom-named builds of that series. + // Each whitelisted series, its builds, and custom-named builds of that series. REQUIRE(is_supported_network_version("02.08.01")); REQUIRE(is_supported_network_version("02.08.01.52")); REQUIRE(is_supported_network_version("02.08.01.55")); REQUIRE(is_supported_network_version("02.08.01_custom")); REQUIRE(is_supported_network_version("02.08.01.52-dev")); + REQUIRE(is_supported_network_version("02.03.00")); + REQUIRE(is_supported_network_version("02.03.00.62")); + REQUIRE(is_supported_network_version("02.03.00.70")); + REQUIRE(is_supported_network_version("02.03.00_custom")); REQUIRE(is_supported_network_version(BAMBU_NETWORK_AGENT_VERSION_LEGACY)); - // Series whitelisted by previous Orca releases - their ABI no longer matches. - REQUIRE_FALSE(is_supported_network_version("02.03.00.62")); + // Series whitelisted by previous Orca releases that no generation here can call. REQUIRE_FALSE(is_supported_network_version("02.01.01.52")); REQUIRE_FALSE(is_supported_network_version("02.00.02.50")); + // A neighbouring series of a whitelisted one is still its own ABI. + REQUIRE_FALSE(is_supported_network_version("02.03.01.51")); + // Unknown series, legacy siblings, and malformed values. REQUIRE_FALSE(is_supported_network_version("02.09.00.10")); std::string legacy = BAMBU_NETWORK_AGENT_VERSION_LEGACY; @@ -175,6 +199,30 @@ TEST_CASE("Only whitelisted series pass the load gate", "[NetworkVersions]") REQUIRE_FALSE(is_supported_network_version("02.08")); } +TEST_CASE("Each version resolves to the ABI generation that can call it", "[NetworkVersions]") +{ + // The generation is keyed on the series, so every build of a series - including the + // custom-named ones - resolves to the same one. + CHECK(network_plugin_abi("02.08.01") == NetworkAbi::Current); + CHECK(network_plugin_abi("02.08.01.55") == NetworkAbi::Current); + CHECK(network_plugin_abi("02.08.01.52-dev") == NetworkAbi::Current); + CHECK(network_plugin_abi("02.03.00") == NetworkAbi::V0203); + CHECK(network_plugin_abi("02.03.00.62") == NetworkAbi::V0203); + CHECK(network_plugin_abi("02.03.00_custom") == NetworkAbi::V0203); + CHECK(network_plugin_abi(BAMBU_NETWORK_AGENT_VERSION_LEGACY) == NetworkAbi::Legacy); + + // Anything the load gate rejects must dispatch through nothing at all, rather than + // defaulting to a layout it does not share. + CHECK(network_plugin_abi("02.01.01.52") == NetworkAbi::Unsupported); + CHECK(network_plugin_abi("02.00.02.50") == NetworkAbi::Unsupported); + CHECK(network_plugin_abi("02.09.00.10") == NetworkAbi::Unsupported); + CHECK(network_plugin_abi("") == NetworkAbi::Unsupported); + + // A series may only be offered once the dispatch layer implements its generation. + for (size_t i = 0; i < AVAILABLE_NETWORK_VERSIONS_COUNT; ++i) + CHECK(AVAILABLE_NETWORK_VERSIONS[i].abi != NetworkAbi::Unsupported); +} + TEST_CASE_METHOD(PluginFolderFixture, "Legacy series never adopts discovered builds", "[NetworkVersions]") { // A different build of the legacy series must not be surfaced: is_legacy_version() From 306d4b73efd3a09bcadd141fccdff503c11d7060 Mon Sep 17 00:00:00 2001 From: Kris Austin Date: Sun, 26 Jul 2026 10:53:56 -0500 Subject: [PATCH 7/8] test: stop littering the working directory with debug files (#14785) --- tests/fff_print/test_helpers.cpp | 11 --- tests/libslic3r/test_3mf.cpp | 9 +- tests/libslic3r/test_hollowing.cpp | 4 +- tests/libslic3r/test_indexed_triangle_set.cpp | 11 ++- tests/libslic3r/test_marchingsquares.cpp | 50 ++++------ tests/sla_print/sla_print_tests.cpp | 2 +- tests/sla_print/sla_supptgen_tests.cpp | 10 +- tests/sla_print/sla_test_utils.cpp | 31 +++--- tests/test_utils.hpp | 99 +++++++++++++++++++ 9 files changed, 156 insertions(+), 71 deletions(-) diff --git a/tests/fff_print/test_helpers.cpp b/tests/fff_print/test_helpers.cpp index 47d21e5aad..64493d5f74 100644 --- a/tests/fff_print/test_helpers.cpp +++ b/tests/fff_print/test_helpers.cpp @@ -491,17 +491,6 @@ SCENARIO("init_print functionality", "[test_helpers]") { THEN("Export gcode functions outputs text.") { REQUIRE(! Slic3r::Test::gcode(print).empty()); } -#if 0 - THEN("Embedded meshes exported") { - std::string path = "C:\\data\\temp\\embedded_meshes\\"; - for (auto kvp : Slic3r::Test::mesh_names) { - Slic3r::TriangleMesh m = mesh(kvp.first); - std::string name = kvp.second; - REQUIRE(Slic3r::store_stl((path + name + ".stl").c_str(), &m, true) == true); - REQUIRE(Slic3r::store_obj((path + name + ".obj").c_str(), &m) == true); - } - } -#endif } } } diff --git a/tests/libslic3r/test_3mf.cpp b/tests/libslic3r/test_3mf.cpp index 7a692f949f..1a082cd8e0 100644 --- a/tests/libslic3r/test_3mf.cpp +++ b/tests/libslic3r/test_3mf.cpp @@ -9,6 +9,8 @@ #include "libslic3r/MultiNozzleUtils.hpp" #include "libslic3r/ProjectTask.hpp" +#include "test_utils.hpp" + #include #include @@ -109,8 +111,8 @@ SCENARIO("Export+Import geometry to/from 3mf file cycle", "[3mf]") { src_object->instances.front()->set_transformation(src_instance_transform); WHEN("model is saved+loaded to/from 3mf file") { - // save the model to 3mf file - std::string test_file = std::string(TEST_DATA_DIR) + "/test_3mf/prusa.3mf"; + ScopedTemporaryFile temp(".3mf"); + const std::string test_file = temp.string(); store_3mf(test_file.c_str(), &src_model, nullptr, false); // load back the model from the 3mf file @@ -120,7 +122,6 @@ SCENARIO("Export+Import geometry to/from 3mf file cycle", "[3mf]") { ConfigSubstitutionContext ctxt{ ForwardCompatibilitySubstitutionRule::Disable }; load_3mf(test_file.c_str(), dst_config, ctxt, &dst_model, false); } - boost::filesystem::remove(test_file); // compare meshes TriangleMesh src_mesh = src_model.mesh(); @@ -522,7 +523,7 @@ SCENARIO("2D convex hull of sinking object", "[3mf][.]") { object->center_around_origin(false); // This outputs the same exact data as the Prusaslicer test - object->volumes[0]->mesh().write_ascii("/tmp/orca.ascii"); + write_debug_stl("3mf/orca.ascii", object->volumes[0]->mesh()); // set instance's attitude so that it is rotated, scaled (and sinking? how is it sinking? the rotation? does it matter if it's sinking?) ModelInstance* instance = object->instances[0]; diff --git a/tests/libslic3r/test_hollowing.cpp b/tests/libslic3r/test_hollowing.cpp index bea399a05e..dc39b47373 100644 --- a/tests/libslic3r/test_hollowing.cpp +++ b/tests/libslic3r/test_hollowing.cpp @@ -4,6 +4,8 @@ #include "libslic3r/SLA/Hollowing.hpp" +#include "test_utils.hpp" + TEST_CASE("Hollow two overlapping spheres") { using namespace Slic3r; @@ -16,6 +18,6 @@ TEST_CASE("Hollow two overlapping spheres") { sla::hollow_mesh(sphere1, sla::HollowingConfig{}, sla::HollowingFlags::hfRemoveInsideTriangles); - sphere1.WriteOBJFile("twospheres.obj"); + write_debug_obj("hollowing/twospheres.obj", sphere1); } diff --git a/tests/libslic3r/test_indexed_triangle_set.cpp b/tests/libslic3r/test_indexed_triangle_set.cpp index f8ef508f6e..43116936d3 100644 --- a/tests/libslic3r/test_indexed_triangle_set.cpp +++ b/tests/libslic3r/test_indexed_triangle_set.cpp @@ -5,6 +5,8 @@ #include "libslic3r/TriangleMesh.hpp" +#include "test_utils.hpp" + using namespace Slic3r; TEST_CASE("Split empty mesh", "[its_split][its]") { @@ -29,13 +31,15 @@ TEST_CASE("Split simple mesh consisting of one part", "[its_split][its]") { REQUIRE(res.front().vertices.size() == cube.vertices.size()); } +// Dump each split part as its own OBJ for eyeballing; no-op in release. void debug_write_obj(const std::vector &res, const std::string &name) { #ifndef NDEBUG size_t part_idx = 0; - for (auto &part_its : res) { - its_write_obj(part_its, (name + std::to_string(part_idx++) + ".obj").c_str()); - } + for (const auto &part_its : res) + write_debug_obj("indexed_triangle_set/" + name + std::to_string(part_idx++) + ".obj", part_its); +#else + (void) res; (void) name; #endif } @@ -260,7 +264,6 @@ TEST_CASE("Reduce one edge by Quadric Edge Collapse", "[its]") CHECK(is_similar(its_, its, cfg)); } -#include "test_utils.hpp" TEST_CASE("Simplify mesh by Quadric edge collapse to 5%", "[its]") { TriangleMesh mesh = load_model("frog_legs.obj"); diff --git a/tests/libslic3r/test_marchingsquares.cpp b/tests/libslic3r/test_marchingsquares.cpp index 4768db5f7b..6844ecb6ac 100644 --- a/tests/libslic3r/test_marchingsquares.cpp +++ b/tests/libslic3r/test_marchingsquares.cpp @@ -191,22 +191,21 @@ static void test_expolys(Rst&& rst, const ExPolygons& ref, Vec2i32 window, const for (const ExPolygon& expoly : ref) rst.draw(expoly); - std::fstream out(name + ".png", std::ios::out); - out << rst.encode(sla::PNGRasterEncoder{}); - out.close(); + write_debug_stream("marchingsquares/" + name + ".png", + [&] { return rst.encode(sla::PNGRasterEncoder{}); }); const ExPolygons bmp = rstGetPolys(rst); const ExPolygons ext = sla::raster_to_polygons(rst, window); - SVG svg(name + ".svg", raster_bb); - svg.draw(bmp, "green"); - if (pixel_size.x() >= scale_(0.5)) - svg.draw_grid(raster_bb, "grey", scale_(0.05), pixel_size.x()); - if (window_size.x() >= scale_(1.0)) - svg.draw_grid(raster_bb, "grey", scale_(0.10), window_size.x()); - svg.draw_outline(ref, "red", "red", scale_(0.3)); - svg.draw_outline(ext, "blue", "blue"); - svg.Close(); + write_debug_svg("marchingsquares/" + name + ".svg", raster_bb, [&](SVG &svg) { + svg.draw(bmp, "green"); + if (pixel_size.x() >= scale_(0.5)) + svg.draw_grid(raster_bb, "grey", scale_(0.05), pixel_size.x()); + if (window_size.x() >= scale_(1.0)) + svg.draw_grid(raster_bb, "grey", scale_(0.10), window_size.x()); + svg.draw_outline(ref, "red", "red", scale_(0.3)); + svg.draw_outline(ext, "blue", "blue"); + }); // Note all these areas are unscaled back to mm^2. double raster_area = unscaled(unscaled(area(bmp))); @@ -432,9 +431,7 @@ static void recreate_object_from_rasters(const std::string& objname, float lh) double disp_w = 120.96; double disp_h = 68.04; -#ifndef NDEBUG size_t cntr = 0; -#endif for (ExPolygons& layer : layers) { auto rst = create_raster(res, disp_w, disp_h); @@ -442,11 +439,8 @@ static void recreate_object_from_rasters(const std::string& objname, float lh) rst.draw(island); } -#ifndef NDEBUG - std::fstream out(objname + std::to_string(cntr) + ".png", std::ios::out); - out << rst.encode(sla::PNGRasterEncoder{}); - out.close(); -#endif + write_debug_stream("marchingsquares/" + objname + std::to_string(cntr) + ".png", + [&] { return rst.encode(sla::PNGRasterEncoder{}); }); ExPolygons layer_ = sla::raster_to_polygons(rst); // float delta = scaled(std::min(rst.pixel_dimensions().h_mm, @@ -454,21 +448,19 @@ static void recreate_object_from_rasters(const std::string& objname, float lh) // layer_ = expolygons_simplify(layer_, delta); -#ifndef NDEBUG - SVG svg(objname + std::to_string(cntr) + ".svg", rstBBox(rst)); - svg.draw(layer_); - svg.draw(layer, "green"); - svg.Close(); -#endif + write_debug_svg("marchingsquares/" + objname + std::to_string(cntr) + ".svg", rstBBox(rst), + [&](SVG &svg) { + svg.draw(layer_); + svg.draw(layer, "green"); + }); double layera = 0., layera_ = 0.; for (auto& p : layer) layera += p.area(); for (auto& p : layer_) layera_ += p.area(); -#ifndef NDEBUG - std::cout << cntr++ << std::endl; -#endif + ++cntr; + double diff = std::abs(layera_ - layera); REQUIRE((diff <= 0.1 * layera || diff < scaled(1.) * scaled(1.))); @@ -477,7 +469,7 @@ static void recreate_object_from_rasters(const std::string& objname, float lh) indexed_triangle_set out = slices_to_mesh(layers, bb.min.z(), double(lh), double(lh)); - its_write_obj(out, "out_from_rasters.obj"); + write_debug_obj("marchingsquares/out_from_rasters.obj", out); } TEST_CASE("Recreate object from rasters", "[SL1Import]") { recreate_object_from_rasters("frog_legs.obj", 0.05f); } diff --git a/tests/sla_print/sla_print_tests.cpp b/tests/sla_print/sla_print_tests.cpp index 269f5ed7b3..308925a163 100644 --- a/tests/sla_print/sla_print_tests.cpp +++ b/tests/sla_print/sla_print_tests.cpp @@ -229,7 +229,7 @@ TEST_CASE("halfcone test", "[halfcone]") { indexed_triangle_set m = sla::get_mesh(br, 45); its_merge_vertices(m); - its_write_obj(m, "Halfcone.obj"); + write_debug_obj("sla_print/Halfcone.obj", m); } TEST_CASE("Test concurrency") diff --git a/tests/sla_print/sla_supptgen_tests.cpp b/tests/sla_print/sla_supptgen_tests.cpp index 458b2a7686..32f049d78e 100644 --- a/tests/sla_print/sla_supptgen_tests.cpp +++ b/tests/sla_print/sla_supptgen_tests.cpp @@ -13,7 +13,7 @@ TEST_CASE("Overhanging point should be supported", "[SupGen]") { // Pyramid with 45 deg slope TriangleMesh mesh = make_pyramid(10.f, 10.f); mesh.rotate_y(float(PI)); - mesh.WriteOBJFile("Pyramid.obj"); + write_debug_obj("sla_supptgen/Pyramid.obj", mesh); sla::SupportPoints pts = calc_support_pts(mesh); @@ -55,7 +55,7 @@ TEST_CASE("Overhanging horizontal surface should be supported", "[SupGen]") { TriangleMesh mesh = make_cube(width, depth, height); mesh.translate(0., 0., 5.); // lift up - mesh.WriteOBJFile("Cuboid.obj"); + write_debug_obj("sla_supptgen/Cuboid.obj", mesh); sla::SupportPointGenerator::Config cfg; sla::SupportPoints pts = calc_support_pts(mesh, cfg); @@ -81,7 +81,7 @@ TEST_CASE("Overhanging edge should be supported", "[SupGen]") { TriangleMesh mesh = make_prism(width, depth, height); mesh.rotate_y(float(PI)); // rotate on its back mesh.translate(0., 0., height); - mesh.WriteOBJFile("Prism.obj"); + write_debug_obj("sla_supptgen/Prism.obj", mesh); sla::SupportPointGenerator::Config cfg; sla::SupportPoints pts = calc_support_pts(mesh, cfg); @@ -106,7 +106,7 @@ TEST_CASE("Hollowed cube should be supported from the inside", "[SupGen][Hollowe hollow_mesh(mesh, HollowingConfig{}); - mesh.WriteOBJFile("cube_hollowed.obj"); + write_debug_obj("sla_supptgen/cube_hollowed.obj", mesh); auto bb = mesh.bounding_box(); auto h = float(bb.max.z() - bb.min.z()); @@ -129,7 +129,7 @@ TEST_CASE("Two parallel plates should be supported", "[SupGen][Hollowed]") mesh_high.translate(0., 0., 10.); // lift up mesh.merge(mesh_high); - mesh.WriteOBJFile("parallel_plates.obj"); + write_debug_obj("sla_supptgen/parallel_plates.obj", mesh); sla::SupportPointGenerator::Config cfg; sla::SupportPoints pts = calc_support_pts(mesh, cfg); diff --git a/tests/sla_print/sla_test_utils.cpp b/tests/sla_print/sla_test_utils.cpp index 1bd4238408..a74bad09e4 100644 --- a/tests/sla_print/sla_test_utils.cpp +++ b/tests/sla_print/sla_test_utils.cpp @@ -47,8 +47,9 @@ void test_support_model_collision(const std::string &obj_filename, notouch = notouch && area(intersections) < PI * pinhead_r * pinhead_r; } - /*if (!notouch) */export_failed_case(support_slices, byproducts); - + if (!notouch) + export_failed_case(support_slices, byproducts); + REQUIRE(notouch); } @@ -62,11 +63,11 @@ void export_failed_case(const std::vector &support_slices, const Sup std::stringstream ss; if (!intersections.empty()) { ss << byproducts.obj_fname << std::setprecision(4) << n << ".svg"; - SVG svg(ss.str()); - svg.draw(sup_slice, "green"); - svg.draw(mod_slice, "blue"); - svg.draw(intersections, "red"); - svg.Close(); + write_debug_svg("sla/" + ss.str(), [&](SVG &svg) { + svg.draw(sup_slice, "green"); + svg.draw(mod_slice, "blue"); + svg.draw(intersections, "red"); + }); } } @@ -74,8 +75,8 @@ void export_failed_case(const std::vector &support_slices, const Sup byproducts.supporttree.retrieve_full_mesh(its); TriangleMesh m{its}; m.merge(byproducts.input_mesh); - m.WriteOBJFile((Catch::getResultCapture().getCurrentTestName() + "_" + - byproducts.obj_fname).c_str()); + write_debug_obj("sla/" + Catch::getResultCapture().getCurrentTestName() + + "_" + byproducts.obj_fname, m); } void test_supports(const std::string &obj_filename, @@ -350,13 +351,11 @@ void check_raster_transformations(sla::RasterBase::Orientation o, sla::RasterBas REQUIRE((w < res.width_px && h < res.height_px)); auto px = raster.read_pixel(w, h); - - if (px != FullWhite) { - std::fstream outf("out.png", std::ios::out); - - outf << raster.encode(sla::PNGRasterEncoder()); - } - + + if (px != FullWhite) + write_debug_stream("sla/raster_transform_mismatch.png", + [&] { return raster.encode(sla::PNGRasterEncoder()); }); + REQUIRE(px == FullWhite); } diff --git a/tests/test_utils.hpp b/tests/test_utils.hpp index 7fe55c5333..d928f2f41e 100644 --- a/tests/test_utils.hpp +++ b/tests/test_utils.hpp @@ -3,9 +3,14 @@ #include #include +#include #include +#include +#include +#include + #if defined(WIN32) || defined(_WIN32) #define PATH_SEPARATOR R"(\)" #else @@ -44,4 +49,98 @@ private: boost::filesystem::path m_path; }; +// --------------------------------------------------------------------------- +// Debug-only test artifacts +// +// Files a test dumps for inspection: a mesh, an SVG, or any streamable blob such +// as a PNG. In debug builds each run writes to a fresh temp folder (path printed +// once); the name may include a subfolder (e.g. "marchingsquares/foo.svg"). +// --------------------------------------------------------------------------- + +// Maps name to a path under the run's temp folder, creating any parent dirs +// (forward slashes work on Windows). Not gated, so only call it from a +// write_debug_* helper or inside an #ifndef NDEBUG block. +inline std::string debug_artifact_path(const std::string &name) +{ + static const boost::filesystem::path root = [] { + boost::filesystem::path dir = boost::filesystem::temp_directory_path() + / boost::filesystem::unique_path("orca-test-artifacts-%%%%-%%%%"); + boost::filesystem::create_directories(dir); + std::printf("Debug test artifacts will be written to %s\n", dir.string().c_str()); + return dir; + }(); + boost::filesystem::path full = root / name; + boost::filesystem::create_directories(full.parent_path()); + return full.string(); +} + +// Dump a mesh as OBJ. +inline void write_debug_obj(const std::string &name, const Slic3r::TriangleMesh &mesh) +{ +#ifndef NDEBUG + mesh.WriteOBJFile(debug_artifact_path(name).c_str()); +#else + (void) name; (void) mesh; +#endif +} + +inline void write_debug_obj(const std::string &name, const indexed_triangle_set &its) +{ +#ifndef NDEBUG + its_write_obj(its, debug_artifact_path(name).c_str()); +#else + (void) name; (void) its; +#endif +} + +// Dump a mesh as ASCII STL. +inline void write_debug_stl(const std::string &name, const Slic3r::TriangleMesh &mesh) +{ +#ifndef NDEBUG + mesh.write_ascii(debug_artifact_path(name).c_str()); +#else + (void) name; (void) mesh; +#endif +} + +// Draw an SVG artifact through a callback that receives the open SVG. Second +// overload takes a BoundingBox when the drawing needs one. +template +inline void write_debug_svg(const std::string &name, Draw &&draw) +{ +#ifndef NDEBUG + Slic3r::SVG svg(debug_artifact_path(name)); + draw(svg); + svg.Close(); +#else + (void) name; (void) draw; +#endif +} + +template +inline void write_debug_svg(const std::string &name, const Slic3r::BoundingBox &bbox, Draw &&draw) +{ +#ifndef NDEBUG + Slic3r::SVG svg(debug_artifact_path(name), bbox); + draw(svg); + svg.Close(); +#else + (void) name; (void) bbox; (void) draw; +#endif +} + +// Write a callback's result (e.g. raster.encode(sla::PNGRasterEncoder{})) to an +// artifact. operator<< is resolved by ADL at the call site, so this header needn't +// include the producer's headers. +template +inline void write_debug_stream(const std::string &name, Produce &&produce) +{ +#ifndef NDEBUG + std::ofstream out(debug_artifact_path(name), std::ios::out | std::ios::binary); + out << produce(); +#else + (void) name; (void) produce; +#endif +} + #endif // SLIC3R_TEST_UTILS From ed86efcf56888aa09d34e2c20e8d04136a897d53 Mon Sep 17 00:00:00 2001 From: Alexandre Folle de Menezes Date: Sun, 26 Jul 2026 12:59:24 -0300 Subject: [PATCH 8/8] Improve and complement pt-BR translation (#14963) --- localization/i18n/pt_BR/OrcaSlicer_pt_BR.po | 583 +++++++------------- 1 file changed, 208 insertions(+), 375 deletions(-) diff --git a/localization/i18n/pt_BR/OrcaSlicer_pt_BR.po b/localization/i18n/pt_BR/OrcaSlicer_pt_BR.po index 5cb96cbd96..ba106097be 100644 --- a/localization/i18n/pt_BR/OrcaSlicer_pt_BR.po +++ b/localization/i18n/pt_BR/OrcaSlicer_pt_BR.po @@ -4,7 +4,7 @@ msgstr "" "Project-Id-Version: Orca Slicer\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2026-07-23 15:24-0300\n" -"PO-Revision-Date: 2026-07-22 20:39-0300\n" +"PO-Revision-Date: 2026-07-26 11:14-0300\n" "Last-Translator: Alexandre Folle de Menezes\n" "Language-Team: Portuguese, Brazilian\n" "Language: pt_BR\n" @@ -555,7 +555,7 @@ msgstr "Reiniciar" msgctxt "Keyboard Shortcut" msgid "Enter" -msgstr "" +msgstr "Enter" msgid "Shortcut Key " msgstr "Tecla de atalho " @@ -886,7 +886,7 @@ msgstr "Selecionar todos os conectores" msgctxt "Cut tool" msgid "Cut" -msgstr "" +msgstr "Cortar" msgid "Rotate cut plane" msgstr "Rotacionar plano de corte" @@ -1749,14 +1749,14 @@ msgstr "Selecionar ponto" msgctxt "Keyboard Shortcut" msgid "Delete" -msgstr "" +msgstr "Delete" msgid "Restart selection" msgstr "Reiniciar seleção" msgctxt "Keyboard Shortcut" msgid "Esc" -msgstr "" +msgstr "Esc" msgid "Cancel a feature until exit" msgstr "Cancelar um recurso até sair" @@ -1863,7 +1863,7 @@ msgstr "Saindo do gizmo de Medida" msgctxt "Assembly tool" msgid "Assemble" -msgstr "" +msgstr "Montar" msgid "Please confirm explosion ratio = 1 and select at least two volumes." msgstr "Por favor, confirme a taxa de explosão = 1 e selecione pelo menos dois volumes." @@ -2538,14 +2538,14 @@ msgstr "Mostrar" msgctxt "Keyboard Shortcut" msgid "Del" -msgstr "" +msgstr "Del" msgid "Delete the selected object" msgstr "Apagar o objeto selecionado" msgctxt "Keyboard Shortcut" msgid "Backspace" -msgstr "" +msgstr "Backspace" msgid "Load..." msgstr "Carregar…" @@ -3516,7 +3516,6 @@ msgstr "Aquecer o bico" msgid "Cut filament" msgstr "Cortar filamento" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Pull back the current filament" msgstr "Retirar o filamento atual" @@ -3536,13 +3535,13 @@ msgid "Check filament location" msgstr "Verificar localização do filamento" msgid "Switch" -msgstr "" +msgstr "Alternar" msgid "hotend" msgstr "hotend" msgid "Wait for AMS cooling" -msgstr "" +msgstr "Aguardar resfriamento do AMS" msgid "Switch current filament at Filament Track Switch" msgstr "" @@ -3605,7 +3604,7 @@ msgid "Launch troubleshoot center" msgstr "Abrir a central de solução de problemas" msgid "Set nozzle count" -msgstr "" +msgstr "Definir contagem de bicos" msgid "Please set nozzle count" msgstr "Por favor, defina a contagem de bicos" @@ -3686,9 +3685,8 @@ msgstr "Organizando" msgid "Arranging canceled." msgstr "Organização cancelada." -# TODO: Review, changed by lang refactor. PR 14254 msgid "Arranging complete, but some items were not able to be arranged. Reduce spacing and try again." -msgstr "A organização foi concluída, mas há itens desembalados. Reduza o espaçamento e tente novamente." +msgstr "A organização foi concluída, mas há itens que não podem ser arranjados. Reduza o espaçamento e tente novamente." msgid "Arranging done." msgstr "Organização concluída." @@ -3754,7 +3752,6 @@ msgstr "Falha no login" msgid "Please check the printer network connection." msgstr "Por favor, verifique a conexão de rede da impressora." -# TODO: Review, changed by lang refactor. PR 14254 msgid "Abnormal print file data: please slice again." msgstr "Dados de arquivo de impressão anormais. Por favor, fatie novamente." @@ -3767,7 +3764,6 @@ msgstr "O tempo para envio da tarefa expirou. Verifique o estado da rede e tente msgid "Cloud service connection failed. Please try again." msgstr "Falha na conexão com o serviço de nuvem. Por favor, tente novamente." -# TODO: Review, changed by lang refactor. PR 14254 msgid "Print file not found; please slice again." msgstr "Arquivo de impressão não encontrado. Por favor, fatie novamente." @@ -3780,18 +3776,15 @@ msgstr "Falha ao enviar o trabalho de impressão. Por favor, tente novamente." msgid "Failed to upload file to ftp. Please try again." msgstr "Falha ao enviar o arquivo via FTP. Por favor, tente novamente." -# TODO: Review, changed by lang refactor. PR 14254 msgid "Check the current status of the Bambu Lab server by clicking on the link above." -msgstr "Verifique o estado atual do servidor Bambu clicando no link acima." +msgstr "Verifique o estado atual do servidor Bambu Lab clicando no link acima." msgid "The size of the print file is too large. Please adjust the file size and try again." msgstr "O tamanho do arquivo de impressão é muito grande. Por favor, ajuste o tamanho do arquivo e tente novamente." -# TODO: Review, changed by lang refactor. PR 14254 msgid "Print file not found; please slice it again and send it for printing." msgstr "Arquivo de impressão não encontrado. Por favor, fatie-o novamente e envie para impressão." -# TODO: Review, changed by lang refactor. PR 14254 msgid "Failed to upload print file via FTP. Please check the network status and try again." msgstr "Falha ao enviar o arquivo de impressão via FTP. Por favor, verifique o estado da rede e tente novamente." @@ -3843,7 +3836,6 @@ msgstr "Ocorreu um erro desconhecido no estado do Armazenamento. Tente novamente msgid "Sending G-code file over LAN" msgstr "Enviando arquivo de G-code via LAN" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Sending G-code file to SD card" msgstr "Enviando arquivo de G-code para o cartão SD" @@ -3914,7 +3906,6 @@ msgstr "Tempo restante: %dmin%ds" msgid "Importing SLA archive" msgstr "Importando arquivo SLA" -# TODO: Review, changed by lang refactor. PR 14254 msgid "The SLA archive doesn't contain any presets. Please activate some SLA printer presets first before importing that SLA archive." msgstr "O arquivo SLA não contém nenhuma predefinição. Por favor, ative algumas predefinições de impressora SLA antes de importar esse arquivo SLA." @@ -3927,7 +3918,6 @@ msgstr "Importação concluída." msgid "The imported SLA archive did not contain any presets. The current SLA presets were used as fallback." msgstr "O arquivo SLA importado não contém nenhuma predefinição. As predefinições SLA atuais foram usadas como alternativa." -# TODO: Review, changed by lang refactor. PR 14254 msgid "You cannot load an SLA project with a multi-part object on the bed" msgstr "Você não pode carregar um projeto SLA com um objeto muilti-peças na mesa" @@ -4169,7 +4159,7 @@ msgstr "Escolha entre os seguintes filamentos" #, c-format, boost-format msgid "Select filament that installed to the %s" -msgstr "" +msgstr "Selecionar filamento instalado em %s" msgid "Left AMS" msgstr "AMS Esquerdo" @@ -4214,7 +4204,6 @@ msgstr "Desativar AMS" msgid "Print with filament on external spool" msgstr "Imprimir com filamento no carretel externo" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Please change the desiccant when it is too wet. The indicator may not represent accurately in following cases: when the lid is open or the desiccant pack is changed. It takes a few hours to absorb the moisture, and low temperatures also slow down the process." msgstr "Por favor, mude o dessecante quando estiver muito molhado. O indicador pode não representar com precisão nos casos a seguir: quando a tampa está aberta ou quando o dessecante é trocado. Leva algumas horas para absorver a umidade, e baixas temperaturas também atrasam o processo." @@ -4236,17 +4225,16 @@ msgstr "Não ativar AMS" msgid "Print using filament on external spool." msgstr "Imprimir usando filamento no carretel externo." -# TODO: Review, changed by lang refactor. PR 14254 msgid "Print with filament in AMS" -msgstr "Imprimir com filamentos no AMS" +msgstr "Imprimir com filamento no AMS" msgctxt "Nozzle position" msgid "Left" -msgstr "" +msgstr "Esquerda" msgctxt "Nozzle position" msgid "Right" -msgstr "" +msgstr "Direita" msgid "When the current material run out, the printer will continue to print in the following order." msgstr "Quando o material atual acabar, a impressora continuará a imprimir na seguinte ordem." @@ -4263,7 +4251,6 @@ msgstr "Quando o material atual acabar, a impressora usará um filamento idênti msgid "The printer does not currently support auto refill." msgstr "A impressora atualmente não suporta recarga automática." -# TODO: Review, changed by lang refactor. PR 14254 msgid "AMS filament backup is not enabled; please enable it in the AMS settings." msgstr "O backup de filamento do AMS não está ativado, por favor ative-o nas configurações do AMS." @@ -4286,7 +4273,6 @@ msgstr "Configurações do AMS" msgid "Insertion update" msgstr "Atualização de inserção" -# TODO: Review, changed by lang refactor. PR 14254 msgid "The AMS will automatically read the filament information when inserting a new Bambu Lab filament spool. This takes about 20 seconds." msgstr "O AMS irá ler automaticamente as informações do filamento ao inserir um novo carretel de filamento da Bambu Lab. Isso leva cerca de 20 segundos." @@ -4296,11 +4282,9 @@ msgstr "Nota: se um novo filamento for inserido durante a impressão, o AMS não msgid "When inserting a new filament, the AMS will not automatically read its information, leaving it blank for you to enter manually." msgstr "Ao inserir um novo filamento, o AMS não irá ler automaticamente suas informações, deixando-o em branco para você inserir manualmente." -# TODO: Review, changed by lang refactor. PR 14254 msgid "Update on startup" -msgstr "Atualização de inicialização" +msgstr "Atualizar na inicialização" -# TODO: Review, changed by lang refactor. PR 14254 msgid "The AMS will automatically read the information of inserted filament on start-up. It will take about 1 minute. The reading process will rotate the filament spools." msgstr "O AMS irá ler automaticamente as informações do filamento inserido na inicialização. Levará cerca de 1 minuto. O processo de leitura irá girar os carretéis de filamento." @@ -4355,7 +4339,6 @@ msgstr "Calibração" msgid "Failed to download the plug-in. Please check your firewall settings and VPN software and retry." msgstr "Falha ao baixar o plug-in. Verifique as configurações do seu firewall e software VPN e tente novamente." -# TODO: Review, changed by lang refactor. PR 14254 msgid "Failed to install the plug-in. The plug-in file may be in use. Please restart OrcaSlicer and try again. Also check whether it is blocked or has been deleted by anti-virus software." msgstr "Falha ao instalar o plug-in. O plug-in pode estar em uso. Reinicie o OrcaSlicer e tente novamente. Também verifique se ele está bloqueado ou excluído pelo software antivírus." @@ -4384,13 +4367,11 @@ msgstr "Ocorreu um erro. O sistema pode ter esgotado a memória, ou um bug pode msgid "A fatal error occurred: \"%1%\"" msgstr "Ocorreu um erro fatal: \"%1%\"" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Please save your project and restart the application." -msgstr "Por favor, salve o projeto e reinicie o programa." +msgstr "Por favor, salve seu projeto e reinicie a aplicação." -# TODO: Review, changed by lang refactor. PR 14254 msgid "Processing G-Code from previous file…" -msgstr "Processando G-code do arquivo anterior…" +msgstr "Processando o G-code do arquivo anterior…" msgid "Slicing complete" msgstr "Fatiamento concluído" @@ -4512,16 +4493,14 @@ msgstr "Escolha um arquivo STL para importar a forma da mesa:" msgid "Invalid file format." msgstr "Formato de arquivo inválido." -# TODO: Review, changed by lang refactor. PR 14254 msgid "Error: invalid model" -msgstr "Erro! Modelo inválido" +msgstr "Erro: modelo inválido" msgid "The selected file contains no geometry." msgstr "O arquivo selecionado não contém geometria." -# TODO: Review, changed by lang refactor. PR 14254 msgid "The selected file contains several disjointed areas. This is not supported." -msgstr "O arquivo selecionado contém várias áreas disjuntas. Isso não é suportado." +msgstr "O arquivo selecionado contém várias áreas desconexas. Isso não é suportado." msgid "Choose a file to import bed texture from (PNG/SVG):" msgstr "Escolha um arquivo para importar a textura da mesa (PNG/SVG):" @@ -5014,10 +4993,10 @@ msgid "Abort" msgstr "Abortar" msgid "Disable Purification for This Print" -msgstr "" +msgstr "Desativar Purificação para Esta Impressão" msgid "Don't Remind Me" -msgstr "" +msgstr "Não me Lembrar" msgid "Retry" msgstr "Tentar Novamente" @@ -5160,7 +5139,7 @@ msgid "N/A" msgstr "N/D" msgid "System agents" -msgstr "" +msgstr "Agentes do sistema" msgid "No plugin selected" msgstr "Nenhum plugin selecionado" @@ -5546,7 +5525,7 @@ msgstr "Qualidade / Velocidade" msgctxt "Mesh action" msgid "Smooth" -msgstr "" +msgstr "Suavizar" msgid "Radius" msgstr "Raio" @@ -5606,18 +5585,15 @@ msgstr "Bico esquerdo: X:%1%-%2%, Y:%3%-%4%, Z:%5%-%6%\n" msgid "Right nozzle: X:%1%-%2%, Y:%3%-%4%, Z:%5%-%6%" msgstr "Bico direito: X:%1%-%2%, Y:%3%-%4%, Z:%5%-%6%" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Tool move" -msgstr "Mover Ferramenta" +msgstr "Mover ferramenta" msgid "Tool Rotate" msgstr "Rotacionar Ferramenta" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Move object" -msgstr "Mover Objeto" +msgstr "Mover objeto" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Auto orientation options" msgstr "Opções de Orientação Automática" @@ -5759,7 +5735,6 @@ msgstr "Taxa de Explosão" msgid "Section View" msgstr "Vista de Seção" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Assembly Control" msgstr "Controle de Montagem" @@ -5788,9 +5763,8 @@ msgstr "Um objeto está sobre a borda da placa." msgid "A G-code path goes beyond the max print height." msgstr "Um caminho de G-code ultrapassa a altura máxima de impressão." -# TODO: Review, changed by lang refactor. PR 14254 msgid "A G-code path goes beyond plate boundaries." -msgstr "Um caminho de G-code ultrapassa a borda da placa." +msgstr "Um caminho de G-code ultrapassa os limites da placa." msgid "Not support printing 2 or more TPU filaments." msgstr "Não suporta a impressão de 2 ou mais filamentos de TPU." @@ -5849,7 +5823,6 @@ msgstr "Seleção de etapa de calibração" msgid "Micro lidar calibration" msgstr "Calibração do micro lidar" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Bed leveling" msgstr "Nivelamento da mesa" @@ -5937,9 +5910,8 @@ msgstr "Nova Janela" msgid "Open a new window" msgstr "Abrir uma nova janela" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Closing application" -msgstr "Aplicativo está fechando" +msgstr "Fechando o aplicativo" msgid "Closing Application while some presets are modified." msgstr "Fechando o aplicativo enquanto algumas predefinições são modificadas." @@ -6184,9 +6156,8 @@ msgstr "Colar" msgid "Paste clipboard" msgstr "Colar da área de transferência" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Delete Selected" -msgstr "Excluir seleção" +msgstr "Excluir Seleção" msgid "Deletes the current selection" msgstr "Exclui a seleção atual" @@ -6194,9 +6165,8 @@ msgstr "Exclui a seleção atual" msgid "Deletes all objects" msgstr "Exclui todos os objetos" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Clone Selected" -msgstr "Clonar selecionado" +msgstr "Clonar Seleção" msgid "Clone copies of selections" msgstr "Clonar cópias das seleções" @@ -6272,7 +6242,7 @@ msgstr "Preferências" msgctxt "Menu" msgid "Edit" -msgstr "" +msgstr "Editar" msgid "View" msgstr "Visualizar" @@ -6371,15 +6341,13 @@ msgstr "&Visualizar" msgid "&Help" msgstr "&Ajuda" -# TODO: Review, changed by lang refactor. PR 14254 #, c-format, boost-format msgid "A file exists with the same name: %s. Do you want to overwrite it?" -msgstr "Existe um arquivo com o mesmo nome: %s, deseja sobrescrevê-lo?" +msgstr "Existe um arquivo com o mesmo nome: %s. Quer sobrescrevê-lo?" -# TODO: Review, changed by lang refactor. PR 14254 #, c-format, boost-format msgid "A config exists with the same name: %s. Do you want to overwrite it?" -msgstr "Existe uma configuração com o mesmo nome: %s, deseja sobrescrevê-la?" +msgstr "Existe uma configuração com o mesmo nome: %s. Quer sobrescrevê-la?" msgid "Overwrite file" msgstr "Sobrescrever arquivo" @@ -6402,9 +6370,8 @@ msgid_plural "There are %d configs exported. (Only non-system configs)" msgstr[0] "Foi exportada uma configuração (%d). (Apenas configurações não do sistema)" msgstr[1] "Foram exportadas %d configurações. (Apenas configurações não do sistema)" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Export Result" -msgstr "Resultado da exportação" +msgstr "Resultado da Exportação" msgid "Select profile to load:" msgstr "Selecione o perfil para carregar:" @@ -6456,7 +6423,6 @@ msgstr "O dispositivo não pode lidar com mais conversas. Por favor, tente novam msgid "Player is malfunctioning. Please reinstall the system player." msgstr "O reprodutor está com problemas. Por favor, reinstale o reprodutor do sistema." -# TODO: Review, changed by lang refactor. PR 14254 msgid "The player is not loaded; please click the \"play\" button to retry." msgstr "O reprodutor não está carregado, por favor clique no botão \"Reproduzir\" para tentar novamente." @@ -6478,7 +6444,6 @@ msgstr "Ocorreu um problema. Por favor, atualize o firmware da impressora e tent msgid "LAN Only Liveview is off. Please turn on the liveview on printer screen." msgstr "Liveview via LAN está desativado. Por favor, ative a liveview na tela da impressora." -# TODO: Review, changed by lang refactor. PR 14254 msgid "Please enter the IP of the printer to connect." msgstr "Por favor, digite o IP da impressora para conectar." @@ -7367,16 +7332,16 @@ msgid "" msgstr "" msgid "There is stringing-prone filament in the current print job. Enabling nozzle clumping detection now may degrade print quality. Are you sure you want to enable it?" -msgstr "" +msgstr "O trabalho de impressão atual utiliza um filamento propenso a criar fios. Ativar a detecção de acúmulo de material no bico agora pode comprometer a qualidade da impressão. Tem certeza de que deseja ativá-la?" msgid "Enable Nozzle Clumping Detection" -msgstr "" +msgstr "Ativar Detecção de Aglomeração no Bico" msgid "When enabled, the printer will automatically capture photos of printed parts and upload them to the cloud. Would you like to enable this option?" -msgstr "" +msgstr "Quando ativada, a impressora capturará automaticamente fotos das peças impressas e as enviará para a nuvem. Gostaria de ativar essa opção?" msgid "Confirm Enable Print Status Snapshot" -msgstr "" +msgstr "Confirmar a ativação do instantâneo do status de impressão" msgid "Enable detection of build plate position" msgstr "Ativar detecção da posição da placa de impressão" @@ -7391,19 +7356,19 @@ msgid "Identifies the type and position of the build plate on the heatbed. Pausi msgstr "Identifica o tipo e a posição da placa de impressão na mesa aquecida. Interrompe a impressão se for detectada alguma incompatibilidade." msgid "Purifies the chamber air as the print finishes, based on the selected mode." -msgstr "" +msgstr "Purifica o ar da câmara ao finalizar a impressão, com base no modo selecionado." msgid "Purifies the chamber air through internal circulation as each print finishes." -msgstr "" +msgstr "Purifica o ar da câmara por meio de circulação interna ao final de cada impressão." msgid "Automatically match the corresponding switch strategy for leak-prone filaments (disable blob detection) and regular filaments (enable blob detection)." -msgstr "" +msgstr "Aplique automaticamente a estratégia de alternância correspondente para filamentos propensos a vazamentos (desativar detecção de acúmulo) e filamentos comuns (ativar detecção de acúmulo)." msgid "Detect whether the nozzle is wrapped by filament or other foreign matter." -msgstr "" +msgstr "Detecte se o bico está envolto por filamento ou outro material estranho." msgid "After disabling, nozzle wrapping cannot be detected, which may lead to print failure or nozzle damage." -msgstr "" +msgstr "Após a desativação, o acúmulo de material no bico não poderá ser detectado, o que pode levar a falhas de impressão ou danos ao bico." msgid "AI Detections" msgstr "Detecções de AI" @@ -7460,31 +7425,31 @@ msgid "Check if the nozzle is clumping by filament or other foreign objects." msgstr "Verifica se o bico está com filamento acumulado ou outros objetos estranhos." msgid "Purify Air at Print End" -msgstr "" +msgstr "Purificar o Ar ao Final da Impressão" msgid "Internal Circulation" msgstr "Circulação Interna" msgid "Alignment Detection" -msgstr "" +msgstr "Detecção de Alinhamento" msgid "Pauses printing when build plate misalignment is detected." -msgstr "" +msgstr "Pausa a impressão quando é detectado desalinhamento da placa de impressão." msgid "Foreign Object Detection" -msgstr "" +msgstr "Detecção de Corpos Estranhos" msgid "Checks for any objects on the build plate at the start of a print to avoid collisions." -msgstr "" +msgstr "Verifica se há objetos na placa de impressão no início da impressão para evitar colisões." msgid "Printed Part Displacement Detection" -msgstr "" +msgstr "Detecção de Deslocamento de Peças Impressas" msgid "Monitors the printed part during printing and alerts immediately if it shifts or collapses." -msgstr "" +msgstr "Monitora a peça impressa durante a impressão e emite um alerta imediato caso ela se desloque ou colapse." msgid "Checks if the nozzle is clumping by filament or other foreign objects." -msgstr "" +msgstr "Verifica se o bico está obstruído por aglomerados de filamento ou outros corpos estranhos." msgid "On" msgstr "Ligado" @@ -7499,10 +7464,10 @@ msgid "Pause printing" msgstr "Pausar impressão" msgid "Print Status Snapshot" -msgstr "" +msgstr "Instantâneo do Status de Impressão" msgid "Automatically capture and upload print photos, showing defects during printing and the final result for remote viewing." -msgstr "" +msgstr "Captura e envia automaticamente fotos da impressão, exibindo defeitos durante o processo e o resultado final para visualização remota." msgctxt "Nozzle Type" msgid "Type" @@ -7526,7 +7491,7 @@ msgid "High flow" msgstr "Alto fluxo" msgid "TPU High flow" -msgstr "" +msgstr "TPU de Alto Fluxo" msgid "No wiki link available for this printer." msgstr "Não há link wiki disponível para esta impressora." @@ -7694,7 +7659,7 @@ msgid "Project Filaments" msgstr "Filamentos do Projeto" msgid "Purge mode" -msgstr "" +msgstr "Modo de purga" msgid "Flushing volumes" msgstr "Volumes de Purga" @@ -7752,18 +7717,15 @@ msgstr "Desmontado com sucesso. O dispositivo %s (%s) agora pode ser removido co msgid "Ejecting of device %s (%s) has failed." msgstr "A ejeção do dispositivo %s (%s) falhou." -# TODO: Review, changed by lang refactor. PR 14254 msgid "Previously unsaved items have been detected. Do you want to restore them?" -msgstr "Projeto não salvo anterior detectado, deseja restaurá-lo?" +msgstr "Itens previamente não salvos foram detectados. Deseja restaurá-los?" msgid "Restore" msgstr "Restaurar" -# TODO: Review, changed by lang refactor. PR 14254 msgid "The current heatbed temperature is relatively high. The nozzle may clog when printing this filament in a closed environment. Please open the front door and/or remove the upper glass." -msgstr "A temperatura atual da mesa aquecida está relativamente alta. O bico pode ficar obstruído ao imprimir este filamento em um compartimento fechado. Por favor, abra a porta frontal e/ou remova o vidro superior." +msgstr "A temperatura atual da mesa aquecida está relativamente alta. O bico pode entupir ao imprimir este filamento em um ambiente fechado. Por favor, abra a porta frontal e/ou remova o vidro superior." -# TODO: Review, changed by lang refactor. PR 14254 msgid "The nozzle hardness required by the filament is higher than the default nozzle hardness of the printer. Please replace the hardened nozzle or filament, otherwise, the nozzle will be worn down or damaged." msgstr "A dureza do bico necessária para o filamento é maior do que a dureza padrão do bico da impressora. Por favor substitua o bico endurecido ou o filamento, caso contrário o bico será desgastado ou danificado." @@ -7814,9 +7776,8 @@ msgstr "Você gostaria que o OrcaSlicer corrigisse isso automaticamente limpando msgid "The 3MF file version %s is newer than %s's version %s, found the following unrecognized keys:" msgstr "A versão do 3MF %s é mais recente do que a versão do %s %s, encontradas as seguintes chaves não reconhecidas:" -# TODO: Review, changed by lang refactor. PR 14254 msgid "You should update your software.\n" -msgstr "Será melhor atualizar o seu software.\n" +msgstr "Você deveria atualizar o seu software.\n" #, c-format, boost-format msgid "The 3MF file version %s is newer than %s's version %s, we suggest to upgrade your software." @@ -7836,9 +7797,8 @@ msgstr "O arquivo 3MF foi criado pelo Bambu Studio. Algumas configurações pode msgid "Invalid values found in the 3MF:" msgstr "Valores inválidos encontrados no 3MF:" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Please correct them in the Param tabs" -msgstr "Por favor, corrija-os nas guias de parâmetros" +msgstr "Por favor, corrija-os nas guias de Parâmetros" msgid "The 3MF has the following modified G-code in filament or printer presets:" msgstr "O 3MF tem os seguintes G-code modificados em predefinições de filamento ou impressora:" @@ -7858,13 +7818,11 @@ msgstr "Por favor, confirme se o G-code dentro dessas predefinições é seguro msgid "Customized Preset" msgstr "Predefinição Personalizada" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Component name(s) inside step file not in UTF8 format!" -msgstr "O nome dos componentes dentro do arquivo STEP não está no formato UTF-8!" +msgstr "Os nomes dos componentes dentro do arquivo STEP não estão no formato UTF-8!" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Because of unsupported text encoding, garbage characters may appear!" -msgstr "O nome pode exibir caracteres inválidos!" +msgstr "Devido à codificação não suportada, podem aparecer caracteres inválidos!" msgid "Remember my choice." msgstr "Lembrar minha escolha." @@ -7879,27 +7837,25 @@ msgstr "Objetos com volume zero removidos" msgid "The volume of the object is zero" msgstr "O volume do objeto é zero" -# TODO: Review, changed by lang refactor. PR 14254 #, c-format, boost-format msgid "" "The object from file %s is too small, and may be in meters or inches.\n" " Do you want to scale to millimeters?" msgstr "" -"O objeto do arquivo %s é muito pequeno e pode estar em metros ou polegadas.\n" -"Deseja dimensioná-lo para milímetros?" +"O objeto do arquivo %s é muito pequeno, e pode estar em metros ou polegadas.\n" +"Deseja redimensioná-lo para milímetros?" msgid "Object too small" msgstr "Objeto muito pequeno" -# TODO: Review, changed by lang refactor. PR 14254 msgid "" "This file contains several objects positioned at multiple heights.\n" "Instead of considering them as multiple objects, should \n" "the file be loaded as a single object with multiple parts?" msgstr "" "Este arquivo contém vários objetos posicionados em alturas múltiplas.\n" -"Em vez de considerá-los como múltiplos objetos, o arquivo deve ser carregado\n" -"como um único objeto com múltiplas peças?" +"Em vez de considerá-los como múltiplos objetos, o arquivo deve ser\n" +"carregado como um único objeto com múltiplas peças?" msgid "Multi-part object detected" msgstr "Objeto multi-peça detectado" @@ -7907,9 +7863,8 @@ msgstr "Objeto multi-peça detectado" msgid "Load these files as a single object with multiple parts?\n" msgstr "Carregar esses arquivos como um único objeto com múltiplas peças?\n" -# TODO: Review, changed by lang refactor. PR 14254 msgid "An object with multiple parts was detected" -msgstr "Objeto com múltiplas peças foi detectado" +msgstr "Um objeto com múltiplas peças foi detectado" msgid "Auto-Drop" msgstr "" @@ -7945,13 +7900,12 @@ msgstr "Salvar arquivo como" msgid "Export OBJ file:" msgstr "Exportar arquivo OBJ:" -# TODO: Review, changed by lang refactor. PR 14254 #, c-format, boost-format msgid "" "The file %s already exists.\n" "Do you want to replace it?" msgstr "" -"O arquivo %s já existe\n" +"O arquivo %s já existe.\n" "Deseja substituí-lo?" msgid "Confirm Save As" @@ -7960,24 +7914,23 @@ msgstr "Confirmar Salvar Como" msgid "Delete object which is a part of cut object" msgstr "Excluir objeto que é uma peça do objeto cortado" -# TODO: Review, changed by lang refactor. PR 14254 msgid "" "You are trying to delete an object which is a part of a cut object.\n" "This action will break a cut correspondence.\n" "After that, model consistency can't be guaranteed." msgstr "" -"Você está tentando excluir um objeto que é peça de um objeto cortado.\n" +"Você está tentando excluir um objeto que é parte de um objeto cortado.\n" "Essa ação quebrará uma correspondência de corte.\n" "Após isso, a consistência do modelo não pode ser garantida." msgid "Delete Object" -msgstr "" +msgstr "Excluir Objeto" msgid "Delete All Objects" -msgstr "" +msgstr "Excluir Todos os Objetos" msgid "Reset Project" -msgstr "" +msgstr "Redefinir Projeto" msgid "The selected object couldn't be split." msgstr "O objeto selecionado não pôde ser dividido." @@ -7997,7 +7950,6 @@ msgstr "Outro trabalho de exportação está em execução." msgid "Unable to replace with more than one volume" msgstr "Não é possível substituir por mais de um volume" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Error during replacement" msgstr "Erro durante a substituição" @@ -8007,12 +7959,11 @@ msgstr "Substituir de:" msgid "Select a new file" msgstr "Selecione um novo arquivo" -# TODO: Review, changed by lang refactor. PR 14254 msgid "File for the replacement wasn't selected" msgstr "O arquivo para a substituição não foi selecionado" msgid "Replace with 3D file" -msgstr "" +msgstr "Substituir com arquivo 3D" msgid "Select folder to replace from" msgstr "Selecione a pasta origem da substituição" @@ -8950,7 +8901,7 @@ msgid "Displays current viewport FPS in the top-right corner." msgstr "Exibe o FPS atual da janela de visualização no canto superior direito." msgid "G-code Preview" -msgstr "" +msgstr "Pré-visualização de G-code" msgid "Dim lower layers" msgstr "" @@ -9251,7 +9202,7 @@ msgstr "A predefinição selecionada é nula!" msgctxt "Layer range" msgid "End" -msgstr "" +msgstr "Final" msgid "Customize" msgstr "Personalizar" @@ -9473,7 +9424,7 @@ msgstr "" "*Modo automático: Verifique a calibração antes de imprimir. Pule se for desnecessário." msgid "Shared PA Profile" -msgstr "" +msgstr "Perfil de PA Compartilhado" msgid "Nozzles and filaments of the same type share the same PA profile." msgstr "" @@ -9585,7 +9536,7 @@ msgid "%s space less than 20MB. Timelapse may not save properly. You can turn it msgstr "" msgid "Clean up files" -msgstr "" +msgstr "Limpar arquivos" msgid "Low internal storage. This timelapse will overwrite the oldest video files." msgstr "" @@ -9597,16 +9548,16 @@ msgid "Insufficient external storage for time-lapse photography. Connect to comp msgstr "" msgid "Storage Space Not Enough" -msgstr "" +msgstr "Espaço de Armazenamento Insuficiente" msgid "Confirm & Print" -msgstr "" +msgstr "Confirmar e Imprimir" msgid "Cancel Timelapse & Print" -msgstr "" +msgstr "Cancelar Timelapse e Imprimir" msgid "Clean Up" -msgstr "" +msgstr "Limpar" msgid "Preparing print job" msgstr "Preparando trabalho de impressão" @@ -10085,7 +10036,7 @@ msgid "A copy of the current system preset will be created, which will be detach msgstr "Uma cópia da predefinição de sistema atual será criada, que será desanexada da predefinição de sistema." msgid "The current custom preset will be detached from the parent system preset." -msgstr "A predefinição customizada atual será desanexada da predefinição de sistema pai." +msgstr "A predefinição personalizada atual será desanexada da predefinição de sistema pai." msgid "Modifications to the current profile will be saved." msgstr "Modificações ao perfil atual serão salvas." @@ -11257,28 +11208,28 @@ msgstr "Selecionar objetos por retângulo" msgctxt "Keyboard Shortcut" msgid "Arrow Up" -msgstr "" +msgstr "Seta para Cima" msgid "Move selection 10mm in positive Y direction" msgstr "Mover seleção 10mm na direção Y positiva" msgctxt "Keyboard Shortcut" msgid "Arrow Down" -msgstr "" +msgstr "Seta para Baixo" msgid "Move selection 10mm in negative Y direction" msgstr "Mover seleção 10mm na direção Y negativa" msgctxt "Keyboard Shortcut" msgid "Arrow Left" -msgstr "" +msgstr "Seta para Esquerda" msgid "Move selection 10mm in negative X direction" msgstr "Mover seleção 10mm na direção X negativa" msgctxt "Keyboard Shortcut" msgid "Arrow Right" -msgstr "" +msgstr "Seta para Direita" msgid "Move selection 10mm in positive X direction" msgstr "Mover seleção 10mm na direção X positiva" @@ -11363,14 +11314,14 @@ msgstr "Alternar modo de impressão para objeto/peça" msgctxt "Keyboard Shortcut" msgid "Tab" -msgstr "" +msgstr "Tab" msgid "Switch between Prepare/Preview" msgstr "Alternar entre Preparar/Pré-visualizar" msgctxt "Keyboard Shortcut" msgid "Space" -msgstr "" +msgstr "Espaço" msgid "Open actions speed dial" msgstr "" @@ -11431,14 +11382,14 @@ msgstr "Mover o controle deslizante 5 vezes mais rápido" msgctxt "Keyboard Shortcut" msgid "Home" -msgstr "" +msgstr "Home" msgid "Horizontal slider - Move to start position" msgstr "Barra deslizante horizontal — Mover para a posição inicial" msgctxt "Keyboard Shortcut" msgid "End" -msgstr "" +msgstr "End" msgid "Horizontal slider - Move to last position" msgstr "Barra deslizante horizontal — Mover para a última posição" @@ -12056,7 +12007,6 @@ msgstr "Exportando G-code" msgid "Generating G-code" msgstr "Gerando G-code" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Processing of the filename_format template failed." msgstr "Falha no processamento do gabarito filename_format." @@ -12087,7 +12037,6 @@ msgstr "Áreas de exclusão da mesa para cabeçotes de impressão paralelos" msgid "Ordered list of bed exclude areas by parallel printhead count. Item 1 applies to one printhead, item 2 to two printheads, and so on. Leave an item empty for no excluded area." msgstr "Lista ordenada de áreas de exclusão da mesa, com base na quantidade de cabeçotes de impressão operando em paralelo. O item 1 aplica-se a um cabeçote de impressão, o item 2 a dois cabeçotes, e assim por diante. Deixe um item em branco se não houver área de exclusão." -# TODO: Review, changed by lang refactor. PR 14254 msgid "Excluded bed area" msgstr "Área de exclusão da mesa" @@ -12103,9 +12052,8 @@ msgstr "Modelo personalizado da mesa" msgid "Elephant foot compensation" msgstr "Compensação de pé de elefante" -# TODO: Review, changed by lang refactor. PR 14254 msgid "This shrinks the first layer on the build plate to compensate for elephant foot effect." -msgstr "Encolhe a primeira camada na placa de impressão para compensar o efeito de pé de elefante." +msgstr "Isso encolhe a primeira camada na placa de impressão para compensar o efeito de pé de elefante." msgid "Elephant foot compensation layers" msgstr "Camadas de compensação de pé de elefante" @@ -12125,16 +12073,14 @@ msgstr "" "O valor inicial para a segunda camada está definido.\n" "As camadas subsequentes tornam-se linearmente mais densas pela altura especificada em elefant_foot_compensation_layers." -# TODO: Review, changed by lang refactor. PR 14254 msgid "This is the height for each layer. Smaller layer heights give greater accuracy but longer printing time." -msgstr "Altura de fatiamento para cada camada. Altura de camada menor significa mais precisão e maior tempo de impressão." +msgstr "Essa é a altura para cada camada. Alturas de camada menores significam mais precisão, mas maior tempo de impressão." msgid "Printable height" msgstr "Altura de impressão" -# TODO: Review, changed by lang refactor. PR 14254 msgid "This is the maximum printable height which is limited by the height of the build area." -msgstr "Altura máxima de impressão limitada pelo mecanismo da impressora." +msgstr "Essa é a altura máxima de impressão, que é limitada pela altura da área de impressão." msgid "Extruder printable height" msgstr "Altura de impressão da extrusora" @@ -12211,9 +12157,8 @@ msgstr "Senha" msgid "Ignore HTTPS certificate revocation checks" msgstr "Ignorar verificações de revogação de certificado HTTPS" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Ignore HTTPS certificate revocation checks in the case of missing or offline distribution points. One may want to enable this option for self signed certificates if connection fails." -msgstr "Ignorar verificações de revogação de certificado HTTPS em caso de pontos de distribuição ausentes ou offline. Pode-se querer habilitar esta opção para certificados autoassinados se a conexão falhar." +msgstr "Ignorar verificações de revogação de certificado HTTPS em caso de pontos de distribuição ausentes ou offline. Pode-se querer habilitar esta opção para certificados auto-assinados se a conexão falhar." msgid "Names of presets related to the physical printer." msgstr "Nomes das predefinições relacionados à impressora física." @@ -12233,16 +12178,14 @@ msgstr "" msgid "Avoid crossing walls" msgstr "Evitar atravessar paredes" -# TODO: Review, changed by lang refactor. PR 14254 msgid "This detours to avoid traveling across walls, which may cause blobs on the surface." -msgstr "Desviar para evitar atravessar paredes, que pode causar irregularidades na superfície." +msgstr "Isso desvia para evitar atravessar paredes, o que pode causar irregularidades na superfície." msgid "Avoid crossing walls - Max detour length" msgstr "Evitar atravessar paredes - Distância máximo do desvio" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Maximum detour distance for avoiding crossing wall: The printer won't detour if the detour distance is larger than this value. Detour length could be specified either as an absolute value or as percentage (for example 50%) of a direct travel path. A value of 0 will disable this." -msgstr "Distância máxima de desvio para evitar atravessar paredes. Não desviar se a distância de desvio for maior que esse valor. A distancia do desvio pode ser especificada como um valor absoluto ou como porcentagem (por exemplo, 50%) de um caminho de deslocamento direto. Zero para desativar." +msgstr "Distância máxima de desvio para evitar atravessar paredes. A impressora não desviará se a distância de desvio for maior que esse valor. A distancia do desvio pode ser especificada como um valor absoluto ou como porcentagem (por exemplo, 50%) de um caminho de deslocamento direto. O valor 0 desativa isso." msgid "mm or %" msgstr "mm ou %" @@ -12251,59 +12194,46 @@ msgid "Other layers" msgstr "Outras camadas" msgid "Bed temperature for layers except the initial one. A value of 0 means the filament does not support printing on the Cool Plate SuperTack." -msgstr "Temperatura da mesa para camadas exceto a inicial. O valor 0 significa que o filamento não suporta a impressão na Placa Fria SuperTack." +msgstr "Essa é a temperatura da mesa para camadas exceto a inicial. O valor 0 significa que o filamento não suporta a impressão na Placa Fria SuperTack." -# TODO: Review, changed by lang refactor. PR 14254 msgid "This is the bed temperature for layers except for the first one. A value of 0 means the filament does not support printing on the Cool Plate." -msgstr "Temperatura da mesa para camadas exceto a inicial. O valor 0 significa que o filamento não suporta a impressão na Placa Fria." +msgstr "Essa é a temperatura da mesa para camadas exceto a inicial. O valor 0 significa que o filamento não suporta a impressão na Placa Fria." -# TODO: Review, changed by lang refactor. PR 14254 msgid "This is the bed temperature for layers except for the first one. A value of 0 means the filament does not support printing on the Textured Cool Plate." -msgstr "Temperatura da mesa para camadas exceto a inicial. O valor 0 significa que o filamento não suporta a impressão na Placa Fria Texturizada." +msgstr "Essa é a temperatura da mesa para camadas exceto a inicial. O valor 0 significa que o filamento não suporta a impressão na Placa Fria Texturizada." -# TODO: Review, changed by lang refactor. PR 14254 msgid "This is the bed temperature for layers except for the first one. A value of 0 means the filament does not support printing on the Engineering Plate." -msgstr "Temperatura da mesa para camadas exceto a inicial. O valor 0 significa que o filamento não suporta a impressão na Placa de Engenharia." +msgstr "Essa é a temperatura da mesa para camadas exceto a inicial. O valor 0 significa que o filamento não suporta a impressão na Placa de Engenharia." -# TODO: Review, changed by lang refactor. PR 14254 msgid "This is the bed temperature for layers except for the first one. A value of 0 means the filament does not support printing on the High Temp Plate." -msgstr "Temperatura da mesa para camadas exceto a inicial. O valor 0 significa que o filamento não suporta a impressão na Placa de Alta Temperatura." +msgstr "Essa é a temperatura da mesa para camadas exceto a inicial. O valor 0 significa que o filamento não suporta a impressão na Placa de Alta Temperatura." -# TODO: Review, changed by lang refactor. PR 14254 msgid "This is the bed temperature for layers except for the first one. A value of 0 means the filament does not support printing on the Textured PEI Plate." -msgstr "Temperatura da mesa para camadas exceto a inicial. O valor 0 significa que o filamento não suporta a impressão na Placa PEI Texturizada." +msgstr "Essa é a temperatura da mesa para camadas exceto a inicial. O valor 0 significa que o filamento não suporta a impressão na Placa PEI Texturizada." -# TODO: Review, changed by lang refactor. PR 14254 msgid "First layer" msgstr "Primeira camada" -# TODO: Review, changed by lang refactor. PR 14254 msgid "First layer bed temperature" -msgstr "Temperatura da mesa na primeira camada" +msgstr "Essa é a temperatura da mesa na primeira camada" -# TODO: Review, changed by lang refactor. PR 14254 msgid "This is the bed temperature of the first layer. A value of 0 means the filament does not support printing on the Cool Plate SuperTack." -msgstr "Temperatura da mesa na primeira camada. O valor 0 significa que o filamento não suporta a impressão na Placa Fria SuperTack." +msgstr "Essa é a temperatura da mesa na primeira camada. O valor 0 significa que o filamento não suporta a impressão na Placa Fria SuperTack." -# TODO: Review, changed by lang refactor. PR 14254 msgid "This is the bed temperature of the first layer. A value of 0 means the filament does not support printing on the Cool Plate." -msgstr "Temperatura da mesa na primeira camada. O valor 0 significa que o filamento não suporta a impressão na Placa Fria." +msgstr "Essa é a temperatura da mesa na primeira camada. O valor 0 significa que o filamento não suporta a impressão na Placa Fria." -# TODO: Review, changed by lang refactor. PR 14254 msgid "This is the bed temperature of the first layer. A value of 0 means the filament does not support printing on the Textured Cool Plate." -msgstr "Temperatura da mesa na primeira camada. O valor 0 significa que o filamento não suporta a impressão na Placa Fria Texturizada." +msgstr "Essa é a temperatura da mesa na primeira camada. O valor 0 significa que o filamento não suporta a impressão na Placa Fria Texturizada." -# TODO: Review, changed by lang refactor. PR 14254 msgid "This is the bed temperature of the first layer. A value of 0 means the filament does not support printing on the Engineering Plate." -msgstr "Temperatura da mesa na primeira camada. O valor 0 significa que o filamento não suporta a impressão na Placa de Engenharia." +msgstr "Essa é a temperatura da mesa na primeira camada. O valor 0 significa que o filamento não suporta a impressão na Placa de Engenharia." -# TODO: Review, changed by lang refactor. PR 14254 msgid "This is the bed temperature of the first layer. A value of 0 means the filament does not support printing on the High Temp Plate." -msgstr "Temperatura da mesa na primeira camada. O valor 0 significa que o filamento não suporta a impressão na Placa de Alta Temperatura." +msgstr "Essa é a temperatura da mesa na primeira camada. O valor 0 significa que o filamento não suporta a impressão na Placa de Alta Temperatura." -# TODO: Review, changed by lang refactor. PR 14254 msgid "This is the bed temperature of the first layer. A value of 0 means the filament does not support printing on the Textured PEI Plate." -msgstr "Temperatura da mesa na primeira camada. O valor 0 significa que o filamento não suporta a impressão na Placa PEI Texturizada." +msgstr "Essa é a temperatura da mesa na primeira camada. O valor 0 significa que o filamento não suporta a impressão na Placa PEI Texturizada." msgid "Plate types supported by the printer" msgstr "Tipos de placa suportadas pela impressora" @@ -12338,9 +12268,8 @@ msgstr "Este é o número de camadas sólidas da casca de base, incluindo a prim msgid "Bottom shell thickness" msgstr "Espessura da casca de base" -# TODO: Review, changed by lang refactor. PR 14254 msgid "The number of bottom solid layers is increased when slicing if the thickness calculated by bottom shell layers is thinner than this value. This can avoid having too thin a shell when layer height is small. 0 means that this setting is disabled and the thickness of the bottom shell is determined simply by the number of bottom shell layers." -msgstr "O número de camadas sólidas da base é aumentado ao fatiar se a espessura calculada pelas camadas de casca de base for mais fina do que este valor. Isso pode evitar que a casca de base seja muito fina quando a altura da camada é pequena. 0 significa que esta configuração está desativada e a espessura da casca de base é absolutamente determinada pelas camadas de casca de base." +msgstr "O número de camadas sólidas da base é aumentado ao fatiar se a espessura calculada pelas camadas de casca de base for mais fina do que este valor. Isso pode evitar que a casca de base seja muito fina quando a altura da camada é pequena. 0 significa que esta configuração está desativada e a espessura da casca de base é determinada apenas pelas camadas de casca de base." msgid "Apply gap fill" msgstr "Aplicar preenchimento de vão" @@ -12745,13 +12674,11 @@ msgstr "" "O valor 0 permite a reversão em todas as camadas pares, sempre.\n" "Quando Detectar parede saliente não está habilitado, esta opção é ignorada e a reversão acontece em todas as camadas pares, sempre." -# TODO: Review, changed by lang refactor. PR 14254 msgid "Slow down for overhangs" msgstr "Reduzir velocidade em saliências" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Enable this option to slow down when printing overhangs. The speeds for different overhang percentages are set below." -msgstr "Ative esta opção para diminuir a velocidade de impressão para diferentes de degraus de saliência." +msgstr "Ative esta opção para reduzir a velocidade ao imprimir saliências. As velocidades para diferentes porcentagens de saliência são definidas abaixo." msgid "Slow down for curled perimeters" msgstr "Reduzir vel. para perímetros encurvados" @@ -12792,9 +12719,8 @@ msgstr "Velocidade de pontes internas. Se o valor for expresso como uma porcenta msgid "Brim width" msgstr "Largura da borda" -# TODO: Review, changed by lang refactor. PR 14254 msgid "This is the distance from the model to the outermost brim line." -msgstr "Distância do modelo até a linha da borda mais externa." +msgstr "Essa é a distância do modelo até a linha da borda mais externa." msgid "Brim type" msgstr "Tipo de borda" @@ -12805,9 +12731,8 @@ msgstr "Isso controla a geração da borda no lado externo e/ou interno dos mode msgid "Brim-object gap" msgstr "Espaço entre a borda e objeto" -# TODO: Review, changed by lang refactor. PR 14254 msgid "This creates a gap between the innermost brim line and the object and can make the brim easier to remove." -msgstr "Um espaço entre a linha mais interna da borda e o objeto pode facilitar a remoção da borda." +msgstr "Isso cria um espaço entre a linha mais interna da borda e o objeto e pode tornar a borda mais fácil de remover." msgid "Brim flow ratio" msgstr "Taxa de fluxo em borda" @@ -12825,18 +12750,16 @@ msgstr "" "\n" "Nota: o valor resultante não será afetado pela taxa de fluxo da primeira camada." -# TODO: Review, changed by lang refactor. PR 14254 msgid "Brim follows compensated outline" msgstr "Borda segue contorno compensado" -# TODO: Review, changed by lang refactor. PR 14254 msgid "" "When enabled, the brim is aligned with the first-layer perimeter geometry after Elephant Foot Compensation is applied.\n" "This option is intended for cases where Elephant Foot Compensation significantly alters the first-layer footprint.\n" "\n" "If your current setup already works well, enabling it may be unnecessary and can cause the brim to fuse with upper layers." msgstr "" -"Quando ativado, o borda fica alinhado com a geometria do perímetro da primeira camada após a aplicação da Compensação da Pé de Elefante.\n" +"Quando ativada, a borda fica alinhada com a geometria do perímetro da primeira camada após a aplicação da Compensação da Pé de Elefante.\n" "Esta opção destina-se aos casos em que a Compensação da Pé de Elefante altera significativamente a pegada da primeira camada.\n" "\n" "Se a sua configuração atual já funciona bem, ativá-la pode ser desnecessário e pode fazer com que o borda se funda com as camadas superiores." @@ -12881,17 +12804,14 @@ msgstr "uáquina compatível ascendente" msgid "Condition" msgstr "Condição" -# TODO: Review, changed by lang refactor. PR 14254 msgid "A Boolean expression using the configuration values of an active printer profile. If this expression evaluates to true, this profile is considered compatible with the active printer profile." msgstr "Uma expressão booleana usando os valores de configuração de um perfil de impressora ativo. Se essa expressão for avaliada como true, esse perfil será considerado compatível com o perfil de impressora ativo." -# TODO: Review, changed by lang refactor. PR 14254 msgid "A Boolean expression using the configuration values of an active print profile. If this expression evaluates to true, this profile is considered compatible with the active print profile." msgstr "Uma expressão booleana usando os valores de configuração de um perfil de impressão ativo. Se essa expressão for avaliada como true, esse perfil será considerado compatível com o perfil de impressão ativo." -# TODO: Review, changed by lang refactor. PR 14254 msgid "This determines the print sequence, allowing you to print layer-by-layer or object-by-object." -msgstr "Sequência de impressão, camada por camada ou objeto por objeto." +msgstr "Isso determina a sequência de impressão, permitindo a impressão camada por camada ou objeto por objeto." msgid "By layer" msgstr "Por camada" @@ -13180,13 +13100,13 @@ msgid "" msgstr "" msgid "Inward and Outward" -msgstr "" +msgstr "Para Dentro e Para Fora" msgid "Inward" -msgstr "" +msgstr "Para Dentro" msgid "Outward" -msgstr "" +msgstr "Para Fora" msgid "Bottom surface pattern" msgstr "Padrão de superfície inferior" @@ -13961,7 +13881,6 @@ msgstr "TPMS-FK" msgid "Gyroid" msgstr "Giroide" -# TODO: Review, changed by lang refactor. PR 14254 msgid "This is the acceleration of top surface infill. Using a lower value may improve top surface quality." msgstr "Esta é a aceleração do preenchimento da superfície superior. Usar um valor menor pode melhorar a qualidade da superfície superior." @@ -13977,9 +13896,8 @@ msgstr "Aceleração do preenchimento esparso. Se o valor for expresso como uma msgid "Acceleration of internal solid infill. If the value is expressed as a percentage (e.g. 100%), it will be calculated based on the default acceleration." msgstr "Aceleração do preenchimento sólido interno. Se o valor for expresso como uma porcentagem (por exemplo, 100%), será calculado com base na aceleração padrão." -# TODO: Review, changed by lang refactor. PR 14254 msgid "This is the printing acceleration for the first layer. Using limited acceleration can improve build plate adhesion." -msgstr "Aceleração da primeira camada. Usar um valor menor pode melhorar a adesão à placa de impressão." +msgstr "Esta é a aceleração para a primeira camada. Usar aceleração limitada melhorar a adesão à placa de impressão." msgid "Enable accel_to_decel" msgstr "Habilitar accel_to_decel" @@ -14026,25 +13944,20 @@ msgstr "" msgid "Line width of the first layer. If expressed as a %, it will be computed over the nozzle diameter." msgstr "Largura da linha da primeira camada. Se expresso como uma %, será calculado sobre o diâmetro do bico." -# TODO: Review, changed by lang refactor. PR 14254 msgid "First layer height" msgstr "Altura da primeira camada" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Height of the first layer. Making the first layer height thicker can improve build plate adhesion." msgstr "Altura da primeira camada. Tornar a altura da primeira camada mais espessa pode melhorar a adesão à placa de impressão." -# TODO: Review, changed by lang refactor. PR 14254 msgid "This is the speed for the first layer except for solid infill sections." -msgstr "Velocidade da primeira camada, exceto a parte de preenchimento sólido." +msgstr "Esta é a velocidade da primeira camada, exceto as regiões de preenchimento sólido." -# TODO: Review, changed by lang refactor. PR 14254 msgid "First layer infill" msgstr "Preenchimento da primeira camada" -# TODO: Review, changed by lang refactor. PR 14254 msgid "This is the speed for solid infill parts of the first layer." -msgstr "Velocidade da parte de preenchimento sólido da primeira camada." +msgstr "Esta é a velocidade das regiões de preenchimento sólido da primeira camada." msgid "First layer travel speed" msgstr "Velocidade de deslocamento da primeira camada" @@ -14058,9 +13971,8 @@ msgstr "Número de camadas lentas" msgid "The first few layers are printed slower than normal. The speed is gradually increased in a linear fashion over the specified number of layers." msgstr "As primeiras camadas são impressas mais lentamente do que o normal. A velocidade é aumentada gradualmente de forma linear sobre o número especificado de camadas." -# TODO: Review, changed by lang refactor. PR 14254 msgid "First layer nozzle temperature" -msgstr "Temperatura do bico da primeira camada" +msgstr "Temperatura do bico na primeira camada" msgid "Nozzle temperature for printing the first layer with this filament" msgstr "Temperatura do bico para imprimir a primeira camada com este filamento" @@ -14123,29 +14035,28 @@ msgid "Ironing flow" msgstr "Fluxo do alisamento" msgid "Filament-specific override for ironing flow. This allows you to customize the ironing flow for each filament type. Too high value results in overextrusion on the surface." -msgstr "Ajuste específico para cada filamento no fluxo de alisamento. Isso permite customizar o fluxo de alisamento para cada tipo de filamento. Um valor muito alto resulta em sobrextrusão na superfície." +msgstr "Ajuste específico para cada filamento no fluxo de alisamento. Isso permite personalizar o fluxo de alisamento para cada tipo de filamento. Um valor muito alto resulta em sobrextrusão na superfície." msgid "Ironing line spacing" msgstr "Espaçamento de linha do alisamento" msgid "Filament-specific override for ironing line spacing. This allows you to customize the spacing between ironing lines for each filament type." -msgstr "Configuração específica para cada filamento no espaçamento das linhas do alisamento. Isso permite customizar o espaçamento entre as linhas do alisamento para cada tipo de filamento." +msgstr "Configuração específica para cada filamento no espaçamento das linhas do alisamento. Isso permite personalizar o espaçamento entre as linhas do alisamento para cada tipo de filamento." msgid "Ironing inset" msgstr "Inserção do alisamento" msgid "Filament-specific override for ironing inset. This allows you to customize the distance to keep from the edges when ironing for each filament type." -msgstr "Controle da inserção de alisamento para cada filamento. Isso permite customizar a distância a ser mantida das bordas ao alisar cada tipo de filamento." +msgstr "Controle da inserção de alisamento para cada filamento. Isso permite personalizar a distância a ser mantida das bordas ao alisar cada tipo de filamento." msgid "Ironing speed" msgstr "Velocidade do alisamento" msgid "Filament-specific override for ironing speed. This allows you to customize the print speed of ironing lines for each filament type." -msgstr "Controle da velocidade de alisamento para cada filamento. Isso permite customizar a velocidade de impressão das linhas de alisamento para cada tipo de filamento." +msgstr "Controle da velocidade de alisamento para cada filamento. Isso permite personalizar a velocidade de impressão das linhas de alisamento para cada tipo de filamento." -# TODO: Review, changed by lang refactor. PR 14254 msgid "This setting makes the toolhead randomly jitter while printing walls so that the surface has a rough textured look. This setting controls the fuzzy position." -msgstr "Movimento aleatório durante a impressão da parede, de modo que a superfície tenha uma aparência áspera. Essa configuração controla a posição difusa." +msgstr "Essa configuração faz o cabeçote tremer durante a impressão da parede de modo que a superfície tenha uma aparência áspera. Essa configuração controla a posição difusa." msgid "Painted only" msgstr "Somente pintado" @@ -14165,9 +14076,8 @@ msgstr "Todas as paredes" msgid "Fuzzy skin thickness" msgstr "Espessura da textura difusa" -# TODO: Review, changed by lang refactor. PR 14254 msgid "The width of jittering: it’s recommended to keep this lower than the outer wall line width." -msgstr "A largura dentro da qual tremer. É aconselhável que seja menor do que a largura da linha da parede externa." +msgstr "A largura do tremor: é recomendado que seja menor do que a largura da linha da parede externa." msgid "Fuzzy skin point distance" msgstr "Distância do ponto da textura difusa" @@ -14312,9 +14222,8 @@ msgstr "Camadas e Perímetros" msgid "Don't print gap fill with a length is smaller than the threshold specified (in mm). This setting applies to top, bottom and solid infill and, if using the classic perimeter generator, to wall gap fill." msgstr "Não imprimir preenchimento de lacuna com um comprimento menor que o limiar especificado (em mm). Esta configuração se aplica ao preenchimento superior, inferior e sólido e, se estiver usando o gerador de perímetro clássico, ao preenchimento de lacuna de parede." -# TODO: Review, changed by lang refactor. PR 14254 msgid "This is the speed for gap infill. Gaps usually have irregular line width and should be printed more slowly." -msgstr "Velocidade de preenchimento de vão. Vazios geralmente têm largura de linha irregular e devem ser impressas mais lentamente." +msgstr "Essa é a velocidade de preenchimento de vão. Vazios geralmente têm largura de linha irregular e devem ser impressas mais lentamente." msgid "Precise Z height" msgstr "Altura Z precisa" @@ -14343,9 +14252,8 @@ msgstr "Habilite isso para adicionar o número da linha (Nx) no início de cada msgid "Scan first layer" msgstr "Escanear primeira camada" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Enable this to allow the camera on the printer to check the quality of the first layer." -msgstr "Habilitar isso para ativar a câmera na impressora para verificar a qualidade da primeira camada." +msgstr "Habilite isso para ativar a câmera na impressora para verificar a qualidade da primeira camada." msgid "Power Loss Recovery" msgstr "Recuperação de Perda de Energia" @@ -14359,9 +14267,8 @@ msgstr "Configuração da impressora" msgid "Nozzle type" msgstr "Tipo de bico" -# TODO: Review, changed by lang refactor. PR 14254 msgid "The metallic material of the nozzle: This determines the abrasive resistance of the nozzle and what kind of filament can be printed." -msgstr "O material metálico do bico. Isso determina a resistência ao desgaste do bico e que tipo de filamento pode ser impresso." +msgstr "O material metálico do bico: isso determina a resistência à abrasão do bico e que tipo de filamento pode ser impresso." msgid "Hardened steel" msgstr "Aço endurecido" @@ -14375,7 +14282,6 @@ msgstr "Carbeto de tungstênio" msgid "Nozzle HRC" msgstr "Bico HRC" -# TODO: Review, changed by lang refactor. PR 14254 msgid "The nozzle's hardness. Zero means no checking for nozzle hardness during slicing." msgstr "A dureza do bico. Zero significa que não há verificação da dureza do bico durante o fatiamento." @@ -14410,10 +14316,10 @@ msgid "Enable this option if machine has auxiliary part cooling fan. G-code comm msgstr "Habilitar esta opção se a máquina tiver ventilador auxiliar de resfriamento de peças. Comando G-code: M106 P2 S(0-255)." msgid "Fan direction" -msgstr "" +msgstr "Direção da ventoinha" msgid "Cooling fan direction of the printer" -msgstr "" +msgstr "Direção da ventoinha de resfriamento da impressora" msgid "Both" msgstr "" @@ -14468,9 +14374,8 @@ msgstr "O custo da impressora por hora." msgid "money/h" msgstr "dinheiro/h" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Support controlling chamber temperature" -msgstr "Controlar a temperatura da câmara de suporte" +msgstr "Suporta controle da temperatura da câmara" msgid "" "This option is enabled if machine support controlling chamber temperature\n" @@ -14537,7 +14442,6 @@ msgstr "Ative isso para obter um arquivo G-code comentado, com cada linha explic msgid "Infill combination" msgstr "Combinar preenchimento" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Automatically combine sparse infill of several layers to print together in order to reduce time. Walls are still printed with original layer height." msgstr "Combina automaticamente o preenchimento esparso de várias camadas para imprimir juntas e reduzir o tempo. A parede ainda é impressa com a altura original da camada." @@ -14713,14 +14617,12 @@ msgstr "" msgid "Line width of internal sparse infill. If expressed as a %, it will be computed over the nozzle diameter." msgstr "Largura da linha do preenchimento esparso interno. Se expresso como %, será calculado sobre o diâmetro do bico." -# TODO: Review, changed by lang refactor. PR 14254 msgid "Infill/wall overlap" msgstr "Sobreposição de preenchimento/parede" -# TODO: Review, changed by lang refactor. PR 14254 #, no-c-format, no-boost-format msgid "This allows the infill area to be enlarged slightly to overlap with walls for better bonding. The percentage value is relative to line width of sparse infill. Set this value to ~10-15% to minimize potential over extrusion and accumulation of material resulting in rough top surfaces." -msgstr "A área de preenchimento é aumentada ligeiramente para se sobrepor à parede para uma melhor ligação. O valor percentual é relativo à largura da linha do preencimento esparso. Defina este valor como ~10-15% para minimizar uma potencial sobre extrusão e acumulo de material resultando em superfícies superiores ásperas." +msgstr "Isso permite a área de preenchimento ser aumentada ligeiramente para se sobrepor à parede para uma melhor ligação. O valor percentual é relativo à largura da linha do preencimento esparso. Defina este valor como ~10-15% para minimizar uma potencial sobre extrusão e acumulo de material resultando em superfícies superiores ásperas." msgid "Top/Bottom solid infill/wall overlap" msgstr "Sobreposição Superior/Inferior de preenchimento sólido/parede" @@ -14729,9 +14631,8 @@ msgstr "Sobreposição Superior/Inferior de preenchimento sólido/parede" msgid "Top solid infill area is enlarged slightly to overlap with wall for better bonding and to minimize the appearance of pinholes where the top infill meets the walls. A value of 25-30% is a good starting point, minimizing the appearance of pinholes. The percentage value is relative to line width of sparse infill." msgstr "A área de preenchimento sólido é ligeiramente alargada para se sobrepor à parede para melhor adesão e para minimizar a aparência de furos onde o preenchimento encontra as paredes. Um valor de 25-30% é um bom ponto de partida, minimizando a aparência dos buracos. O valor percentual é relativo à largura da linha do preenchimento esparso." -# TODO: Review, changed by lang refactor. PR 14254 msgid "This is the speed for internal sparse infill." -msgstr "Velocidade do preenchimento esparso interno." +msgstr "Essa é a velocidade do preenchimento esparso interno." msgid "Inherits profile" msgstr "Herda o perfil" @@ -14748,9 +14649,8 @@ msgstr "Força a geração de cascas sólidas entre materiais/volumes adjacentes msgid "Maximum width of a segmented region" msgstr "Largura máxima de uma região segmentada" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Maximum width of a segmented region. A value of 0 disables this feature." -msgstr "Largura máxima de uma região segmentada. Zero desativa essa funcionalidade." +msgstr "Largura máxima de uma região segmentada. O valor 0 desativa essa funcionalidade." msgid "Interlocking depth of a segmented region" msgstr "Profundidade de intertravamento de uma região segmentada" @@ -14794,24 +14694,20 @@ msgstr "Prevenção de fronteiras intertravadas" msgid "The distance from the outside of a model where interlocking structures will not be generated, measured in cells." msgstr "A distância da parte externa de um modelo onde estruturas intertravadas não serão geradas, medida em células." -# TODO: Review, changed by lang refactor. PR 14254 msgid "Ironing type" -msgstr "Tipo de Alisamento" +msgstr "Tipo de alisamento" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Ironing uses a small flow to print at the same height of a surface to make flat surfaces smoother. This setting controls which layers are being ironed." -msgstr "O alisamento usa um pequeno fluxo para imprimir na mesma altura da superfície novamente para deixá-la mais lisa. Esta configuração controla qual camada está sendo alisada." +msgstr "O alisamento usa um fluxo pequeno para imprimir na mesma altura da superfície novamente para deixá-la mais lisa. Esta configuração controla que camadas estão sendo alisadas." msgid "No ironing" msgstr "Sem alisamento" -# TODO: Review, changed by lang refactor. PR 14254 msgid "All top surfaces" -msgstr "Superfícies superiores" +msgstr "Todas as superfícies superiores" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Topmost surface only" -msgstr "Superfície superior mais alta" +msgstr "Apenas superfície superior mais alta" msgid "All solid layers" msgstr "Todas as camadas sólidas" @@ -14822,20 +14718,17 @@ msgstr "Padrão do Alisamento" msgid "The pattern that will be used when ironing." msgstr "O padrão que será usado durante o alisamento." -# TODO: Review, changed by lang refactor. PR 14254 msgid "This is the amount of material to be extruded during ironing. It is relative to the flow of normal layer height. Too high a value will result in overextrusion on the surface." -msgstr "A quantidade de material a extrudar durante o alisamento. Relativo ao fluxo da altura normal da camada. Um valor muito alto resulta em superextrusão na superfície." +msgstr "Essa é a quantidade de material a ser extrudada durante o alisamento. Relativo ao fluxo da altura normal da camada. Um valor muito alto resultará em superextrusão na superfície." -# TODO: Review, changed by lang refactor. PR 14254 msgid "This is the distance between the lines used for ironing." -msgstr "A distância entre as linhas do alisamento." +msgstr "Essa é a distância entre as linhas usadas para o alisamento." msgid "The distance to keep from the edges. A value of 0 sets this to half of the nozzle diameter." msgstr "A distância a ser mantida das bordas. Um valor de 0 define isso como metade do diâmetro do bico." -# TODO: Review, changed by lang refactor. PR 14254 msgid "This is the print speed for ironing lines." -msgstr "Velocidade de impressão das linhas do alisamento." +msgstr "Essa é a velocidade de impressão das linhas do alisamento." msgid "Ironing angle offset" msgstr "Delocamento de ângulo para alisamento" @@ -14895,9 +14788,8 @@ msgstr "Este G-code é inserido a cada mudança de camada após a elevação Z." msgid "Clumping detection G-code" msgstr "G-code para detecção de aglomeração" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Silent Mode" -msgstr "Suporta modo silencioso" +msgstr "Modo Silencioso" msgid "Whether the machine supports silent mode in which machine uses lower acceleration to print more quietly" msgstr "Se a máquina suporta o modo silencioso, no qual a máquina usa uma aceleração menor para imprimir" @@ -15195,7 +15087,6 @@ msgstr "" "O valor zero usará a taxa de amortecimento do firmware.\n" "Para desativar o modelador de entrada, use o tipo Desativar." -# TODO: Review, changed by lang refactor. PR 14254 msgid "The part cooling fan speed may be increased when auto cooling is enabled. This is the maximum speed for the part cooling fan." msgstr "A velocidade do ventilador de resfriamento de peças pode ser aumentada quando o resfriamento automático está habilitado. Este é o limite máximo de velocidade do ventilador de resfriamento de peças." @@ -15313,9 +15204,8 @@ msgstr "O OrcaSlicer pode carregar arquivos de G-code para um hospedeiro de impr msgid "Nozzle volume" msgstr "Volume do bico" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Volume of nozzle between the filament cutter and the end of the nozzle" -msgstr "Volume do bico entre o cortador de filamento e a extremidade do bico" +msgstr "Volume do bico entre o cortador de filamento e o final do bico" msgid "Cooling tube position" msgstr "Posição do tubo de resfriamento" @@ -15350,16 +15240,14 @@ msgstr "Quando definido como zero, a distância que o filamento é movido da pos msgid "Start end points" msgstr "Pontos de início e fim" -# TODO: Review, changed by lang refactor. PR 14254 msgid "The start and end points which are from the cutter area to the excess chute." -msgstr "Os pontos de início e fim que vão da área do cortador até a lata de lixo." +msgstr "Os pontos de início e fim que vão da área do cortador até a calha de excesso." msgid "Reduce infill retraction" msgstr "Reduzir retração durante o preenchimento" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Don't retract when the travel is entirely within an infill area. That means the oozing can't been seen. This can reduce times of retraction for complex model and save printing time, but make slicing and G-code generating slower. Note that z-hop is also not performed in areas where retraction is skipped." -msgstr "Não retrair quando o movimento está na área de preenchimento completamente. Isso significa que o vazamento não pode ser visto. Isso pode reduzir o número de retratações para modelos complexos e economizar tempo de impressão, mas torna a geração de fatiamento e G-code mais lenta." +msgstr "Não retrair quando o movimento está completamente na área de preenchimento. Isso significa que o vazamento não pode ser visto. Isso pode reduzir o número de retratações para modelos complexos e economizar tempo de impressão, mas torna a geração de fatiamento e G-code mais lenta. Note que o Z-Hop também não é realizado em áreas onde a retração é omitida." msgid "This option will drop the temperature of the inactive extruders to prevent oozing." msgstr "Esta opção diminuirá a temperatura das extrusoras inativas para evitar vazamentos." @@ -15367,7 +15255,6 @@ msgstr "Esta opção diminuirá a temperatura das extrusoras inativas para evita msgid "Filename format" msgstr "Formato do nome do arquivo" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Users can decide project file names when exporting." msgstr "O usuário pode definir o nome do arquivo do projeto ao exportar." @@ -15392,10 +15279,9 @@ msgstr "Área maxima de um furo na base do modelo antes que ele seja preenchido msgid "Detect overhang walls" msgstr "Detectar paredes salientes" -# TODO: Review, changed by lang refactor. PR 14254 #, c-format, boost-format msgid "This detects the overhang percentage relative to line width and uses a different speed to print. For 100%% overhang, bridging speed is used." -msgstr "Detecta a porcentagem relativa de saliência em relação a largura do perímetro e usa uma velocidade diferente de impressão. Para saliências 100%%, a velocidade de ponte é usada." +msgstr "Isso detecta a porcentagem relativa de saliência em relação a largura do perímetro e usa uma velocidade diferente de impressão. Para saliências 100%%, a velocidade de ponte é usada." msgid "Outer walls" msgstr "Paredes externas" @@ -15420,13 +15306,11 @@ msgstr "" msgid "Line width of inner wall. If expressed as a %, it will be computed over the nozzle diameter." msgstr "Largura de linha da parede interna. Se expressado como %, será computado de acordo com o diâmetro do bico." -# TODO: Review, changed by lang refactor. PR 14254 msgid "This is the speed for inner walls." -msgstr "Velocidade da parede interna." +msgstr "Essa é a velocidade para paredes internas." -# TODO: Review, changed by lang refactor. PR 14254 msgid "This is the number of walls per layer." -msgstr "Número de paredes em cada camada." +msgstr "Esse é o número de paredes por camada." msgid "Alternate extra wall" msgstr "Parede extra alternada" @@ -15480,32 +15364,26 @@ msgstr "Variante da impressora" msgid "Raft contact Z distance" msgstr "Distância Z de contato da jangada" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Z gap between raft and object. If Support Top Z Distance is 0, this value is ignored and the object is printed in direct contact with the raft (no gap)." -msgstr "Espaço Z entre a balsa e o objeto. Se a Distância Z Superior do Suporte for 0, este valor é ignorado e o objeto é impresso em contato direto com a balsa (sem espaço)." +msgstr "Folga Z entre a jangada e o objeto. Se a Distância Z Superior do Suporte for 0, este valor é ignorado e o objeto é impresso em contato direto com a jangada (sem folga)." msgid "Raft expansion" msgstr "Expansão da jangada" -# TODO: Review, changed by lang refactor. PR 14254 msgid "This expands all raft layers in XY plane." -msgstr "Expandir todas as camadas da jangada no plano XY." +msgstr "Isso expande todas as camadas da jangada no plano XY." -# TODO: Review, changed by lang refactor. PR 14254 msgid "First layer density" msgstr "Densidade da primeira camada" -# TODO: Review, changed by lang refactor. PR 14254 msgid "This is the density of the first raft or support layer." -msgstr "Densidade da primeira camada da jangada ou do suporte." +msgstr "Essa é a densidade da primeira camada da jangada ou do suporte." -# TODO: Review, changed by lang refactor. PR 14254 msgid "First layer expansion" msgstr "Expansão da primeira camada" -# TODO: Review, changed by lang refactor. PR 14254 msgid "This expands the first raft or support layer to improve bed adhesion." -msgstr "Expanda a primeira camada da jangada ou do suporte para melhorar a adesão à placa da mesa de impressão." +msgstr "Isso expande a primeira camada da jangada ou do suporte para melhorar a adesão à placa de impressão." msgid "Raft layers" msgstr "Camadas da jangada" @@ -15513,9 +15391,8 @@ msgstr "Camadas da jangada" msgid "Object will be raised by this number of support layers. Use this function to avoid warping when printing ABS." msgstr "O objeto será elevado por este número de camadas de suporte. Use esta função para evitar empenamento ao imprimir ABS." -# TODO: Review, changed by lang refactor. PR 14254 msgid "The G-code path is generated after simplifying the contour of models to avoid too many points and G-code lines. Smaller values mean higher resolution and more time required to slice." -msgstr "O caminho do G-code é gerado após simplificar o contorno dos modelos para evitar excesso de pontos e linhas de G-code. Um valor menor significa maior resolução e mais tempo para fatiar." +msgstr "O caminho do G-code é gerado após simplificar o contorno dos modelos para evitar excesso de pontos e linhas de G-code. Valores menores significam maior resolução e mais tempo para fatiar." msgid "Travel distance threshold" msgstr "Limiar de distância de deslocamento" @@ -15526,26 +15403,25 @@ msgstr "Acionar a retração somente quando a distância da deslocamento for mai msgid "Retract amount before wipe" msgstr "Quantidade de retração antes da limpeza" -# TODO: Review, changed by lang refactor. PR 14254 msgid "This is the length of fast retraction before a wipe, relative to retraction length." -msgstr "O comprimento da retração rápida antes da limpeza, em relação ao comprimento da retração." +msgstr "Este é o comprimento da retração rápida antes de uma limpeza, em relação ao comprimento da retração." msgid "Retract amount after wipe" -msgstr "" +msgstr "Quantidade de retração depois da limpeza" #, c-format msgid "" "The length of fast retraction after wipe, relative to retraction length.\n" "The value will be clamped by 100% minus the retract amount before the wipe value." msgstr "" +"Este é o comprimento da retração rápida antes de uma limpeza, em relação ao comprimento da retração.\n" +"O valor será limitado a 100% menos a distância de retração, antes da aplicação do valor de limpeza." -# TODO: Review, changed by lang refactor. PR 14254 msgid "Retract on layer change" msgstr "Retrair ao mudar de camada" -# TODO: Review, changed by lang refactor. PR 14254 msgid "This forces a retraction on layer changes." -msgstr "Forçar uma retração ao mudar de camada." +msgstr "Isso força uma retração em mudanças de camada." msgid "Retraction Length" msgstr "Distância de retração" @@ -15553,7 +15429,6 @@ msgstr "Distância de retração" msgid "Some amount of material in extruder is pulled back to avoid ooze during long travel. Set zero to disable retraction." msgstr "Alguma quantidade de material na extrusora é puxada para dentro para evitar vazamento durante deslocamentos longos. Defina zero para desativar a retração." -# TODO: Review, changed by lang refactor. PR 14254 msgid "Long retraction when cut (beta)" msgstr "Retração longa quando cortado (beta)" @@ -15575,9 +15450,8 @@ msgstr "Distância de retração na troca de extrusora" msgid "Z-hop height" msgstr "Altura de Z-hop" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Whenever there is a retraction, the nozzle is lifted a little to create clearance between the nozzle and the print. This prevents the nozzle from hitting the print when traveling more. Using spiral lines to lift z can prevent stringing." -msgstr "Sempre que a retração é feita, o bico é levantado um pouco para criar folga entre o bico e a impressão. Isso evita que o bico atinja a impressão ao se mover. Usar linhas em espiral para levantar z pode evitar stringing." +msgstr "Sempre que há uma retração, o bico é levantado um pouco para criar folga entre o bico e a impressão. Isso evita que o bico atinja a impressão ao se mover. Usar linhas em espiral para levantar Z pode evitar stringing." msgid "Z-hop lower boundary" msgstr "Limite inferior do Z-hop" @@ -15669,14 +15543,12 @@ msgstr "Quando a retração é compensada após o movimento de deslocamento, a e msgid "When the retraction is compensated after changing tool, the extruder will push this additional amount of filament." msgstr "Quando a retração é compensada após a troca de ferramenta, a extrusora empurrará essa quantidade adicional de filamento." -# TODO: Review, changed by lang refactor. PR 14254 msgid "Retraction speed" msgstr "Velocidade de retração" msgid "Speed for retracting filament from the nozzle." msgstr "Velocidade para retração do filamento do bico." -# TODO: Review, changed by lang refactor. PR 14254 msgid "Deretraction speed" msgstr "Velocidade de desretração" @@ -15684,10 +15556,10 @@ msgid "Speed for reloading filament into the nozzle. Zero means same speed of re msgstr "Velocidade para recarregar o filamento no bico. Zero significa mesma velocidade da retração." msgid "Deretraction speed (extruder change)" -msgstr "" +msgstr "Velocidade de desretração (troca de extrusora)" msgid "Speed for reloading filament into the nozzle when switching extruder." -msgstr "" +msgstr "Velocidade para recarregar o filamento no bico ao trocar de extrusora." msgid "Use firmware retraction" msgstr "Usar retração de firmware" @@ -15707,9 +15579,8 @@ msgstr "Desativar a geração do M73: Definir tempo restante de impressão no G- msgid "Seam position" msgstr "Posição da costura" -# TODO: Review, changed by lang refactor. PR 14254 msgid "This is the starting position for each part of the outer wall." -msgstr "A posição inicial para imprimir cada parte da parede externa." +msgstr "Esta é a posição inicial para cada parte da parede externa." msgid "Nearest" msgstr "Mais próximo" @@ -15746,13 +15617,13 @@ msgid "Scarf joint seam (beta)" msgstr "Costura em bisel (beta)" msgid "Use scarf joint to minimize seam visibility and increase seam strength." -msgstr "Use a junta em bisel para minimizar a visibilidade da costura e aumentar a resistência da costura." +msgstr "Use a costura em bisel para minimizar a visibilidade da costura e aumentar a resistência da costura." msgid "Conditional scarf joint" -msgstr "Junta em bisel condicional" +msgstr "Costura em bisel condicional" msgid "Apply scarf joints only to smooth perimeters where traditional seams do not conceal the seams at sharp corners effectively." -msgstr "Aplique juntas em bisel apenas em perímetros suaves onde costuras tradicionais não escondem as costuras em cantos agudos de forma eficaz." +msgstr "Aplique costuras em bisel apenas em perímetros suaves onde costuras tradicionais não escondem as costuras em cantos agudos de forma eficaz." msgid "Conditional angle threshold" msgstr "Limiar de ângulo condicional" @@ -15769,19 +15640,19 @@ msgstr "Limiar de saliência condicional" #, no-c-format, no-boost-format msgid "This option determines the overhang threshold for the application of scarf joint seams. If the unsupported portion of the perimeter is less than this threshold, scarf joint seams will be applied. The default threshold is set at 40% of the external wall's width. Due to performance considerations, the degree of overhang is estimated." -msgstr "Esta opção determina o limiar de saliência para a aplicação de juntas em bisel. Se a parte sem suporte do perímetro for inferior a esse limiar, as costuras junta em bisel serão aplicadas. O limiar padrão é definido em 40% da largura da parede externa. Devido a considerações de desempenho, o grau de saliência é estimado." +msgstr "Esta opção determina o limiar de saliência para a aplicação de costuras em bisel. Se a parte sem suporte do perímetro for inferior a esse limiar, as costuras costura em bisel serão aplicadas. O limiar padrão é definido em 40% da largura da parede externa. Devido a considerações de desempenho, o grau de saliência é estimado." msgid "Scarf joint speed" -msgstr "Velocidade da junta em bisel" +msgstr "Velocidade da costura em bisel" msgid "This option sets the printing speed for scarf joints. It is recommended to print scarf joints at a slow speed (less than 100 mm/s). It's also advisable to enable 'Extrusion rate smoothing' if the set speed varies significantly from the speed of the outer or inner walls. If the speed specified here is higher than the speed of the outer or inner walls, the printer will default to the slower of the two speeds. When specified as a percentage (e.g., 80%), the speed is calculated based on the respective outer or inner wall speed. The default value is set to 100%." -msgstr "Esta opção define a velocidade de impressão para as juntas em bisel. É recomendável imprimir as juntas em bisel em uma velocidade baixa (menor que 100 mm/s). Também é aconselhável habilitar 'Suavização da taxa de extrusão' se a velocidade definida variar significativamente da velocidade das paredes externas ou internas. Se a velocidade especificada aqui for maior que a velocidade das paredes externas ou internas, a impressora utilizará a mais lenta das duas velocidades. Quando especificado como uma porcentagem (por exemplo, 80%), a velocidade é calculada com base na velocidade do perímetro externo ou interna respectiva. O valor padrão é definido como 100%." +msgstr "Esta opção define a velocidade de impressão para as costuras em bisel. É recomendável imprimir as costuras em bisel em uma velocidade baixa (menor que 100 mm/s). Também é aconselhável habilitar 'Suavização da taxa de extrusão' se a velocidade definida variar significativamente da velocidade das paredes externas ou internas. Se a velocidade especificada aqui for maior que a velocidade das paredes externas ou internas, a impressora utilizará a mais lenta das duas velocidades. Quando especificado como uma porcentagem (por exemplo, 80%), a velocidade é calculada com base na velocidade do perímetro externo ou interna respectiva. O valor padrão é definido como 100%." msgid "Scarf joint flow ratio" -msgstr "Taxa de fluxo da junta em bisel" +msgstr "Taxa de fluxo da costura em bisel" msgid "This factor affects the amount of material for scarf joints." -msgstr "Este fator afeta a quantidade de material para as juntas em bisel." +msgstr "Este fator afeta a quantidade de material para as costuras em bisel." msgid "Scarf start height" msgstr "Altura inicial do bisel" @@ -15812,10 +15683,10 @@ msgid "Minimum number of segments of each scarf." msgstr "Número mínimo de segmentos de cada bisel." msgid "Scarf joint for inner walls" -msgstr "Junta em bisel em paredes internas" +msgstr "Costura em bisel em paredes internas" msgid "Use scarf joint for inner walls as well." -msgstr "Usar junta em bisel em paredes internas também." +msgstr "Usar costura em bisel em paredes internas também." msgid "Role base wipe speed" msgstr "Velocidade de limpeza baseada na função" @@ -15850,9 +15721,8 @@ msgstr "A velocidade de limpeza é determinada pela velocidade especificada nest msgid "Skirt distance" msgstr "Distância da saia" -# TODO: Review, changed by lang refactor. PR 14254 msgid "This is the distance from the skirt to the brim or the object." -msgstr "A distância da saia para a borda ou objeto." +msgstr "Esta é a distância da saia para a borda ou objeto." msgid "Skirt start point" msgstr "Ponto de partida da saia" @@ -15863,7 +15733,6 @@ msgstr "Ângulo do centro do objeto ao ponto inicial da saia. Zero é a posiçã msgid "Skirt height" msgstr "Altura da saia" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Number of skirt layers: usually only one" msgstr "Número de camadas de saia: geralmente apenas uma" @@ -15902,9 +15771,8 @@ msgstr "Por objeto" msgid "Skirt loops" msgstr "Voltas da saia" -# TODO: Review, changed by lang refactor. PR 14254 msgid "This is the number of loops for the skirt. 0 means the skirt is disabled." -msgstr "Número de voltas da saia. Zero significa desativar a saia." +msgstr "Este é o número de voltas para a saia. Zero significa desativar a saia." msgid "Skirt speed" msgstr "Velocidade da saia" @@ -15932,7 +15800,6 @@ msgstr "A velocidade de impressão no G-code exportado será reduzida quando o t msgid "Minimum sparse infill threshold" msgstr "Limiar mínimo de preenchimento esparso" -# TODO: Review, changed by lang refactor. PR 14254 msgid "Sparse infill areas which are smaller than this threshold value are replaced by internal solid infill." msgstr "Áreas de preenchimento esparso menores que este valor limiar são substituídas por preenchimento sólido interno." @@ -15960,13 +15827,11 @@ msgstr "" msgid "Line width of internal solid infill. If expressed as a %, it will be computed over the nozzle diameter." msgstr "Largura da linha de preenchimento sólido interno. Se expresso como uma %, será calculado sobre o diâmetro do bico." -# TODO: Review, changed by lang refactor. PR 14254 msgid "This is the speed for internal solid infill, not including the top or bottom surface." -msgstr "Velocidade de preenchimento sólido interno, não a superfície superior e inferior." +msgstr "Esta é a velocidade de preenchimento sólido interno, não incluindo a superfície superior e inferior." -# TODO: Review, changed by lang refactor. PR 14254 msgid "This enables spiraling, which smooths out the Z moves of the outer contour and turns a solid model into a single walled print with solid bottom layers. The final generated model has no seam." -msgstr "A espiralização suaviza os movimentos Z do contorno externo. E transforma um modelo sólido em uma impressão de parede única com camadas inferiores sólidas. O modelo final gerado não tem costura." +msgstr "Isso ativa a espiralização, que suaviza os movimentos Z do contorno externo e transforma um modelo sólido em uma impressão de parede única com camadas inferiores sólidas. O modelo final gerado não tem costura." msgid "Smooth Spiral" msgstr "Espiral Suave" @@ -15995,9 +15860,8 @@ msgstr "Taxa de fluxo de acabamento de espiral" msgid "Sets the finishing flow ratio while ending the spiral. Normally the spiral transition scales the flow ratio from 100% to 0% during the last loop which can in some cases lead to under extrusion at the end of the spiral." msgstr "Define a taxa de fluxo de acabamento ao finalizar a espiral. Normalmente a transição em espiral dimensiona a taxa de fluxo de 100% a 0% durante a última volta, o que pode em alguns casos levar à subextrusão no final da espiral." -# TODO: Review, changed by lang refactor. PR 14254 msgid "If smooth or traditional mode is selected, a timelapse video will be generated for each print. After each layer is printed, a snapshot is taken with the chamber camera. All of these snapshots are composed into a timelapse video when printing completes. If smooth mode is selected, the toolhead will move to the excess chute after each layer is printed and then take a snapshot. Since the melt filament may leak from the nozzle during the process of taking a snapshot, a prime tower is required for smooth mode to wipe the nozzle." -msgstr "Se o modo suave ou tradicional for selecionado, um vídeo em timelapse será gerado para cada impressão. Após cada camada ser impressa, uma captura de tela é feita com a câmera da câmara. Todas essas capturas de tela são compostas em um vídeo em timelapse quando a impressão é concluída. Se o modo suave for selecionado, o cabeçote se moverá para fora após cada camada ser impressa e então tirará uma captura de tela. Como o filamento derretido pode vazar do bico durante o processo de tirar uma captura de tela, é necessário uma torre de purga para o modo suave para limpar o bico." +msgstr "Se o modo suave ou tradicional for selecionado, um vídeo em timelapse será gerado para cada impressão. Após cada camada ser impressa, uma foto é feita com a câmera da câmara. Todas essas capturas de tela são compostas em um vídeo em timelapse quando a impressão é concluída. Se o modo suave for selecionado, o cabeçote se moverá para a calha de excesso após cada camada ser impressa e então tirará uma foto. Como o filamento derretido pode vazar do bico durante o processo de tirar uma foto, é necessário uma torre de purga para o modo suave para limpar o bico." msgid "Traditional" msgstr "Tradicional" @@ -16006,10 +15870,10 @@ msgid "Smooth" msgstr "Suave" msgid "Farthest point timelapse" -msgstr "" +msgstr "Timelapse no ponto mais distante" msgid "When enabled, the timelapse snapshot is taken at the farthest point from camera instead of traveling to the wipe tower or excess chute. Only effective in traditional timelapse mode on non-I3 printers." -msgstr "" +msgstr "Quando ativado, a foto de timelapse é capturada no ponto mais distante da câmera, em vez de a cabeça de impressão se deslocar até a torre de purga ou a calha de descarte. Funciona apenas no modo de timelapse tradicional em impressoras que não sejam do tipo I3." msgid "Temperature variation" msgstr "Variação de temperatura" @@ -16039,13 +15903,11 @@ msgstr "Código G escrito no início do arquivo de saída, antes de qualquer out msgid "Start G-code" msgstr "G-code Inicial" -# TODO: Review, changed by lang refactor. PR 14254 msgid "G-code added when starting a print." -msgstr "G-code inicial ao iniciar a impressão completa." +msgstr "G-code adicionado quando inicia uma impressão." -# TODO: Review, changed by lang refactor. PR 14254 msgid "G-code added when the printer starts using this filament" -msgstr "G-code inicial ao iniciar a impressão com este filamento" +msgstr "G-code adicionado quando a impressora começa a usar este filamento" msgid "Single Extruder Multi Material" msgstr "Multimaterial com Extrusora Única" @@ -16201,13 +16063,13 @@ msgid "Top Z distance" msgstr "Distância Z superior" msgid "Z gap between the support's top and object." -msgstr "Espaço Z entre o topo do suporte e o objeto." +msgstr "Vão Z entre o topo do suporte e o objeto." msgid "Bottom Z distance" msgstr "Distância Z inferior" msgid "Z gap between the object and the support bottom. If Support Top Z Distance is 0 and the bottom has interface layers, this value is ignored and the support is printed in direct contact with the object (no gap)." -msgstr "Espaço Z entre o objeto e a base do suporte. Se a Distância Z Superior do Suporte for 0 e a base tiver camadas de interface, este valor é ignorado e o suporte é impresso em contato direto com o objeto (sem espaço)." +msgstr "Vão Z entre o objeto e a base do suporte. Se a Distância Z Superior do Suporte for 0 e a base tiver camadas de interface, este valor é ignorado e o suporte é impresso em contato direto com o objeto (sem espaço)." msgid "Support/raft base" msgstr "Base de suporte/jangada" @@ -16632,10 +16494,10 @@ msgid "Selects how the wipe-tower prime and flush volumes are computed on multi- msgstr "" msgid "Saving" -msgstr "" +msgstr "Salvando" msgid "Fast" -msgstr "" +msgstr "Rápido" msgid "This is the width of prime towers." msgstr "Esta é a largura das torres de purga." @@ -17856,7 +17718,7 @@ msgstr "" "\n" "Observe que há alguns casos que podem tornar os resultados da calibração não confiáveis, como adesão insuficiente na placa de impressão. A melhoria da adesão pode ser obtida lavando a placa de impressão ou aplicando cola. Para obter mais informações sobre este tópico, consulte nosso Wiki.\n" "\n" -"Os resultados da calibração têm cerca de 10 por cento de jitter em nosso teste, o que pode fazer com que o resultado não seja exatamente o mesmo em cada calibração. Ainda estamos investigando a causa raiz para fazer melhorias com novas atualizações." +"Os resultados da calibração têm cerca de 10 por cento de tremor em nosso teste, o que pode fazer com que o resultado não seja exatamente o mesmo em cada calibração. Ainda estamos investigando a causa raiz para fazer melhorias com novas atualizações." msgid "When to use Flow Rate Calibration" msgstr "Quando usar a Calibração da Taxa de Fluxo" @@ -17870,7 +17732,7 @@ msgid "" msgstr "" "Depois de usar a Calibração de Dinâmica de Fluxo, ainda pode haver alguns problemas de extrusão, como:\n" "1. Superextrusão: excesso de material no objeto impresso, formando grumos ou espinhas, ou as camadas parecem mais espessas do que o esperado e não uniformes\n" -"2. Subextrusão: camadas muito finas, resistência fraca do preenchimento ou vazios na camada superior do modelo, mesmo ao imprimir lentamente\n" +"2. Subextrusão: camadas muito finas, resistência fraca do preenchimento ou vãos na camada superior do modelo, mesmo ao imprimir lentamente\n" "3. Baixa Qualidade de Superfície: a superfície de suas impressões parece áspera ou irregular\n" "4. Integridade Estrutural Fraca: as impressões quebram facilmente ou não parecem tão robustas quanto deveriam" @@ -19839,7 +19701,7 @@ msgid "Enable smart filament assign: Assign one filament to multiple nozzles to msgstr "" msgid "Fila Saving" -msgstr "" +msgstr "Econo Filamento" msgid "Don't remind me again" msgstr "Não me avise novamente" @@ -19854,7 +19716,7 @@ msgid "Convenience Mode" msgstr "Modo Conveniência" msgid "Custom Mode" -msgstr "Modo Customizado" +msgstr "Modo Personalizado" msgid "Generates filament grouping for the left and right nozzles based on the most filament-saving principles to minimize waste." msgstr "Gera agrupamento de filamentos para os bicos esquerdo e direito com base nos princípios de máxima economia de filamento para minimizar o desperdício." @@ -20159,7 +20021,7 @@ msgid "Show details" msgstr "Mostrar detalhes" msgid "Hide details" -msgstr "" +msgstr "Esconder detalhes" msgid "Version to install:" msgstr "Versão para instalar:" @@ -20187,7 +20049,7 @@ msgid "(Latest)" msgstr "(Mais recente)" msgid "(installed)" -msgstr "" +msgstr "(instalado)" msgid "The Bambu Network Plug-in has been installed successfully." msgstr "O Plug-in de Rede Bambu foi instalado com sucesso." @@ -20327,13 +20189,13 @@ msgid "Filament Drying Settings" msgstr "" msgid "Stopping" -msgstr "" +msgstr "Parando" msgid "Unable to dry temporarily due to ..." msgstr "" msgid "Drying Error" -msgstr "" +msgstr "Erro de Secagem" msgid "Please check the Assistant for troubleshooting" msgstr "" @@ -20352,16 +20214,16 @@ msgid "Back" msgstr "" msgid "Drying-Heating" -msgstr "" +msgstr "Secando-Aquecendo" msgid "Drying-Dehumidifying" -msgstr "" +msgstr "Secando-Desumidificando" msgid " maximum drying temperature is " -msgstr "" +msgstr " temperatura máxima de secagem é " msgid " minimum drying temperature is " -msgstr "" +msgstr " temperatura mínima de secagem é " msgid "This filament may not be completely dried." msgstr "Este filamento pode não estar completamente seco." @@ -20409,7 +20271,7 @@ msgid " The AMS might be in use during Task." msgstr "" msgid " Firmware update in progress, please wait..." -msgstr "" +msgstr " Atualização de firmware em progresso, por favor aguarde..." msgid " Please plug in the power and then use the drying function." msgstr "" @@ -20421,7 +20283,7 @@ msgid "System is busy" msgstr "O sistema está ocupado" msgid " Initiating other drying processes, please wait a few seconds..." -msgstr "" +msgstr " Iniciando outros processos de secagem, por favor aguarde alguns segundos..." msgid "For better drying results, remove the filament and allow it to rotate." msgstr "Para obter melhores resultados de secagem, remova o filamento e permita que ele gire." @@ -20665,8 +20527,8 @@ msgid "" "Z seam location\n" "Did you know that you can customize the location of the Z seam, and even paint it on your print, to have it in a less visible location? This improves the overall look of your model. Check it out!" msgstr "" -"Local da costura\n" -"Você sabia que pode customizar a posição da costura, e até mesmo pintá-la na sua peça, para tê-la em um lugar menos visível? Isso vai aumentar a qualidade geral do seu modelo. Tente!" +"Local da costura Z\n" +"Você sabia que pode personalizar a posição da costura Z, e até mesmo pintá-la na sua peça, para tê-la em um lugar menos visível? Isso vai aumentar a qualidade geral do seu modelo. Tente!" #: resources/data/hints.ini: [hint:Fine-tuning for flow rate] msgid "" @@ -20684,8 +20546,7 @@ msgstr "" "Divida suas impressões em placas\n" "Você sabia que pode dividir um modelo que tem diversas peças em placas individuais distintas prontas para imprimir? Isso simplifica o processo de manter o controle de todas as peças." -#: resources/data/hints.ini: [hint:Speed up your print with Adaptive Layer -#: Height] +#: resources/data/hints.ini: [hint:Speed up your print with Adaptive Layer Height] msgid "" "Speed up your print with Adaptive Layer Height\n" "Did you know that you can print a model even faster by using the Adaptive Layer Height option? Check it out!" @@ -20757,8 +20618,7 @@ msgstr "" "Melhorar a resistência\n" "Você sabia que pode usar mais voltas de parede e maior densidade de preenchimento esparso mais alta para melhorar a resistência do modelo?" -#: resources/data/hints.ini: [hint:When do you need to print with the printer -#: door opened] +#: resources/data/hints.ini: [hint:When do you need to print with the printer door opened] msgid "" "When do you need to print with the printer door opened?\n" "Did you know that opening the printer door can reduce the probability of extruder/hotend clogging when printing lower temperature filament with a higher enclosure temperature? There is more info about this in the Wiki." @@ -20774,33 +20634,6 @@ msgstr "" "Evitar empenamento\n" "Você sabia que ao imprimir materiais propensos ao empenamento como ABS, aumentar adequadamente a temperatura da mesa aquecida pode reduzir a probabilidade de empenamento?" -#~ msgid "Enter" -#~ msgstr "Enter" - -#~ msgid "Esc" -#~ msgstr "Esc" - -#~ msgid "Del" -#~ msgstr "Del" - -#~ msgid "Arrow Up" -#~ msgstr "Seta para cima" - -#~ msgid "Arrow Down" -#~ msgstr "Seta para baixo" - -#~ msgid "Arrow Left" -#~ msgstr "Seta para esquerda" - -#~ msgid "Arrow Right" -#~ msgstr "Seta para direita" - -#~ msgid "End" -#~ msgstr "Fim" - -#~ msgid "Hotend" -#~ msgstr "Extrusora" - #~ msgid "Skip for Now" #~ msgstr "Pular por Enquanto" @@ -22354,7 +22187,7 @@ msgstr "" #~ msgstr "Tempo para o firmware da impressora (ou a Unidade de Material Multi 2.0) descarregar um filamento durante uma troca de ferramenta (ao executar o código T). Este tempo é adicionado ao tempo total de impressão pelo estimador de tempo do G-code." #~ msgid "Filter out gaps smaller than the threshold specified" -#~ msgstr "Filtrar vazios menores que o limiar especificado" +#~ msgstr "Filtrar vãos menores que o limiar especificado" #~ msgid "" #~ "Enable this option for chamber temperature control. An M191 command will be added before \"machine_start_gcode\"\n"