mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-12 10:21:17 +00:00
e95a7654593606d95e85b102e7a50d74a12bbc94
32277
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e95a765459 |
Post clang-tidy's Fix as Review Suggestions Instead of Pushing It
A failed clang-tidy check now runs once more with --fix and uploads the patch as the clang-tidy-fix artifact; the check still fails. /bot fix-tidy downloads that patch for the PR's latest commit, checks it adds only includes to C++ files the PR changes, and posts one review: a one-click suggestion for each include on a line the PR's diff shows, and the whole patch with a git apply command. Nothing is pushed and no PR code runs in the bot, so the PR's author can run it too and no token is needed. |
||
|
|
5b68ebad0f | Temporary clang-tidy Probe: Use std::string_view Without Its Include | ||
|
|
b10cd14372 |
Add a /bot fix-tidy Command That Pushes clang-tidy's Missing Includes
A maintainer comments /bot fix-tidy on a PR. A read-only job runs scripts/run_clang_tidy.sh --fix on the PR's merge commit and returns the patch; a second job, which never runs the PR's code, accepts it only when it adds nothing but #include lines to C++ files the PR already changes, then commits it on the PR head and pushes with CLANG_TIDY_BOT_TOKEN. |
||
|
|
d2675f15d1 |
Fix Qidi start G-code ignoring the selected plate type (#16372)
# Description With "Support multi bed types" enabled on a Qidi printer, the first-layer bed temperature was always taken from the Smooth PEI / High Temp Plate setting, whichever plate type was selected. The slicer computes the right value (`first_layer_bed_temperature` in the G-code footer follows the selected plate), but the machine start G-code of these presets hardcoded `[hot_plate_temp_initial_layer]`: - `fdm_qidi_x3_common` (base of X-Max 3, X-Plus 3, X-Smart 3, X-Plus 4, Q1 Pro) - `Qidi Q1 Pro 0.4 nozzle` - `Qidi X-CF Pro 0.4 nozzle` They now use `[bed_temperature_initial_layer_single]`, which follows the selected plate type, as most other profiles do. The Qidi bundle version is bumped to `02.04.00.17`. With "Support multi bed types" disabled, these printers use the default plate (Smooth PEI / High Temp Plate), so the temperature is unchanged there. # Screenshots/Recordings/Graphs N/A — profile change only. ## Tests `python3 scripts/orca_profile_tool.py check` passes. Sliced on Qidi X-Max 3 0.4 nozzle with "Support multi bed types" enabled, `0.20mm Standard @Qidi XMax3`, `Generic PLA @Qidi`, with a different bed temperature set for each plate type (first layer / other layers). Before, with first layer set to 50–55 °C: `M190 S54` for every plate type. After, a two-layer print: | Plate type | Set (first / other) | `M190` at start | `M140` at layer 2 | | --- | --- | --- | --- | | Cool Plate SuperTack | 51 / 61 | 51 | 61 | | Cool Plate | 52 / 62 | 52 | 62 | | Textured Cool Plate | 53 / 63 | 53 | 63 | | Engineering Plate | 54 / 64 | 54 | 64 | | Smooth PEI / High Temp Plate | 55 / 65 | 55 | 65 | | Textured PEI Plate | 56 / 66 | 56 | 66 | 🤖 Generated with [Claude Code](https://claude.com/claude-code)4nightly-builds |
||
|
|
5371c85362 |
Refresh Dark Mode Colours When the System Theme Differs at Startup (#16390)
On Linux and macOS the app follows the system theme: at startup it overwrites the saved dark_color_mode with the system appearance. The label colours and the cached dark mode flag were already set from the saved value by then, and the later "changed" check reads the mode only after the overwrite, so it never fires. For that session every dialog that maps its colours through UpdateDlgDarkUI keeps the old mode, e.g. white dialogs on a dark main window after switching the desktop to dark, or on a fresh install on a dark desktop. Refresh the label colours and the flag when the system theme differs from the saved mode. |
||
|
|
a243941b72 |
Add favorite filaments to AMS filament dropdown (#13946)
# Description ## Summary: Adds favorite filament support to the AMS material selection dropdown. Users can now click a star icon next to a selectable filament in the AMS Materials Setting dialog to mark it as a favorite. Favorited filaments appear in a new `Favorite filaments` section at the top of the dropdown, while all compatible filaments remain available in the `All filaments` section. This PR also adds generic right-side action icon support to Orca's custom dropdown widget: - Adds optional action icons to `DropDown::Item`. - Adds a new `EVT_DROPDOWN_ITEM_ACTION` event for clicks on the action icon. - Relays the action event through `ComboBox`. - Adds `ComboBox::RefreshDropdownIfOpen()` so callers can refresh an already-open dropdown without changing normal `ForceDropdownOpen()` behavior. The AMS favorites implementation: - Uses existing bundled star assets: - `score_star_dark` for non-favorited filaments - `score_star_light` for favorited filaments - Stores favorite filament IDs in app config under `ams_favorite_filament_ids`. - Stores stable filament IDs instead of display names - Uses existing C-style string escaping helpers for persistence. - Keeps Bambu/RFID/read-only slot behavior unchanged No new external dependencies. **Note:** `AMSMaterialsSetting` now uses Orca's custom `ComboBox` on macOS as well, because the favorite-star UI depends on the custom `DropDown` item renderer. I was able to test this on Windows, but macOS/Linux smoke testing is still needed and would be appreciated. # Screenshots/Recordings: https://github.com/user-attachments/assets/41e881be-d4de-4a7d-8f99-4297567adef2 <img width="494" height="36" alt="Screenshot 2026-05-30 103432" src="https://github.com/user-attachments/assets/e7c34ed3-db7a-4705-a1cd-4bfe7ac1616a" /> ## Tests Tested on Windows: - Built OrcaSlicer locally - Opened the Device tab with Bambu Lab printer with AMS - Opened the AMS Materials Setting dialog from an editable AMS slot - Confirmed clicking a star toggles favorite state. - Star clicks do not select the filament row. - Dropdown remains open after toggling a favorite. - Confirmed favorited filaments appear under `Favorite filaments`. - All compatible filaments remain visible under `All filaments`. - Confirmed Filament still selected as before - Confirmed favorites persist after closing and reopening the dialog. - Confirmed favorites persist after restarting OrcaSlicer. - Confirmed the favorite IDs are saved in `OrcaSlicer.conf` under `ams_favorite_filament_ids`. - Bambu/RFID/read-only filament slots continue using the existing read-only display behavior. Not tested: - macOS - Linux/Wayland These should be tested, `ComboBox` behavior not tested on macOS. [How to Download Pull Requests Artifacts for Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts) |
||
|
|
6a1a5d51e9 | Show an outline star for filaments that are not favorites | ||
|
|
e625845b34 |
Keep Snapshot Suppression for the Whole Object List Edit (#16388)
Five Plater::TakeSnapshot guards in GUI_ObjectList were unnamed temporaries, so snapshots were allowed again as soon as the snapshot was taken. Naming them keeps the suppression until the edit is done, as everywhere else. |
||
|
|
01b340dee1 | Fix regressions in the AMS filament favorites dropdown | ||
|
|
4c2a63e11e |
Copy Plate Pick and Pattern File Paths When Loading a G-code 3MF (#16387)
The importer assigned pick_file.empty() and pattern_bbox_file.empty(), a bool, to the string fields, storing a one-character string instead of the path. |
||
|
|
f8cf34c55b |
Reset the Fan Mover Instead of Releasing It (#16386)
Clearing per-export state called unique_ptr::release(), which gives up ownership without deleting. Every export uses a new GCode, so the pointer is still empty there today and nothing leaks; reset() states the intent and would free the object if a GCode were ever reused. |
||
|
|
d007134001 | fix: update translation for Assistant(HMS) in Chinese localization | ||
|
|
00d11a7ba0 | Merge branch 'main' into UI-changes | ||
|
|
70f518d87a |
Added new profiles for the FELIX Printers vendor (#16314)
* Added new profiles for the FELIX Printers vendor * fixed all errors from profiles |
||
|
|
f4bde2da0a | Fix missing plate bed override normalization in Configuration Wizard (#16365) | ||
|
|
d5a11eb076 | ci: cancel Build all runs of closed pull requests (#16380) | ||
|
|
461275d9bb | ci: build pushes that change deps_src or the build scripts (#16378) | ||
|
|
95d3e8eafc |
Queue Pull Request macOS Builds for a Free Runner (#16343)
* ci: wait for the base branch's macOS deps instead of rebuilding them * Hold Pull Request macOS Builds Until a Runner Is Free A pull request's macOS build now waits in one repo-wide line, from a Linux job, until the macOS runners every active Build all run holds or still needs leave two free. A run is let in whole: its first arch takes both runners and its second goes straight through, since letting arches in one at a time deadlocks with every runner held by a run waiting for its other arch. Push, nightly and manual runs reserve two runners until their macOS work is done, and a pull request run keeps its two until its universal build and tests finish, so jobs that never pass the line are accounted for. Queued jobs count as busy. API errors and a four-hour limit let the build in rather than block it. Co-authored-by: raistlin7447 <kris.austin@gmail.com> * Count Pending Runs That Hold macOS Runners Co-authored-by: raistlin7447 <kris.austin@gmail.com> * Show the macOS Admission Checks Live in the Job Log Co-authored-by: raistlin7447 <kris.austin@gmail.com> * Let a Labelled Pull Request Go Ahead of the macOS Line A pull request labelled macos-priority when its run starts waits in its own line under the same rule, and the normal line counts every priority run still waiting as holding two runners, so the next free runners go to it. It still waits for runners in use and for main's reserve. Co-authored-by: raistlin7447 <kris.austin@gmail.com> * Let Each macOS Arch In on Its Own Without Holding an Idle Runner Letting a pull request run in whole deadlocks when its second arch reaches the line behind another pull request: the run holds two runners, the pull request in front waits for them, and the second arch never gets to check. Each arch now waits on its own. A run with one arch let in holds a runner only until that arch's app build is done, and a run with both holds two until its universal build and tests finish, so whoever is first in line always gets in eventually. Co-authored-by: raistlin7447 <kris.austin@gmail.com> * Stop Holding Runners for a Pull Request's Universal Build and Tests Holding two runners from a pull request's second arch until its universal build and tests finished kept one idle through the slower arch's build. Replaying the week of 10-03 against the real queue, that idle capacity pushed the median pull request from under three hours to over sixteen. Those jobs take minutes, so they now queue like any other job, and a pull request holds a runner only for an arch that is still building. Each arch then always needs exactly one runner. Co-authored-by: raistlin7447 <kris.austin@gmail.com> * Count Runs That Move Between Status Lists A run is listed as pending while any of its jobs waits in a concurrency group, so it moves to in_progress the moment its last gate leaves the line. A check made in those seconds read the in_progress list before the move and the pending list after it, missed the run, and let in a second arch the limit had no room for. Runs created in the last day are now listed by creation time as well, and the run doing the check is read directly. Co-authored-by: raistlin7447 <kris.austin@gmail.com> * Log API Calls and the Rate Limit Left on Each macOS Admission Check The calls per check grow with the number of active runs, and the token's hourly limit is shared by the whole repository, so each check now logs how many calls it made and what the last response says is left. * Give the macOS Admission Jobs Fixed Names A skipped job is listed under its unevaluated name, so every Linux and Windows leg showed the label expression. Split the waiting job into a normal and a priority job with fixed names, sharing their steps through a YAML anchor. --------- Co-authored-by: raistlin7447 <kris.austin@gmail.com> |
||
|
|
52eeb0f228 | Fix arc fitting with overhang fan control (#16297) | ||
|
|
8145b0792f | Free macOS Disk Space Only When the Build Might Not Fit (#16364) | ||
|
|
2ccd73759f |
Show English context for Speed Dial search matches
Display English action names and breadcrumbs for English-only matches Show English tab titles when tabs match only by their English names |
||
|
|
cab7a7d75f | Translate the web dialogs and pages through the .po catalogs (#16363) | ||
|
|
bd125d32da | Support searching the Speed Dial by English names in any UI language (#16369) | ||
|
|
7369674d8f | Add #pragma once to Headers Without an Include Guard (#16362) | ||
|
|
70da7e89bb | Release the Slice Button's Mouse Capture When a Press Loses Its Release (#16358) | ||
|
|
a671bf605d |
Dismiss the Slice-Plate Popup on Every Path Into the Filament Grouping Dialog (#16359)
#16284 closed the Slice-plate hover popup only when the Slice button opened the filament grouping dialog. The dialog also opens from the plate's filament grouping icon, the preview legend, slicing notification links and the send dialog, and a popup still shown on those paths keeps the mouse capture on macOS and starves the modal dialog of clicks. Dismiss it in FilamentMapDialog::ShowModal() instead, so every path is covered. The popup could also stay up on its own on macOS: an enter event stopped its close timer, and when the timer fired with the cursor inside it returned without re-arming, so without a later leave event the popup never closed. On macOS the timer now keeps running while the popup is shown and closes it once the cursor is outside. The popup also stops and frees its timer when destroyed. |
||
|
|
bae8f64b8b |
Complete and refine Simplified Chinese translations (#16356)
* Complete Simplified Chinese translations * Fix Simplified Chinese 3D view, camera and extruder terms * Use Chinese CAD view names on the navigator cube |
||
|
|
2df07b7ed8 | Verify the TLS Certificate When Downloading a Cloud Plugin (#16253) | ||
|
|
295094a594 |
Pack bed texture and model into printer bundles (#15965)
* feat(preset): pack bed texture and model into printer bundles Export copies bed_custom_texture and bed_custom_model into the .orca_printer zip under bed/. The manifest lists them in printer_asset. Import extracts each file into the bundle folder and rewrites the preset to that path. A missing file is skipped. A path that leaves the bundle directory is rejected. An old bundle with no printer_asset imports as before. * Add includes flagged by clang-tidy misc-include-cleaner * Gate bed asset packing on an export checkbox and key printer_asset per printer The Export Configs checkbox can leave a custom bed out of a shared printer bundle. printer_asset is keyed by each printer_config zip entry, with files under bed/<n>/, so a later multi-printer bundle does not need a second manifest. The unshipped flat printer_asset object is not read. --------- Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com> |
||
|
|
210bc927bc | Revert "Add Thai Localization (Educational / Hybrid Translation)" (#16349) | ||
|
|
00cfb5b259 | Refresh translation catalogs and fix xgettext warnings (#16346) | ||
|
|
2bd105b0ce |
Add Thai Localization (Educational / Hybrid Translation) (#15940)
Hi OrcaSlicer Team,I have completed the Thai localization for OrcaSlicer (over 7,000 lines).Please note: This is a "Hybrid Translation" designed specifically for educational purposes. Instead of direct literal translations (which often confuse beginners due to technical jargon), I have included brief explanations of the mechanical and physics principles directly within the UI strings.Example: For "TPU is not supported by AMS", the translation briefly explains why (because the material is soft, rubber-like, and will jam the long tubes).This approach was developed for the EDrobot project in Khon Kaen, Thailand to help STEM students and beginners understand 3D printing concepts immediately without a steep language barrier.[cite: 8] We focus on empowering students to learn robotics and 3D design effectively.[cite: 8] I understand this makes some strings longer than standard localizations, but it has proven incredibly helpful for beginners. I would be honored if you consider adding this to the official release as the Thai language option. I am happy to maintain this file for future updates.Thank you for building such an amazing slicer!Best regards,Chetsit Srichan (EDrobot) |
||
|
|
b81d761605 |
Refresh plate tab after bed type change (#16344)
Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com> |
||
|
|
6922edfc20 |
Fix Anycubic Vyper start G-code hotend wait (#16216)
* Fix Anycubic Vyper start gcode hotend wait * Bump Anycubic profile version --------- Co-authored-by: 陈诗健 <chenshijian@chenshijiandeMacBook-Air.local> Co-authored-by: SoftFever <softfeverever@gmail.com> Co-authored-by: SoftFever <103989404+SoftFever@users.noreply.github.com> |
||
|
|
7ec147603f | Default Bambu profiles to 3MF output (#14767) | ||
|
|
546b9c621d |
Fix Qidi start G-code ignoring the selected plate type
The start G-code of fdm_qidi_x3_common, Qidi Q1 Pro 0.4 nozzle and Qidi X-CF Pro 0.4 nozzle heated the bed to hot_plate_temp_initial_layer, so with "Support multi bed types" enabled every plate type got the Smooth PEI / High Temp Plate temperature. Use bed_temperature_initial_layer_single, which follows the selected plate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
8ee9e5915a | Fix Prusa CORE One pressure advance condition in filament profiles (#16093) | ||
|
|
cd5b786704 |
Add AnkerMake PLA+ filament profiles (Basic, Matte, Metallic, Silk, Glitter) (#16293)
# Description Adds Anker's own AnkerMake PLA+ filament line to the Anker vendor bundle: **Basic, Matte, Metallic, Silk, Glitter**. Until now the bundle only had `Generic PLA+ @Anker`. Each filament follows the existing Anker layout. A `@Anker base` profile (`instantiation: false`, inherits `Generic PLA+ @Anker base`, `filament_vendor: AnkerMake`) holds the settings. An instantiated `@Anker` profile holds only `compatible_printers`. `filament_id` and `setting_id` were minted with `scripts/orca_profile_tool.py generate-id`, and `Anker.json` was re-indexed with `update-index`. The Anker vendor version goes from `02.04.00.05` to `02.04.00.06`. ## Source of the values The values come from the AnkerMake filament profiles bundled with **eufyMake Studio** (Anker's PrusaSlicer 2.6 fork, AGPL-3.0, source at https://github.com/eufymake/eufyMake-PrusaSlicer-Release), in `profiles/Anker-ini/AnkerMake base/base.ini`. OrcaSlicer is also AGPL-3.0. Conversion notes: - PrusaSlicer keys are mapped to their Orca equivalents. For example, `bed_temperature`/`first_layer_bed_temperature` go to `hot_plate_temp*` and `textured_plate_temp*`, `fan_below_layer_time` goes to `fan_cooling_layer_time`, `slowdown_below_layer_time` goes to `slow_down_layer_time`, and `extrusion_multiplier` goes to `filament_flow_ratio`. Only keys that differ from the inherited `Generic PLA+ @Anker base` chain are written. - Where Studio sets retraction but leaves deretraction unset (Metallic, Silk), `filament_deretraction_speed` is set equal to the retraction speed. That matches Prusa's "0 = same as retract" meaning. - **Max volumetric speed is new.** Studio ships `filament_max_volumetric_speed = 0` (uncapped) for these filaments, and Orca has no equivalent. The caps were derived from the peak flow that Studio's own M5C "Fast" process pushes: Basic/Matte/Glitter 22 mm³/s, Metallic 20, Silk 13. Please treat these as the least-validated numbers in the PR. - Studio has pressure advance disabled for these filaments, so no PA values are carried over. - Colour variants are not included because they differ only in colour. `default_filament_colour` uses each family's base colour. - `compatible_printers` follows Studio's `compatible_printers_condition` for each family, limited to the 0.4/0.6 printers that `Generic PLA+ @Anker` covers: - Basic, Glitter: M5, M5 All-Metal, M5C at 0.4 and 0.6 - Silk: 0.4 on all three, plus M5C 0.6 and M5 All-Metal 0.6 (Studio excludes M5 0.6) - Matte, Metallic: 0.4 only (Studio restricts these to 0.4) - Studio also allows Basic on 0.2 nozzles. That variant is not added, because the 0.2 Generic profiles use a separate low-flow cap. # Screenshots/Recordings/Graphs N/A (profile-only change). ## Tests - `scripts/check_profile.sh` (local twin of the CI job, run against the full profile tree with the nightly macOS validator) passes all five checks: `profile_tool`, `validate_system`, `validate_slice`, `validate_filament_subtypes`, `validate_custom` (fixtures v1.9.0 to v2.4.2). - `python3 -m unittest discover -s scripts/tests -t scripts`: OK. - The submitter printed with these filament settings in eufyMake Studio on the M5C's stock brass 0.4 nozzle without issues. This Orca conversion has been loaded and sliced in Orca 2.4.2 (as user presets inheriting the Anker generic PLA+) but not yet printed on brass. The printer now has a different nozzle fitted. [How to Download Pull Requests Artifacts for Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts) 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
||
|
|
d715bb760c |
Let Plugin Windows Be Maximized on X11 (#16335)
wxGTK types every wxDialog as a dialog window, and Mutter offers no maximize for anything but a normal window, so the maximize button and shortcut did nothing on GNOME under X11. Modeless plugin windows are now normal windows, kept off the taskbar as before. Modal ones stay dialogs so the window manager keeps routing focus from the main window to them. |
||
|
|
de5f2cb3b9 |
Expose App, GL, Printer and Project Facts to Plugins (#16324)
* Expose App, GL, Printer and Project Facts to Plugins Plugins that help users file bug reports could only guess these from log lines: the app config is deny-listed, and the project path, a project export and the device list had no API. orca.host gains app_info(), gl_info() and selected_printer(), and Plater gains project_path() and export_3mf_copy(path). The copy export leaves the project's file name, saved state and model unchanged, does not write the signed-in account as the designer, and raises the audit event of open(path, "w") so the plugin gets the same permissions as for writing the file itself. * Remove app_language in Favour of app_info app_info() returns the same language. app_language() was added after the last release, so no released build has it. |
||
|
|
07a12f5fe4 |
Read a local plugin's version from its file header (#16118)
* Read a local plugin's version from its file header read_install_state() let the sidecar's installed_version override the version parsed from the entry file's PEP 723 header. That is right for cloud plugins, whose header may be stale, but a local plugin has no other source of truth, so the UI kept showing the version recorded at first install after the file was edited. For local installs, use the header version. * Keep the cloud installed version of an unsubscribed plugin A cloud plugin kept as local after unsubscribing is rewritten with installed_from=local and no cloud_uuid, which the local-header rule could not tell apart from a real local install. Its installed_version is still the cloud one while the header may lag, so the next scan showed the stale header version. Record the conversion in the install state and exclude those plugins from the header rule. Add tests for a local plugin following its header and for an unsubscribed cloud plugin keeping its cloud version. * Add tests for the version of a local plugin A local plugin's version comes from its entry file header. Cover the case of a plugin edited after install, and a plugin unsubscribed from the cloud, which is now a normal local package. |
||
|
|
ea10c84d6b |
Fix scale and mirror of rotated mirrored objects (#16131)
* Fix decomposition of mirrored transformations computeRotationScaling() may place the mirror on any axis, giving unstable rotation/scale for mirrored matrices. Decompose them by flipping each local axis and choosing the one needing the least rotation (lowest axis on ties). Merge the extract helpers into a single extract_rotation_scale() and simplify contains_skew() to check that the scale is diagonal. set_scaling_factor() now preserves the mirror sign on the scaled axis. Selection::scale_and_translate() uses Transformation's rotation and scale instead of calling computeRotationScaling() directly, so scaling a mirrored instance adds no skew. Add tests for mirrored decomposition, scaling, and skew detection. * Use Eigen polar decomposition for instance scaling Selection::scale_and_translate now splits the instance matrix with computeRotationScaling instead of get_rotation_matrix(). Adds a Model test checking that an instance created from another's offset, scale, rotation and mirror reproduces its matrix (as Add instance and Fill bed do). * tidy |
||
|
|
e5d5fb47ca |
Fix BBL infill retraction reduction default (#16201)
# Description Restore `"reduce_infill_retraction": "1"` in the shared Bambu Lab process profile. The BambuStudio profile sync (#15851) replaced it with `reduce_infill_retraction_mode`, which OrcaSlicer does not support, causing the setting to fall back to disabled. This restores the pre-sync behavior and bumps the BBL bundle version to 02.08.00.13. # Validation - All 233 selectable BBL process presets inherit the restored setting with no other resolved settings change. - Static profile validation, system loading, filament validation, and all 1,263 slice cases pass. Fixes #16194 <!-- > A guide for users on how to download the artifacts from this PR. --> [How to Download Pull Requests Artifacts for Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts) |
||
|
|
63918b44a8 | Merge branch 'main' into anker-ankermake-pla-plus-filaments | ||
|
|
53c57c94fd |
Fix the 3D view stuttering every few seconds while signed in to Orca Cloud (#16340)
# Description Orbiting or panning the 3D view no longer hitches every five seconds on Windows and macOS while signed in to Orca Cloud. The stutter is a regression from #15710. No change to slicing or to the Orca Cloud connection status shown in the GUI. # Screenshots/Recordings/Graphs <!-- > Please attach relevant screenshots to showcase the UI changes. > Please attach images that can help explain the changes. --> ## Tests <!-- > Please describe the tests that you have conducted to verify the changes made in this PR. --> <!-- > A guide for users on how to download the artifacts from this PR. --> [How to Download Pull Requests Artifacts for Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts) |
||
|
|
53eeedb642 | Fix the 3D view stuttering every few seconds while signed in to Orca Cloud | ||
|
|
aa14598e32 |
iXex: Parallel Printing Support for IDEX and IQEX Printers (#13086)
## Summary This PR adds first-class parallel printing support to OrcaSlicer for printers with multiple independent X-axis carriages — the **IDEX** and **IQEX** hardware families. The feature is named **IMEX** (independent multi-extruder) internally, which captures the supported topology space more accurately than either acronym alone, and is exposed in the UI under the user-facing label **IDEX/IQEX Configuration**. It is designed to be printer-agnostic and firmware-flexible, with Klipper, RepRapFirmware, and Marlin all supported for per-tool Pressure Advance and per-layer temperature management. MMU/AFC setups where multiple logical filament slots share one physical extruder are supported via a `physical_extruder_map` profile option. Printers whose firmware handles copy/mirror placement internally — RepRapFirmware IDEX duplication mode on Flashforge Creator Pro 2 / Creator 3 Pro is the canonical example — are supported through a firmware-managed-zones flag that emits a centered single-half slice for the firmware to fan out. The implementation spans printer configuration, process settings UI, bed visualization, per-plate mode selection, ghost-object rendering with per-plate filament overrides, placement validation, pre-slice conflict warnings, G-code generation (with per-tool PA, per-layer temperature management, and optional center-origin slice frame), and layer preview animation. It supports five distinct topology paradigms: - **1 gantry × 2–4 tools** — classic IDEX with 2 tools (BCN3D Sigma, Snapmaker J1, Tenlog Hands-2 style — two independent X carriages on a shared Y gantry); multi-extruder on a shared rail when extended to 3 or 4. - **2 gantries × 1 tool each** — two fully-independent XY systems sharing a bed (Vivedino Xplorer style). Both Y rails independent — effectively "half an IQEX." - **2×2 grid, independent quadrants** — IQEX running four separate prints in parallel. - **2×2 grid, paired-gantry multicolor** — IQEX where cross-gantry tools share color responsibilities via the **Span** tile state. - **Firmware-managed center-origin** — slicer emits a centered single-half slice; firmware decides where to physically fan it out. Overlays on any of the above hardware topologies; canonical example is Flashforge IDEX with RepRapFirmware. --- ## Feature Walkthrough ### Printer Configuration Printer preset options that declare IMEX capability and geometry: | Option | Type | Description | |--------|------|-------------| | `is_imex` | bool | Marks this printer as IMEX-capable | | `imex_firmware_managed_zones` | bool | Emit centered single-half slice; firmware handles copy/mirror placement (default `false`) | | `imex_gantry_count` | int | Number of independent Y-axis gantries (rows) | | `imex_tools_per_gantry` | int (1–4) | Toolheads per gantry along X (columns) | | `imex_nozzle_clearance_x` / `imex_nozzle_clearance_y` | float | Nozzle-to-collision-edge distance in mm (literal, not halved) | | `imex_tool_layout` | string | Physical orientation: front-left / front-right / rear-left / rear-right | | `imex_mode_names` | string[] | Names of user-defined parallel modes; first entry is the reserved `primary` sentinel | | `imex_mode_active_tools` | string[] | Tool role assignments per mode (e.g. `0:P,1:C,2:M,3:M`, or `0:P,1:S,2:M,3:M` with Span) | | `imex_mode_gcodes` | string[] | Firmware macro to activate per mode | | `physical_extruder_map` | string[] | 0-indexed map from logical filament slots to physical extruders (for MMU/AFC — see below) | Tool roles per mode: **Primary** (P), **Copy** (C), **Mirror** (M), **Span** (S), **Inactive**. ### Parallel Mode Editor [Kooha-2026-05-14-13-45-35.webm](https://github.com/user-attachments/assets/af7650e6-d636-478b-9398-3f02ec662f03) The **IDEX/IQEX Configuration** and **IDEX/IQEX Parallel Modes** sections live in the printer preset's **Multimaterial** page (visible only when `is_imex = true`). The mode editor is a visual grid: - Each row defines one named parallel mode. The first row is a reserved, non-deletable **Primary** row stored as the sentinel `primary` in `imex_mode_names`. - The Primary tool is always T0; **Tool 0 Position** chooses which physical corner it occupies, and its tile is read-only. The other tool buttons cycle Inactive → Copy → Mirror → Span → Inactive, color-coded, with Span offered only where it applies. - **Span** is only offered when `imex_gantry_count >= 2` AND the tile is on the primary's gantry row — it declares "this tool is the multi-color partner of Primary on the same gantry," distinct from a Copy/Mirror role on a non-primary gantry. - A G-code field per mode holds the firmware macro to activate that mode (e.g. `IMEX_COPY` for Klipper). A placeholder-browser button per row opens the `EditGCodeDialog` for quick insertion. - Modes can be added and removed (removed via a dedicated `imex_remove.svg` ScalableButton). - Deleting the active mode resets affected plates to Primary. - Tool assignments are preserved across `imex_gantry_count` changes: going from IQEX (4 carriages) → IDEX (2 carriages) → IQEX restores all previously assigned roles, and the visible grid anchors to the gantry row containing the Primary assignment. <!-- TODO: replace this placeholder with a re-recorded parallel-mode-editor video showing the Span tile state, the remove button, and the placeholder-browser button. Old URL (out of date): https://github.com/user-attachments/assets/cabf1f70-206d-4f84-9c2e-594b38b83951 --> ### Span Tile State — Paired-Gantry Multicolor <img width="2833" height="1300" alt="2026-05-14-135412_grim" src="https://github.com/user-attachments/assets/52203133-7380-459a-973f-0502a1edc3c2" /> For IQEX printers (`imex_gantry_count >= 2`), the **Span (S)** tile role enables paired-gantry multicolor mirror mode. The same active-tools string `0:P,1:C,2:M,3:M` is ambiguous between two distinct hardware jobs — four independent quadrants vs. paired-gantry multicolor mirror — so the topology has to be declared explicitly rather than inferred. Span on the primary's gantry row is the declaration. Behavior driven by Span presence: - **Multicolor block rule**: requires Span on primary's gantry to allow multi-color slicing in a parallel mode. Without Span, multi-color slicing in a parallel mode is blocked with an actionable error message. - **Ghost aggregation**: one aggregated ghost per non-primary gantry when Span is present, using the column-paired representative tool. Mixed-role gantries fall back to per-tool. - **Zone aggregation**: one row-strip zone per non-primary gantry when Span is present (cell at primary's column collapses col-sep; `make_boxes` expands to full-X strip). - **Aggregated mirror drag**: ghost translates 1:1 with primary in X (copy-style), with X-flip baked into the mesh-local frame so geometry still reads as mirrored. Gantries don't share an X rail, so reflecting motion serves no collision purpose. - **Carriage collision strip audit**: X-boundary checks require `zr == pri_row_k` (matching the existing Y-boundary `c == pri_col` constraint). Prevents spurious strips on primary's right edge in paired-gantry mc-mirror, where T3 sits diagonally and can't actually collide with primary's carriage. Single source of truth: `IMEXHelpers::group_imex_active_tools_by_gantry(active_tools_str, tools_per_gantry)`. Ghost factory and zone calculator both consume it, so pairing logic lives in one place. ### Per-Plate Mode Selection Each build plate has an IMEX mode icon in its toolbar (normal, hover, dark, and dark-hover SVG variants). The mode can be set independently per plate: - **Left-click** cycles through all non-sentinel modes in order. - **Right-click** opens a popup menu listing all modes as radio items for direct selection. - Mode changes are recorded in the undo/redo snapshot system. - The selected mode is persisted in the 3MF project file per plate (`imex_parallel_mode` key). https://github.com/user-attachments/assets/5ab497e3-9c2f-476a-ac67-ef34a592395b When a parallel mode is active alongside multi-material objects on the same plate, a warning badge (`obj_warning.svg`) overlays the plate icon — see **Pre-Slice Warning System** below. ### Bed Visualization + Auto-Arrange Constraints When an IMEX mode is active, the build plate renders the carriage grid: <img width="2761" height="1447" alt="IMEX bed visualization with active primary zone and dimmed secondary zones" src="https://github.com/user-attachments/assets/3ee64b7b-a3ed-40aa-ad07-fa2f1cb74edf" /> - The **active (primary) zone** is full brightness. - **Inactive zones** are dimmed with a color-coded overlay (blue for copy, orange for mirror). - **Zone dividers** are rendered as lines across the bed. - The grid is a full 2D layout: `imex_tools_per_gantry` columns × `imex_gantry_count` rows. Zone sizing is based on the **active** tool count only — inactive tools donate their bed share to active neighbors. - Span-present configs render as a single row-strip zone per non-primary gantry rather than per-tool quadrants, reflecting that the firmware will paint the multi-color across the entire strip. - Colors are drawn from the Okabe-Ito palette for colorblind accessibility, with a deuteranopia/protanopia-safe alternate theme. - **Auto-arrange** is constrained to the primary zone when a parallel mode is active — `ArrangeJob::process()` replaces full-bed `bedpts` with the primary zone corners via `PartPlate::imex_primary_zone()`. Collision strips are additionally registered as hard obstacles through `m_unselected` (the working NFP placer input, not the dead `excluded_regions` field), so placement cannot drop parts into the danger strips. ### Placement Validation https://github.com/user-attachments/assets/5cb5a94a-981e-4fe9-898e-7e2f891b4f40 Objects placed outside the primary zone block slicing: - `has_imex_placement_violations()` in `PartPlate` checks each object's convex hull against the primary zone boundaries and collision strips. - Violations inject into the existing `update_background_process` validation pathway — the Slice button is disabled and an error notification is shown. - Mirror tools additionally generate X-axis collision strips (copy tools move in the same direction and cannot collide). Y-direction strips are scoped to same-column tools to avoid false positives from diagonal mirror pairs. - Strip width is taken **literally** from `imex_nozzle_clearance_x` (the measurement is nozzle-to-collision-edge distance, not carriage half-width). - Multi-color block path: a pre-slice rule blocks multi-color slicing in parallel modes that can't physically support it (e.g. a "fake IMEX" mode where all tools sit on a single gantry without a Span partner). The rule is centralized in `imex_multicolor_block_reason()` with unit-test coverage of every gating case. ### Ghost Object Rendering + Per-Plate Filament Picker When a parallel mode is active, the slicer renders colored, transparent **ghost copies** of primary-head instances on the plate — one per secondary active head, transformed under its Copy/Mirror role (or aggregated per gantry when Span is present). - Ghosts track the primary through drag/rotate/scale/mirror and invalidate on mode, filament-map, or `physical_extruder_map` changes. - **Left-click** on a ghost opens the `IMEXFilamentPickerPopover` for that ghost's head — a compact `BitmapComboBox` that writes directly to the per-plate `imex_head_filament_map` (MMU lane override). - Mirror ghost geometry is a **true reflection about the zone-boundary plane** (`x = primary_zone_center.x + gantry_offset.x/2`), so the ghost stays anchored in the target zone as the primary moves and drag reflects correctly (primary +X → ghost −X, Y tracks 1:1). - Hover tooltip: `"Tn → filament N"` with a color swatch. When no filament resolves to a head, the tooltip surfaces an actionable message directing the user to extend the extruder count in the Machine tab. - Ghost rendering + picking are scoped to the active plate — non-active plates don't draw stale ghosts during arrange/preview transitions, and click-picking never falls through to a non-current plate's ghost geometry. - Per-plate `imex_head_filament_map` round-trips through the 3MF project file. ### Pre-Slice Warning System <img width="1786" height="911" alt="2026-05-14-135714_grim" src="https://github.com/user-attachments/assets/16532d47-2da5-4472-ab9c-4b8b00303bae" /> Before a plate slices, IMEX parallel-mode plates are checked for three classes of conflict: 1. **Multi-material on secondary tools** — when the mode has Copy/Mirror tools and the plate has multiple filaments active; plate icon gets a warning badge. 2. **Bed temperature mismatch** — any two carriages configured >5 °C apart. 3. **Filament type incompatibility** — filaments from different type families on active carriages. A dismissible Yes/No `RichMessageDialog` fires once per user action from both `on_action_slice_plate` and `on_action_slice_all`. The dialog has a **"Don't show again"** checkbox that persists to `app_config` as `imex_pre_slice_warnings=false`. A re-enable toggle lives in **Printer Settings → Multimaterial → IDEX/IQEX Configuration** so suppressed warnings can be restored. ### Firmware-Managed Zones — Center-Origin Slice <img width="701" height="197" alt="2026-05-14-135842_grim" src="https://github.com/user-attachments/assets/3e2dfaab-fb4f-42b1-bf9d-c88dc0c95110" /> The `imex_firmware_managed_zones` printer-config option (default `false`) supports IDEX/IQEX printers whose firmware applies its own copy/mirror offsets in non-primary modes. Canonical examples: **RepRapFirmware IDEX duplication mode** on **Flashforge Creator Pro 2 / Creator 3 Pro**. These printers expect a centered single-half slice at bed origin and fan toolheads out from there — the slicer-managed paradigm of placing toolpaths at zone-relative positions produces gcode the firmware can't reconcile, since it'd double-apply offsets. When the flag is on and the active mode is non-primary, the slicer subtracts the primary zone's plate-local center from the gcode emission frame: - **Writer offset** is augmented by the primary zone center so emitted gcode is centered at bed origin. - **Processor offset** stays at plate_origin only, so the gcode-preview visualizer renders the centered toolpath at bed center rather than at the prepare-view zone placement. The user sees what'll physically print after firmware fan-out. - **`translate_to_print_space()`** is augmented for frame coherence so `first_layer_print_min/max` placeholders consumed by user start_gcode (e.g. Flashforge's M118 "max delta from zero" header) reflect the centered frame. Slice-handoff runs per-slice: `PartPlate::refresh_imex_slice_offset()` is called from `Plater::priv::update_background_process` after `Print::apply()`, so reslicing with mode toggled but no plate change picks up the updated offset. When the flag is off, all related code paths reduce to no-ops byte-identical to standard slicer-managed behavior. The two stock-code touches at `Print.cpp:2553-2554` (gcode_offset composition) and `Print.cpp:2823-2829` (translate_to_print_space) are explicit additive shifts that collapse to identity when the offset is `Vec2d::Zero()`. ### G-code Injection The selected parallel mode's G-code is written into the output file immediately before `machine_start_gcode`: - Looks up the plate's active mode name in `imex_mode_names` and writes the corresponding `imex_mode_gcodes` entry. - Processed through `placeholder_parser_process()` first, so Klipper-style variable substitution works **and** any `{global}` declarations flow forward into `machine_start_gcode`. - Primary mode's G-code field is emitted too. - Guarded against headless CLI slicing — `ensure_imex_zones()` short-circuits when `m_plater` is null so the CLI path (used by upstream's regression-test CI step) doesn't segfault on `wxGetApp().preset_bundle` dereference. ### Placeholder Parser Integration <img width="535" height="393" alt="2026-05-14-140227_grim" src="https://github.com/user-attachments/assets/6d059c77-38f6-4418-bfe9-3bf963e8c836" /> Three placeholders register under **Slicing State** and are settable from anywhere downstream: | Placeholder | Type | Description | |---|---|---| | `imex_mode` | string | The active mode name | | `imex_mode_index` | int | Index into `imex_mode_names` | | `imex_mode_gcode` | string | The resolved mode G-code (post-parser) | ### Per-Tool Pressure Advance — Firmware-Agnostic `set_pressure_advance()` takes an optional tool index (default `-1`, preserving existing behavior for all non-IMEX call sites). Per firmware: - **Klipper**: `EXTRUDER=extruder[N]` when `tool >= 0`, bare command otherwise - **RepRapFirmware**: `M572 D<N>` when `tool >= 0`, `M572 D0` otherwise (preserves the pre-IMEX output; a bare `M572` applies to whatever tool is selected and errors when there is none) - **Marlin 2**: `M900 K<X> T<N>` when `tool >= 0`, bare `M900` otherwise - **Marlin Legacy / fallback**: `M900 K<X>` always - **Repetier**: `M233 X<X> Y<X>` (X is quadratic, Y is linear; same value applied to both) `m_imex_parallel_mode` is set once per export from the active plate mode. PA tool-qualification is gated on this being a **non-primary** parallel mode — primary-mode prints emit ordinary tool-change PA exactly like any non-IMEX printer. Secondary active tools in parallel modes receive explicit per-tool PA at print start since they never go through a tool-change sequence. ### Per-Layer Temperature Management In IMEX parallel modes, all active tools (primary + secondaries) get temperature commands in `layer_change_gcode`: - Layer 1 temperatures only emit on the first layer; subsequent layers use normal layer-change temperatures. - IMEX temperature handling is consolidated into the second-layer transition. - Layer-change temperature commands use `M104 T<N>` with `physical_extruder_map` translation when applicable. ### MMU / AFC Support via `physical_extruder_map` For printers where multiple logical filament slots share one physical extruder (MMU, AFC, toolchangers), the `physical_extruder_map` profile option translates tool slot indices to physical extruder qualifiers before G-code emission. - **Fallback**: on IMEX printers, `Print::apply()` uses the profile's map only when it has one entry per extruder (the `nozzle_diameter` count). Anything else, including the single-entry default, is replaced by the identity map `0..n-1`. - **Used by**: IMEX PA emission, layer-change temperature commands (`M104 T`, `EXTRUDER=`, `M572 D`), ghost color resolution, ghost cache key, tooltip lookup, click gate. - **Profile authoring example** for a 7-slot printer with a 4-lane MMU on extruder 0 and three independent direct drives on extruders 1/2/3: ```json "physical_extruder_map": ["0","0","0","0","1","2","3"] ``` Non-MMU printers need no action — the identity fallback handles them. - A centralized helper in `IMEXHelpers`, `effective_physical_extruder_map(explicit_pem, nozzle_count)` (with a `PresetBundle` overload that prefers the project's map over the printer's), routes all PA/temp/ghost-color lookups through a single code path. - **No UI for editing the map** in this PR — non-trivial MMU/AFC layouts require profile-authoring (hand-edit the printer JSON) or an updated printer profile shipped by a vendor. A future enhancement would expose a per-slot extruder picker in the Multimaterial section. ### Layer Preview Multi-Carriage Animation The sequential preview (scrubber) animates all active carriages simultaneously: - One toolhead marker (colored cube) per active carriage, in addition to the primary. - Secondary marker colors: cyan (T1), yellow (T2), magenta (T3). - **Copy** tools: marker placed at the same relative position within their bed zone as the primary is in the primary zone. - **Mirror** tools: reflect across the target zone's facing edge (left-of-copy reflects across copy zone's left edge; right-of-copy across the right edge). Y position is always zone-relative copy (all tools on a row share a physical Y rail). - Carriage footprint boxes use per-carriage `box_offset_x/y` so the nozzle marker sits at the physically correct edge of the footprint — zone-based X by default, collision-side edge for Mirror; gantry-behind Y for back-row primaries, flipped for front-row primaries with a back-row secondary. - The filament usage legend notes the active carriage count and mode name (e.g. `IMEX: ×2 (copy_mode)`). - **View menu toggle**: `View → Show IDEX/IQEX Toolhead` (visible only on the Preview tab, only when the active printer is IDEX/IQEX) hides/shows the per-carriage toolhead representation during preview playback. State persists to `app_config` as `show_imex_toolhead_boxes`. Useful when scrubbing through dense toolpaths and the boxes get in the way of seeing the underlying geometry: [Kooha-2026-05-14-14-04-09.webm](https://github.com/user-attachments/assets/9869cd4d-3b60-4a6b-8dca-7ee1fc573ac1) Copy mode: https://github.com/user-attachments/assets/0774d285-ba36-4b86-a594-fb6572c3aede Mirror mode: https://github.com/user-attachments/assets/51528926-94dc-4d90-9b1a-1b42d9044be7 --- ## Known Limitations **Per-layer G-code collision detection** The placement-time zone check catches gross violations (object placed in wrong zone) but does not verify that toolpaths on any given layer maintain adequate X separation between adjacent carriages. A per-layer check via `ConflictChecker` was designed but deferred. Without it, a print that passes placement validation could still crash carriages if the primary object's toolpaths reach too close to a zone boundary. **Brim avoidance of IMEX zones** Standard `bed_exclude_area` exclusion zones are already respected by brim generation. IMEX collision strips are not — the brim generator has no visibility into them. The correct fix (feeding computed strip polygons from `PartPlate` through the `Print` object to `Brim.cpp`) was designed but deferred. In practice, users should leave adequate clearance between printed objects and zone boundaries to account for brim width. **`extruder_printable_area` integration** IMEX zones are not clipped against per-extruder printable polygons, and there is no violation check for placing an object outside the intersection of its active extruder's printable area and the IMEX primary zone. Deferred pending clarification on the tool→extruder index mapping. **Ghost rendering in firmware-managed mode** When `imex_firmware_managed_zones` is on, ghost rendering is suppressed entirely. The existing `imex_head_transform` math is slicer-managed semantics (places ghosts at `primary_zone_center + gantry_offset`) and produces wrong positions when the toolpath is being emitted in a centered frame. Proper firmware-managed ghost rendering — showing where copies/mirrors will physically print after firmware fan-out — needs new transforms designed around firmware-frame positions rather than a coordinate-flip of the slicer-managed ones. Deferred to a follow-up. **Filament-accurate multi-region ghost color** For paired-gantry multicolor mirror (Span mode), the aggregated ghost currently uses the representative tool's filament color as a single solid swatch. A future enhancement would render the ghost split into per-source-tool regions, each colored by the secondary tool that mirrors it. Slicer-side correctness is already shipped — the G-code emits the right T-codes; the ghost is a visual aid only. **Ghost path overlays** Secondary carriage toolpaths are not rendered in the layer preview. The secondary markers animate correctly, but the paths they would trace are not drawn. Adding ghost path rendering would require duplicating and offsetting the toolpath geometry per secondary carriage, which is a significant addition to the libvgcode rendering pipeline. **No per-mode slicing** All carriages in a mode execute the same sliced toolpaths (transformed per zone). There is no support for slicing different objects for each carriage independently within one mode. **Global default IMEX mode** There is no job-level IMEX default mode. Each plate's mode must be set individually (default is always Primary). A future improvement would add a global default in the sidebar (following the same pattern as bed type and nozzle diameter), with per-plate overrides. **Dynamic GL-rendered mode icons** The per-plate icon currently uses static SVGs. A GL-rendered carriage-grid icon that visually represents the mode's tool layout would be a nicer UX but is deferred. **Slice-all thumbnail icon refresh** On the "Slice All" path, plates flagged as IMEX-violated do not refresh their toolbar icon. Root cause and fix identified, not yet applied. **Start-G-code filament placeholder** The resolved per-head filament map is not yet exposed as a `PlaceholderParser` vector. Exposing it would let MMU firmware macros pre-load lanes before print start. **No UI for `physical_extruder_map` authoring** The MMU/AFC slot-to-physical-extruder map is currently profile-only — there's no in-app dropdown or editor for it. Users with non-trivial MMU/AFC setups must hand-edit the printer-preset JSON (or rely on a vendor-supplied profile). A future enhancement would expose a per-slot extruder picker in the Multimaterial section so users can declare the mapping without touching JSON. **Bundled IQEX printer profile** A full IQEX printer profile with cover image, bed mesh, and matched process/filament profiles is not bundled with this PR. Users must currently author their own printer preset. Deferred to a follow-up profile-only PR. **Mode lifecycle gap** Per-plate mode is stored as a string (the mode name). If a mode is renamed or deleted from the printer preset after a project is saved, the plate's saved mode name will not resolve and will silently fall back to Primary on next load. A warning on load would be a useful addition. **Firmware-managed prepare→preview frame jump** In firmware-managed-zones mode, the prepare view shows the part at its placed-in-zone position while the preview view shows the centered slice. This is intentional — the views truthfully represent the prepare frame vs. the post-firmware-fan-out frame — but the visual jump can be confusing on first use. --- ## Firmware Assumptions - The implementation assumes the firmware handles all carriage synchronization and offset math. OrcaSlicer only injects the mode-activation macro before machine start and is entirely dependent on user configuration. - Per-tool Pressure Advance is firmware-aware (Klipper / RRF / Marlin 2 / Marlin Legacy / Repetier). Layer-change temperature commands use `M104 T<N>` with `physical_extruder_map` translation. - Tested against Klipper on a real IQEX printer. RepRapFirmware gcode emission is exercised via profile-driven slice tests (Flashforge Creator Pro 2 profile); Marlin and RRF paths have not been validated on real hardware print runs. - Mid-print mode switching is explicitly not supported. The mode is locked at print start. - Firmware-managed-zones mode requires the printer's firmware to translate centered slice coordinates into physical toolhead positions; the slicer does not attempt to model the firmware's offset logic. --- ## Files Changed (High-Level) | File | Change | |------|--------| | `src/libslic3r/PrintConfig.{cpp,hpp}` | IMEX config option definitions + declarations, `IMEXMode` enum, `physical_extruder_map`, `imex_head_filament_map` (plate option), `imex_firmware_managed_zones` | | `src/libslic3r/Preset.cpp` | IMEX keys registered in printer and process preset option lists | | `src/libslic3r/PrintApply.cpp` | `physical_extruder_map` identity fallback on IMEX printers | | `src/libslic3r/Print.{cpp,hpp}` | IMEX slice-offset field + accessors; `translate_to_print_space` augmentation for frame coherence | | `src/libslic3r/GCode.{cpp,hpp}` | Mode G-code injection, per-tool PA emission (firmware-agnostic), per-layer temperatures for all active tools, `m_imex_parallel_mode` state, `set_gcode_offset_with_imex_shift` writer/processor split | | `src/libslic3r/GCodeWriter.{cpp,hpp}` | `set_pressure_advance(tool_index = -1)` per-firmware implementation | | `src/libslic3r/IMEXHelpers.{cpp,hpp}` | `imex_head_transform` (Primary/Copy/Mirror), `parse_imex_active_tools`, `imex_primary_tool_for_mode`, `effective_physical_extruder_map`, per-head filament resolution, `group_imex_active_tools_by_gantry` (Span pairing), `compute_imex_slice_offset` (firmware-managed) | | `src/libslic3r/Format/bbs_3mf.cpp` | Per-plate IMEX mode and `imex_head_filament_map` serialization | | `src/slic3r/GUI/Tab.{cpp,hpp}` | `IMEXModesCtrl` in Multimaterial page, tool assignment persistence across gantry count changes, `primary` sentinel handling, Span tile state, firmware-managed-zones checkbox, null guard in `clear_pages()` | | `src/slic3r/GUI/PartPlate.{cpp,hpp}` | Zone visualization (Span row-strip aggregation), placement violation detection, per-plate mode icon, ghost volume rebuild on mode/map/object mutation, warning-badge overlay, pre-slice warning collection, `refresh_imex_slice_offset()`, ghost suppression in firmware-managed mode, headless-CLI guard | | `src/slic3r/GUI/Plater.cpp` | Validation pathway injection, per-plate mode popup, ghost click handling, pre-slice warning dialog with "Don't show again", IMEX multimaterial conflict routing, per-slice IMEX offset refresh in `update_background_process` | | `src/slic3r/GUI/GLCanvas3D.cpp` | Ghost rendering with per-head filament color and translucent blending, picking via volume composite id, active-plate scoping | | `src/slic3r/GUI/GCodeViewer.cpp` | Multi-carriage marker animation, Mirror math fix, carriage footprint box offsets, legend annotation | | `src/slic3r/GUI/IMEXFilamentPickerPopover.{cpp,hpp}` | Ghost-click filament picker | | `src/slic3r/GUI/Jobs/ArrangeJob.cpp` | Auto-arrange constrained to IMEX primary zone + collision strip exclusion via `m_unselected` | | `src/slic3r/GUI/OG_CustomCtrl.cpp` | Empty `option_set` guard for widget-only lines | | `resources/images/plate_imex_mode*.svg`, `imex_remove.svg` | Per-plate mode icons (light/dark/hover variants), remove button | | `tests/libslic3r/test_imex_helpers.cpp` | Coverage for IMEX head transforms, `effective_physical_extruder_map`, `imex_multicolor_block_reason` gating, `group_imex_active_tools_by_gantry` Span pairing, `compute_imex_slice_offset` | --- ## Testing Notes Validated on an IQEX printer (4 carriages, 2×2 grid, Klipper firmware) and against the Flashforge Creator Pro 2 profile (2-tool IDEX, RRF flavor, center-origin bed) with the following configurations: - **Primary only** — baseline, no regression vs. standard single-extruder workflow - **Copy mode** (T0 Primary, T1 Copy, same row) — carriage markers animate in sync offset by strip width; ghost tracks drag/rotate/scale - **Mirror mode** (T0 Primary, T1 Mirror, same row) — T1 marker reflects T0 across zone-boundary plane; ghost drag reflects X correctly while Y tracks 1:1 - **Cross-row copy + mirror** (T0 Primary row 0, T2 Copy row 1, T3 Mirror row 1) — T2 follows T0's zone-relative position; T3 mirrors T2's X; Y shared per row - **4-tool copy mode** (T0 Primary, T1/T2/T3 Copy) — zone sizing correct, all four markers + ghosts render in sync - **Paired-gantry multicolor (mc-mirror, Span)** — `0:P,1:S,2:M,3:M`: ghost aggregates to one per non-primary gantry, X-flipped, drag tracks 1:1 with primary; collision strip audit doesn't fire spurious strips on primary's right edge - **Firmware-managed copy mode** (Flashforge Creator Pro 2) — emitted gcode coordinates are centered at bed origin (X = −cube_half_width … +cube_half_width), `first_layer_print_min/max` placeholders evaluate symmetrically, M118 header produces correct "max delta from zero" values for Flashforge's existing template, ghosts suppressed - **Firmware-managed flag toggle** — flipping the checkbox off restores slicer-managed iMEX behavior (toolpath at zone position) byte-identically; flipping on restores center-origin slice - **Delete active mode** — no crash; plate resets to Primary - **Placement outside primary zone** — slicing blocked with error notification - **Multi-material conflict warning** — plate icon gets warning badge; Yes/No dialog fires on slice; "Don't show again" checkbox persists - **Multi-color block rule** — `0:P,1:C` on a single gantry blocks at slice time with actionable error message - **Ghost filament picker** — left-click on ghost opens picker; selection writes to `imex_head_filament_map` and round-trips through 3MF save/load - **Per-plate mode selection** — left-click cycles modes, right-click shows popup, undo/redo correctly reverts mode changes, mode persists through project save/load - **Gantry count change round-trip** — reducing from IQEX (4 carriages) to IDEX (2 carriages) and back restores all previously assigned tool roles - **MMU/AFC `physical_extruder_map`** — explicit map routes PA and temperature commands to correct physical extruder qualifiers; auto-derive fallback preserves 1:1 behavior for non-MMU printers - **Headless CLI slicing** — `orca-slicer --slice project.3mf` does not segfault on IMEX-enabled printers - **Dark mode** — all four plate-icon variants render correctly - **Pre-slice warnings suppress + restore** — `app_config` flag flips on checkbox; re-enable toggle in Multimaterial config restores dialog - **Stock non-IMEX printer regression check** — slicing a single-head Voron Trident 350 profile produces byte-identical gcode vs. baseline; the firmware-managed flag and Span tile state both no-op when not applicable - **Full unit test suite** — 247/247 passing, including Layer 1 coverage for `compute_imex_slice_offset`, `group_imex_active_tools_by_gantry` Span pairing, and `imex_multicolor_block_reason` gating |
||
|
|
cf5f7b778d |
Show OK instead of Yes on notice dialogs (#16332)
Several warning and info dialogs passed wxYES as their only button, so
the only choice was "Yes" even though nothing is asked. Use wxOK.
None of the callers act on wxID_YES. They ignore the result, except
Field.cpp, which only checks that it's nonzero, and every button id is.
wxYES_DEFAULT is 0, so dropping it next to wxOK changes nothing.
Also drop the empty `if (ShowModal() == wxID_YES) {}` in
Plater::priv::load_files.
|
||
|
|
b14ed3e0c1 |
Multi nozzle UI Fixes / Improvements (#16075)
# New multi nozzle icon for guidance new icon has tooltip for guidance <img width="407" height="77" alt="Screenshot-20261005181353" src="https://github.com/user-attachments/assets/f27bbf79-7570-4f30-ab3c-52fa972c9fa3" /> **Before** <img width="414" height="228" alt="Screenshot-20261002144627" src="https://github.com/user-attachments/assets/7941891a-5a27-478b-a63f-49cf386e430f" /> **After - Moved icon to pre label position** • reduces possibility of wrapping of labels • Aligns labels and improves readability <img width="399" height="239" alt="Screenshot-20261005181511" src="https://github.com/user-attachments/assets/93f6b555-bbec-496a-b708-8c12c3b3185b" /> # Fix > Modified state not applied on tabs **Before - modified colors not applied to control** <img width="751" height="157" alt="Screenshot-20261001004828" src="https://github.com/user-attachments/assets/0c149fce-45e8-498f-8daa-7b54803399e9" /> **After** <img width="752" height="150" alt="Screenshot-20261005181614" src="https://github.com/user-attachments/assets/5a930bb2-6487-4987-b58d-850de6ae6779" /> Applied color as border because changing text color was not readable <img width="159" height="71" alt="Screenshot-20261002152522" src="https://github.com/user-attachments/assets/f694fc8d-c9f6-46d7-9e06-7762003c9f20" /> and applying as background color was too eye catchy <img width="184" height="80" alt="Screenshot-20261002152533" src="https://github.com/user-attachments/assets/2b67c1dd-8977-46ea-88fc-ce57bfad55bd" /> # Usable tabs on Printer settings • Fix > Using long string makes "Motion ability" and "Notes" tab unreachable • Matches T1,T2.. terminology with nozzle section • Makes https://github.com/OrcaSlicer/OrcaSlicer/pull/14737 available to use now **Before** <img width="751" height="126" alt="Screenshot-20261002152643" src="https://github.com/user-attachments/assets/0241d3c4-acc1-491e-ab85-fa60385c5e85" /> **After** <img width="748" height="129" alt="Screenshot-20261002152628" src="https://github.com/user-attachments/assets/c475d6c8-4f0f-47df-aa04-9d894b1c358d" /> No issue with 8 items <img width="748" height="124" alt="Screenshot-20261002153018" src="https://github.com/user-attachments/assets/9a8c4e12-2cdc-4508-b82b-134476589d80" /> # Dynamic Padding • buttons uses dynamic horizontal padding now depends on item count. reduces padding size to reduce scrolling **Before** <img width="416" height="91" alt="Screenshot-20261002153309" src="https://github.com/user-attachments/assets/977b22db-e686-4967-95be-13772d77cefd" /> **After** <img width="403" height="79" alt="Screenshot-20261005181728" src="https://github.com/user-attachments/assets/9aac0d17-0729-408f-9237-62b17236488f" /> **Before** <img width="745" height="153" alt="Screenshot-20261002153659" src="https://github.com/user-attachments/assets/06951759-419a-40fb-8fc2-d93f5b04d6ea" /> **After** <img width="748" height="149" alt="Screenshot-20261005181833" src="https://github.com/user-attachments/assets/9ccec344-1dd0-4b03-ac12-20d298f413c8" /> **After** <img width="756" height="178" alt="Screenshot-20261009144637" src="https://github.com/user-attachments/assets/1343c39e-5cbd-4f9e-b501-df3797579961" /> <img width="761" height="198" alt="Screenshot-20261009144459" src="https://github.com/user-attachments/assets/efb1ef75-a602-4d3a-a17f-9354ed633357" /> <img width="758" height="196" alt="Screenshot-20261009144510" src="https://github.com/user-attachments/assets/df1d88c5-1b6f-4748-a6bd-a96659a83fe0" /> <img width="764" height="190" alt="Screenshot-20261009154210" src="https://github.com/user-attachments/assets/b876ed71-5fa0-47e4-83c8-a33100e0a229" /> # Keeping compact look & Making it more readable • Removed ":" from labels to gain space as much as possible and increased font size a bit but UI didnt allowed bigger font • Matched font sizes of nozzle section while keeping height of items. Previous font was too small. removed "mm" text to gain more space but added it in tooltip **Before-After** <img width="800" height="240" alt="Screenshot-20261002154132" src="https://github.com/user-attachments/assets/bc4ddcb1-2248-4fc7-9492-ab4fb68cb659" /> • Matched font sizes for BBL nozzle selectors **Before-After** <img width="806" height="200" alt="Screenshot-20261002154513" src="https://github.com/user-attachments/assets/b3d38317-8e4d-4c1e-a490-ce4959ed700a" /> <img width="794" height="156" alt="Screenshot-20261002154649" src="https://github.com/user-attachments/assets/00917584-8e1a-44b0-8c0b-79cec30059bf" /> # Layout and Styling fixes • Matched background color of non active buttons with (Global/Objects) switch **Before** <img width="405" height="159" alt="Screenshot-20261002154947" src="https://github.com/user-attachments/assets/8a8adcc8-431a-48be-aabd-a981335b6ab9" /> **After** <img width="407" height="159" alt="Screenshot-20261005182106" src="https://github.com/user-attachments/assets/7f837be3-ce85-49e6-909a-05350b3d8b30" /> # Other effected dialogs <img width="541" height="195" alt="Screenshot-20261002165003" src="https://github.com/user-attachments/assets/109abbc3-d7c0-4384-9b85-d943e2027963" /> <img width="547" height="308" alt="Screenshot-20261002165307" src="https://github.com/user-attachments/assets/951d9a8d-56f9-4659-aebe-77f236e8c838" /> <!-- > A guide for users on how to download the artifacts from this PR. --> [How to Download Pull Requests Artifacts for Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts) |
||
|
|
d59155b27f |
Moonraker: pass print=true in upload — fix Upload & Print race (fixes #14945) (#15032)
* Moonraker: pass print=true in upload — fixes Upload & Print race with power-on-upload Closes #14945. Upload & Print on the Moonraker (Klipper) host type failed with HTTP 503 "Klippy Host not connected" on any printer that Moonraker powers up in response to an upload (the [power] on_when_upload_queued feature). The file landed on disk, the print never started, and the user hit an error dialog. Root cause: after POST /server/files/upload succeeds we immediately fire POST /printer/print/start. On a cold printer that Moonraker just powered up, Klippy is still coming up when /printer/print/start arrives, so Moonraker returns 503. Fix: add `print=true` to the upload multipart form. Moonraker's own upload endpoint queues the print inside the upload response — and when a [power] device with on_when_upload_queued is configured, it powers the printer on and waits for Klippy READY before starting. That's the whole point of the power-on-upload feature; our second POST was defeating it. Also read `result.print_started` from the upload response — when true, skip our explicit /printer/print/start (Moonraker handled it); when false (older Moonraker or buddy-fork that ignores the print flag), fall back to the explicit call so the existing behaviour is preserved for those servers. Reporter and root-cause identification: @RubenOllesch. (cherry picked from commit |