Replaces the plater-icon popover with colored transparent ghost copies
of primary-head instances on the plate, one per secondary active head
under its Copy/Mirror role transform. Left-click on a ghost opens a
compact filament picker popover for the ghost's head (MMU lane override).
Ghosts track the primary through drag/rotate/scale/mirror and invalidate
on mode or pem changes.
Key pieces:
IMEXHelpers -- imex_head_transform (Primary/Copy/Mirror), shared role
parser, per-head filament resolution with X-axis Mirror anchor.
PartPlate -- ghost state, volume rebuild on mode/map/object mutation,
primary_origin plumbed for Mirror reflection across the primary-row
gantry plane.
GLCanvas3D -- ghost rendering with per-head filament color and
translucent blending; picking routed via volume composite id.
Plater -- ghost click + tooltip; plater icon left-click always cycles.
IMEXFilamentPickerPopover -- BitmapComboBox row for one secondary head,
writes imex_head_filament_map on selection.
bbs_3mf -- round-trip the per-plate imex_head_filament_map option.
PrintConfig -- add imex_head_filament_map as a plate option.
MMU/AFC routing for parallel modes relies on the printer profile's
physical_extruder_map (see prior commit for authoring format). Primary-
row heads and their per-plate filament overrides are resolved through
that map, so PA and temperature emission address the correct physical
extruder when multiple logical slots share one carriage.
Tests: IMEXHelpers coverage for Primary/Copy/Mirror transforms
including a 2x2 off-row regression guard for the X-axis reflection fix.
Auto-populate physical_extruder_map (0-indexed) from printer_extruder_id
(1-indexed) in Print::apply(). The guard only runs when the map is still
at default size (<= 1 element), so printer profiles that set it explicitly
are untouched.
All IMEX parallel-mode PA and temperature emission now routes tool slot
indices through physical_extruder_map before constructing firmware
extruder qualifiers (EXTRUDER=, M104 T, M572 D). This ensures AFC/MMU
setups where multiple slots share one physical extruder get the correct
qualifier -- e.g. T6 on physical extruder 3 emits EXTRUDER=extruder3
instead of EXTRUDER=extruder6.
Profile authoring for MMU/AFC printers:
Add physical_extruder_map to the printer profile JSON as a 0-indexed
string array, one entry per logical filament slot, whose value is the
physical extruder carrying that slot. The array size must be > 1 for
the explicit map to override the auto-derive. Example for a 7-slot
printer with a 4-lane MMU on extruder 0 and three independent direct
drives on extruders 1/2/3:
"physical_extruder_map": ["0","0","0","0","1","2","3"]
Non-MMU printers need no action -- printer_extruder_id already encodes
the 1:1 mapping and the auto-derive handles it.
Deeper integration (zone validation, collision detection, filament
assignment grouping, Moonraker agent auto-population) is deferred.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extends set_pressure_advance() with 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
- RRF: M572 D<N> when tool >= 0, bare M572 otherwise (no D0 fallback)
- Marlin 2: M900 K<X> T<N> when tool >= 0, bare M900 otherwise
- Marlin Legacy / fallback: M900 K<X> always
Adds m_imex_parallel_mode to GCode, set once per export from the active
plate mode. PA and layer-transition temperature tool-qualification are
gated on this being a non-primary parallel mode — primary mode prints
use regular 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.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Temperature emission (GCode.cpp, PrintConfig.cpp, ConfigManipulation.cpp):
- Set temperatures for all active tools in layer_change_gcode for iMEX
parallel modes (primary + secondary carriages)
- Fix filament temperature commands so Layer 1 temperatures only emit on
the first layer; subsequent layers use normal layer-change temperatures
- Remove stray temperature commands that fired outside the intended context
- Consolidate iMEX temperature handling into the second-layer transition;
clean up emission logic and naming throughout
Pre-slice warning dialogs (PartPlate.cpp/hpp, Plater.cpp):
- Collect per-plate IMEX warnings before slicing: multi-material conflict,
bed temperature mismatch between carriages (>5 °C delta), and filament
type incompatibility
- Show a dismissible Yes/No dialog from both "Slice Plate" and "Slice All"
actions; No returns to 3D view, Yes proceeds to slice
- Refresh plate thumbnails after the panel switch so previously-generated
thumbnails are not left black
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
In iMEX (IDEX/IQEX) parallel printing modes (copy/mirror), only the
primary extruder generates toolpaths. The firmware duplicates the
primary's movements for secondary carriages, so they don't appear in
tool_ordering.all_extruders().
This caused is_extruder_used[N] to return false for secondary tools
even though they're physically active and moving.
The fix adds logic to parse the active mode's tool assignments from
imex_mode_active_tools config and marks all assigned tools as used.
Changes:
- Added null checks and bounds validation for config options
- Skip empty tool strings to avoid unnecessary parsing
- Reordered bounds checks for defensive programming
- Added clarifying comments for exception handling
This ensures is_extruder_used[N] is true for all tools in a parallel
mode, allowing printer profiles to correctly enable heaters and
emit cleanup G-code for all active carriages.
Fixes: is_extruder_used[1] returns false in copy/mirror modes (#13086)
Related: Comment [28]/[30], Comment [18] (is_extruder_used in G-code header)
Register imex_mode (string), imex_mode_index (int), and imex_mode_gcode
(string) in OtherSlicingStatesConfigDef so they appear in the placeholder
search UI under Slicing State.
Set all three via placeholder_parser().set() before any script processing
in _do_export(). Process imex_mode_gcode first so {global} declarations
defined there flow forward into machine_start_gcode.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
IMEXModesCtrl:
- Primary mode is now a non-deletable first row stored as sentinel
"primary" in imex_mode_names; older configs load cleanly
- New rows default T0 → Primary when no tool assignment is stored
- Filter "primary" from plater popup/cycle list to prevent double entry
- imex_tools_per_gantry cap raised 2 → 4
Zone sizing:
- Zone width/height now based on active tool count only; inactive tools
donate their bed share to active neighbors (fixes 4-tool layout)
- Active col/row maps (col_to_zone/row_to_zone) applied consistently
across zone fills, collision strips, and primary zone box
GCodeViewer animation:
- Mirror position formula fixed: left-of-copy reflects across copy
zone's left edge; right-of-copy reflects across right edge
(T3 was rendering on top of T1)
- strip_width/row_strip_height use active counts, matching PartPlate
Stability:
- Early return in calc_imex_zones() when tool_states is empty; prevents
OOB crash on new printer with stale process-preset mode name
- is_imex toggle in on_config_change calls refresh_imex_icons() so the
plate mode icon appears without requiring a new project
GCode:
- Remove "primary" guard so Primary mode gcode field is emitted at
start of print
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix crash-inconsistent draft shield
* Skip empty skirt G-code and fix skirt_done handling
Track whether a skirt was actually generated for the current print Z and avoid appending empty skirt G-code. Only pop m_skirt_done when a skirt was emitted to prevent corrupting skirt state across multiple instances per layer. Also use a temporary string (moved into the main gcode) to detect emptiness before updating state. Preserves existing first-layer clearing behavior.
* Union ex brims
Revert "Union ex brims"
This reverts commit bbc9a39faf318dc2df093eb2bdcebf19a4162fe9.
Update Brim.cpp
* dont repeat paths
* Update Brim.cpp
* multimaterial brim independiente
* Normal brim if is by object
* fix print order
* cleaning 1
* cleaning 2
* Normal brim if multimaterial on first layer
* fix artifact
* combine_brim optional
refactoring
* refactoring gcode.cpp
* refactoring brim.cpp
Update Brim.cpp
* Remove multimaterial first-layer check for brims
Stop detecting extruders used on the first layer and remove the is_multimaterial_first_layer guard. Simplify can_combine_brims to only consider combine_brims and whether printing is ByObject, allowing brims to be combined across extruders unless printing by object or combine_brims is disabled. Cleans up unused code and simplifies brim-generation conditions.
* Remove material specification from unified brim comment
* Update PrintConfig.cpp
Introduces first-class parallel printing (copy/mirror modes) for printers
with multiple independent X-axis carriages. Branded iXex (independent X
extruder), targeting Klipper firmware with a firmware-agnostic design.
- PrintConfig: new printer options declaring iXex capability and geometry
(is_ixex, ixex_gantry_count, ixex_tools_per_gantry, carriage dims,
tool layout, and per-mode name/role/gcode arrays)
- Preset: iXex keys registered in printer and process preset option lists
- Tab: IXexModesCtrl visual grid editor in Printer preset tab; mode
dropdown in Process → Others tab; clear_pages() nulls iXex pointers to
prevent dangling-pointer crash on preset save
- PartPlate: 2D zone visualization (active/dimmed/dividers) and
placement-violation detection (has_ixex_placement_violations) that
blocks slicing when objects fall outside the primary zone
- Plater: violation detection wired into update_background_process so
the Slice button is disabled with an error notification on violation
- GCode: mode-activation G-code injected before machine_start_gcode
- GCodeViewer: multi-carriage toolhead markers in sequential preview,
filament legend annotated with active carriage count and mode name
- Handle z contouring in variable speed flow when emitting GCode
- Add logic to restore nominnal z height for regular extrusions
- preserve z_contoured flag when splitting extrusion paths
Previously, wipe tower behavior was determined by checking if the printer
was a QIDI vendor. This introduces a configurable enum (Type 1 / Type 2)
so any printer can select its wipe tower implementation. BBL printers
remain hardcoded to Type 1. Qidi profiles default to Type 1.
When flush_multiplier is applied to flush_volumes_matrix values, the
result can be a decimal (e.g., 272 * 1.3 = 353.6). Some printer firmware
metadata parsers (notably Creality K2) crash when parsing decimal values
in flush_volumes_matrix, causing prints to get stuck at "File selected".
This fix rounds the multiplied values to integers, ensuring compatibility
with firmware that expects integer values in this field.
Fixes compatibility with Creality K2 and potentially other Klipper-based
printers that parse the CONFIG_BLOCK metadata.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude <noreply@anthropic.com>
The WipeTower2 code path never called construct_mesh(), leaving
wipe_tower_mesh_data as std::nullopt. GCode export then dereferenced
it unconditionally, triggering an assertion failure / crash.
Added checks in ByLayer print sequence to handle ordering for Intra-layer ordering option. Prevents new ordering setup when "As object list" is enabled.
print_machine_envelope() used get_extruder_id(extruder_id)*2 to index
machine limit arrays that only hold [Normal, Stealth] (2 entries).
For multi-extruder setups this went out-of-bounds, causing wrong M201/M203
values in the G-code which then override the estimator's correct limits.
Same class of bug as c6d1c11ebb but on the G-code writer side.
Changes:
- Remove unused extruder_id param from print_machine_envelope()
- Use .values.front() for M201/M203, matching M204/M205 in same function
- Change get_option_value() fallback from .back() to .front() so any
future out-of-bounds index returns Normal mode instead of Stealth
Wipe tower interface features and preheat fixes
Fresh PR branch rebuilt on upstream/main (squash of origin/BBL-studio-wipe-tower-merge) to avoid merge-history issues.
* fixes: keys_map is initialized with itself [-Winit-self]
* fixes: operation on repeats may be undefined [-Wsequence-point]
* fixes: warning: suggest parentheses around && within || [-Wparentheses]
* fixes: moving brim_points to itself [-Wself-move]
* fixes: operation on unprintability may be undefined [-Wsequence-point]
* fixes: extra tokens at end of #endif directive [-Wendif-labels]
* fixes: converting to non-pointer type int from NULL [-Wconversion-null]
* review result: simplifies fix 'operation on repeats may be undefined'
Port Z Anti-Aliasing from BambuStudio-ZAA (https://github.com/adob/BambuStudio-ZAA)
to OrcaSlicer. ZAA eliminates stair-stepping on curved and sloped top surfaces
by raycasting each extrusion point against the original 3D mesh and micro-adjusting
Z height to follow the actual surface geometry.
Key changes:
- Add ContourZ.cpp raycasting algorithm (~330 lines)
- Extend geometry with 3D support (Point3, Line3, Polyline3, MultiPoint3)
- Template arc fitting for 2D/3D compatibility
- Change ExtrusionPath::polyline from Polyline to Polyline3
- Add 5 ZAA config options (zaa_enabled, zaa_min_z, etc.)
- Add posContouring pipeline step in PrintObject
- Update GCode writer for 3D coordinate output
- Add ZAA settings UI in Print Settings > Quality
- Add docs/ZAA.md with usage and implementation details
ZAA is opt-in and disabled by default. When disabled, the slicing pipeline
is unchanged.
## Problem
Some Creality printers (e.g. Ender-3 V3 SE) rely on metadata comments in the first lines of G-code files to display print info on the screen (estimated time, filament usage, layer height). This follows a format originally used by Cura/Creality Print:
```
;FLAVOR:Marlin
;TIME:3708.97
;Filament used:6.21m
;Layer height:0.2
```
OrcaSlicer currently has no way to write content before the `HEADER_BLOCK` in the G-code output, and does not expose `print_time_sec` or `filament_length_m` as usable placeholders. As a result, these printers show zeroed-out print info on screen.
## Changes
### 1. New placeholders (post-processing)
Added two new G-code placeholders resolved during post-processing:
- `{print_time_sec}` — total estimated print time in seconds (double)
- `{filament_length_m}` — total filament length in meters (double)
These values are only available after G-code generation, so they use inline marker replacement (`@PRINT_TIME_SEC@`, `@FILAMENT_LENGTH_M@`) that `GCodeProcessor::run_post_process()` substitutes with actual computed values — the same pattern used by existing M73 and layer count placeholders.
### 2. New "File header G-code" option (`machine_top_gcode`)
Added a new G-code field in **Printer Settings > Machine G-code** that writes content at the very top of the output file, before `HEADER_BLOCK_START`. This allows users to add firmware-specific metadata that must appear in the first lines.
### 3. Creality Ender-3 V3 SE profile update
Pre-configured the Ender-3 V3 SE profiles (all 4 nozzle variants) with a default `machine_top_gcode` value matching the Cura-compatible header format, so print info displays correctly out of the box.
# Screenshots
| Before (current OrcaSlicer) | After (this PR) |
|---|---|
|  |  |
## Tests
- Sliced test model with Ender-3 V3 SE profile
- Verified G-code output contains correct values in first lines:
- `;TIME:` with actual print time in seconds
- `;Filament used:` with filament length in meters
- `;Layer height:` with correct layer height
- Verified `{print_time_sec}` and `{filament_length_m}` work in both Machine Start G-code and File header G-code fields
- Verified empty `machine_top_gcode` produces no extra output
- Built and tested on macOS (arm64)
* Emit Disable Power Loss Recovery
Now only works if it's enabled but the goal it's to force disable it.
With this change it will always emit the command for BBL or Marlin 2.
Co-Authored-By: Michael Rook <54159303+michaelr0@users.noreply.github.com>
* Refactor power loss recovery G-code comments
* Return empty power loss recovery when no compatible printer
* Update power loss recovery comments
Update label and tooltip for power loss recovery
* Add enum for power loss recovery mode
Refactored power loss recovery configuration to use a new PowerLossRecoveryMode enum instead of a boolean. Updated GCodeWriter and related logic to handle the new enum, allowing for 'printer_configuration', 'enable', and 'disable' options. Updated config handling, legacy value conversion, and default values accordingly.
* Update PrintConfig.cpp
---------
Co-authored-by: Michael Rook <54159303+michaelr0@users.noreply.github.com>
Co-authored-by: SoftFever <softfeverever@gmail.com>
* Adjust resonance avoidance speed calculation
* Only adjust if it's strictly below max
* If the speed is over half of the set speed range,
use the max speed to prevent slowdowns
Signed-off-by: Kuran Kaname <celtare21@gmail.com>
* update comment
---------
Signed-off-by: Kuran Kaname <celtare21@gmail.com>
Co-authored-by: SoftFever <softfeverever@gmail.com>
* Add ability to disable Power Loss Recovery
* Fix typo in PrintConfig.hpp for power loss recovery
* Attempt to resolve Unknown option exception: disable_power_less_recovery
Add disable_power_loss_recovery property to any json which had scan_first_layer
* Revert "Attempt to resolve Unknown option exception: disable_power_less_recovery"
This reverts commit ddaf34b317.
* Fix typo
* Change attribution from BBS to Orca in PrintConfig.cpp
* Mini refactor power loss recovery handling in GCode export
- Moved power loss recovery G-code generation to a new method in GCodeWriter.
- Support Marlin 2
* Update comments and power loss recovery handling
* Implement power loss recovery G-code commands
Added functions to start and end power loss recovery with appropriate G-code commands and comments.
* Add power loss recovery methods to GCodeWriter
* refactor and fix build errors
---------
Co-authored-by: Michael Rook <michael@rook.id.au>
Co-authored-by: Ioannis Giannakas <59056762+igiannakas@users.noreply.github.com>
Co-authored-by: SoftFever <softfeverever@gmail.com>
1.Immediate do lift in eager_lift function
jira:NONE
Signed-off-by: xun.zhang <xun.zhang@bambulab.com>
Change-Id: I931d22e901e2123bb886c31d8d1a5788fddeed42
(cherry picked from commit 6cea772d4f3b2f7e2a43c35a2271e4bdbba9eadd)