Across 6 files the literal "primary" string was the sentinel for "no IMEX
parallel mode active" — used for short-circuiting in serialization, ghost
visualization, zone calc, popup-menu list construction, the IMEXModesCtrl
non-deletable first row, and several layer-of-export checks. A typo in
any one would silently bypass the guard.
Define kImexPrimaryMode in IMEXHelpers.hpp with a docstring describing
what equality with it means semantically, and route every call site
through it. No behavior change.
Touched: bbs_3mf.cpp, GCode.cpp, PartPlate.cpp, Plater.cpp, Tab.cpp.
The bbs_3mf and Tab files now include IMEXHelpers.hpp; the other three
already did.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Add extrusion role change G-code options
Introduces new G-code options for handling extrusion role changes at the process and filament levels. Updates configuration, GUI, and GCode logic to support 'process_change_extrusion_role_gcode' and 'filament_change_extrusion_role_gcode', allowing custom G-code insertion when the extrusion role changes.
Co-Authored-By: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
* Optimize extrusion role-change gcode handling
Cache gcode template strings and current filament id, and guard creation of DynamicConfig/placeholder processing behind a check that at least one role-change gcode is non-empty. This avoids redundant config lookups and DynamicConfig/placeholder parsing when no custom role-change gcode is defined, improving clarity and performance without changing behavior.
---------
Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
# Description
This commit adds the enhancement of https://github.com/OrcaSlicer/OrcaSlicer/issues/13116
- Adds checkboxes to enable or disable the fan speed override during print or after printing is completed. This allows users to, for example, only override the fan speed during printing whilst leaving the fan speed as is set in the machine gcode at the end.
- Two new flags are added for this: `activate_air_filtration_during_print[extruder]` and `activate_air_filtration_on_completion[extruder]`. These can be used to more finely control machine gcode
- Current filament settings remain as they are: `activate_air_filtration[extruder]` settings are unaffected and by default both new flags are set to true, ensuring the same set behavior as before.
# Screenshots/Recordings/Graphs
<img width="856" height="208" alt="Screenshot From 2026-04-09 18-57-14" src="https://github.com/user-attachments/assets/e71e7de3-2def-4046-b5dc-55bf3b516ce5" />
As you can see there are now checkboxes left of the fan speeds. They have their own tooltip too, which also helps identify the correct flags for users who want to adjust their machine gcode.
## Tests
I have thoroughly tested this on my own computer (CachyOS) and on my printer. I have also carefully checked the gcode in every possible state this is in and ensured that the unsaved changes dialog properly displays the labels for these settings. Flags are set properly, sliced files properly use the flags if you check for them in machine gcode and default behavior is unaffected for those who have already changed settings for air filtration. From what I can see, this does exactly what it should be doing without any issues.
* make initial layer travel move acceleration and jerk configurable
* Update spelling to match UI pattern
* Update min integer and re-order to match patterns
---------
Co-authored-by: Thomas Scheiblauer <tom@sharkbay.at>
Co-authored-by: Ioannis Giannakas <59056762+igiannakas@users.noreply.github.com>
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.
The "Pre-slice warnings" line in the IDEX/IQEX Configuration section
was a widget-only line (no options) with full_width left at the default
of 0. activate_line() only skips the option_set.front() call when
full_width=1; without it, the code falls through to:
bool is_legend_line = option_set.front().opt.gui_type == ...
Calling front() on an empty std::vector is undefined behavior. On
Windows/MSVC release builds this dereferences a null pointer and reads
at offset 0x30 (where ConfigOptionDef::gui_type lands), producing an
ACCESS_VIOLATION at 0x30. On Linux/GCC the same UB happens to be
harmless, so the crash is Windows-only and cannot be reproduced on
Linux.
Fix: set line.full_width = 1, matching the pattern used by the "Modes"
(IMEXModesCtrl) line. This takes the early-return widget path in both
append_line and activate_line, bypassing option_set.front() entirely.
Reported by tester: crash on clicking Multimaterial tab with any
printer (K3D VOSTOK confirmed), build af59501f.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces MessageDialog with RichMessageDialog to show a suppress
checkbox on both slice-plate and slice-all warning paths. Persists
the choice to app_config as imex_pre_slice_warnings=false. Adds a
re-enable toggle in Printer Settings → Multimaterial → IDEX/IQEX
Configuration so the warnings can be restored if suppressed accidentally.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add warning badge (obj_warning.svg overlay) to the iMEX plate icon when
a parallel mode is active alongside multi-material objects on the same plate
- Add has_imex_multimaterial_conflict() using get_extruders(true) so only
filaments actually used on the plate are checked
- Move multi-material caution dialog from reslice() into on_action_slice_plate /
on_action_slice_all so it fires exactly once per user action and does not
disrupt GL thumbnail generation during Slice All
- Fix is_imex missing from p->config init key list so on_config_change()
diff detection correctly triggers refresh_imex_icons()
- Defer imex_changed handling until after set_bed_shape() so m_shape is current
- Replace plain remove button with ScalableButton (imex_remove.svg) in mode rows
- Add EditGCodeDialog launch button per mode row for placeholder browsing
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add BeltBackTransform class that inverts the shear/scale matrix and
applies it in GCodeWriter::to_machine_coords() so G-code outputs in
the machine's physical coordinate space, gated by new
belt_gcode_back_transform config option
- Extend belt floor clipping to all three tree support pipelines
(Prusa-style, Orca organic, TreeModelVolumes) with per-layer polygon
clipping, anti-overhang integration, and belt raft extension layers
- Fix tree drop_nodes() belt termination, organic support global Z
offset, collision calculation index bug, and first-layer brim/empty
layer checks for belt printers
two-shot - first build built but didn't plumb to UI. Woah.
add pre-slice axis remap, because Y needs to be Z
going to change tactic and move based on bbox min
switch to per axis snapping
per axis swap snap now per object
build plate tilt wasn't invalidating slicer settings
support upper bound now correct, need to get lower bound corrected
axis swapped support termination corrected
Z Shear works with and without pre-slice remap now
- Fix support clipping z-shift calculation by removing coordinate-space
mismatch and sync belt_floor_z_shift with global_z_offset; fix
invalidation so posSupportMaterial no longer resets slicing params
- Add belt floor polygon clipping to non-organic tree support
(slim/strong/hybrid) with collision surface integration in
TreeSupportData, belt extension layers, and first-layer brim
suppression
- Add belt floor clipping to organic tree support pipeline with virtual
belt raft layers, per-layer polygons in TreeModelVolumes, and
post-generation layer trimming; fix pre-existing processing_last_mesh
bug in calculateCollision()
Fix belt floor support clipping: z-shift, invalidation, and global offset
- Fix support clipping z-shift calculation by removing coordinate-space
mismatch (raw_bounding_box min.z vs trafo_centered m_belt_min_z) and
sync belt_floor_z_shift with global_z_offset in global shear mode
- Fix invalidation so posSupportMaterial no longer resets slicing params,
preventing the exact posSlice z-shift from being overwritten by the
bounding-box approximation on support-only setting changes
- Remove double-counting of global z_offset on support layers — support
already inherits the offset from object layers during generation
This Work Was Co-Authored-By Claude Opus 4.6 (1M context) <noreply@anthropic.com>
UI: gray out inactive belt sub-options, rename to mesh transforms, move to Advanced
Fix mesh clipping through build plate after belt shear/scale transform
Generalize G-code viewer designed-view toggle for full belt transform
Clip support layers to transformed belt floor plane
Supports below the tilted build plate (Z = shear_factor * from_axis - min_z)
are now clipped via half-plane intersection after generation. Belt floor
parameters stored in SlicingParameters and populated in both update_slicing_parameters()
and the static slicing_parameters() overload.
Make belt G-code viewer toggle more prominent, add B keyboard shortcut
- Add separator + teal "Belt Printer" header in legend panel
- Append [B] hint to checkbox label
- Add B key shortcut in GLCanvas3D to toggle designed/machine view
- Read belt_printer_angle from loaded G-code headers to enable belt view
Add per-axis global transform option for belt printer shear
New belt_shear_{x,y,z}_global bool configs. When enabled, shear incorporates
instance shift so objects at different bed positions get position-aware
transform (Z += factor * instance_shift_on_from_axis).
Fix global shear: use layer Z offset instead of mesh transform, add config invalidation
- Global shear offset applied as post-slicing layer print_z adjustment
instead of mesh transform (which was absorbed by min_z normalization
or shifted mesh out of slice range)
- Register all belt transform options in Print::invalidate_state_by_config_options
to trigger posSlice re-slicing (the fallback only invalidated Print steps,
not PrintObject steps — belt changes had no effect without manual re-slice)
- Belt gcode remap options added to steps_gcode (gcode-export only)
- Skip empty-first-layer check for belt objects with global Z offset
WIP: split instances for global shear, relative Z offsets, debug logging
- PrintApply: when belt global mode active, prevent instance grouping by
adding unique Z perturbation to trafo — each copy becomes its own
PrintObject with independent layers
- PrintObjectSlice: compute global Z offset relative to minimum Y shift
across all PrintObjects (lowest-Y object stays at Z=0)
- Debug logging (warning level) for belt global shift values and offsets
Known issues:
- Cached posSlice results cause stale offsets when mixing copies with
individually-added objects — need to compute min baseline outside slice()
- Supports still generate to Z=0 instead of object's global Z offset
Fix global shear for copied objects: disable shared-object layer optimization
When belt global Z shear is active, each object needs unique layer Z
values based on its bed position. The shared-object optimization was
causing copies to reuse the source object's layers (and its Z offset)
instead of computing their own position-based offset.
started work on getting supports to work properly
one step forward, one step back
this version didn't quite work. Getting somewhere though
about to add UI controllable tests
added configuration options for supports
tweak CLAUDE.md to be more aggressive for my machine. This commit should probably be pulled out before contributing upstream
still chasing down some bugs
moving objects between slices no longer results in improper Z-height because of caching
added more data to the debug logs
Z offset is getting more global again
still not quite there, I think there's a fundamental logic flaw?
hunting for bugs
finally have a functional fix
Add belt floor clipping to tree supports (organic and non-organic)
- Add belt floor polygon clipping to non-organic tree support
(slim/strong/hybrid) in draw_circles() and terminate nodes at the
belt surface instead of the horizontal build plate
- Add belt floor clipping to organic tree support pipeline with virtual
belt raft layers for sub-floor branch generation, per-layer belt
floor polygons in TreeModelVolumes, and post-generation layer trimming
- Fix pre-existing processing_last_mesh bug in TreeModelVolumes that
prevented m_anti_overhang (support blockers) from ever being applied;
skip empty first layer check for belt printers
Commits:
current approach: make a face surface to build supports to
closer!
supports now terminate on shear plane, now need to get shear plane to correct Z height
nearly there
chasing down logic issues still
committing for checkpoint, this still does not work
still got logic problems...
cull support clipping
stashing changes for now. Going to focus on getting the global shear OFF support generation dialed first.
beginning per object shear calcs
Local shear transform is on correct Z offset now
local shear finally works now and needs more testing
global shear works now, needs thorough testing
debugging non-45 degree angles
debugging part 2
supports at all angles work now
remove debug logging
Add belt floor collision to non-organic tree support pipeline
- Integrate belt floor as a collision surface in TreeSupportData so
branches route around the belt naturally, replacing the explicit
termination checks in drop_nodes()
- Add belt extension layers below the object after draw_circles() to
allow support geometry to extend to the diagonal belt surface instead
of terminating at a horizontal first layer
- Fix coordinate overflow in belt floor polygons (scale_(1e4) exceeds
int32), skip first-layer brim expansion for belt printers, and
extend empty first layer check bypass to all belt modes
add debug logging, Z translate for tree supports
still not seeing any cutoff surface yet
adding debug options
attempt #2 at trees
if hit Z buildplate stop but don't set to_buildplate true
getting closer
tree support almost there, just need to get rid of the circles at the beginning
getting closer
belt / shear plane clip works, need to figure out the buidlplate plane issues
more logic, added debugging logs
supports now extend somewhat below Z=0 in global shear mode
fix bad alloc, add 10mm below build plate
fully works now
shear transform + prusa tree support generation works now.
pull out debug logging
- Implement per-object global shear transform in PrintObject with
layer Z-offset calculation, config invalidation, and fix for
shared-object layer optimization breaking copied objects
- Clip support layers to the transformed belt floor plane and begin
work on tree support adaptation for sheared coordinate space
- Improve belt UI: gray out inactive sub-options, add B keyboard
shortcut for G-code viewer design-view toggle, fix mesh clipping
through build plate after shear/scale transform
y' = y + z·cot(α),
while x' = x and z' = z
getting closer to customizable variant
getting closer
X/Y/Z shear initial
clean up UI
add 1/sin(a) transform, idea taken from blackbelt cura plugin
Things work now (turns out I've been using the wrong set of transforms)
- Implement core belt slicing pipeline: R(-alpha, X) mesh rotation in PrintObjectSlice with corrected object height calculation for proper layer count
Add to_machine_coords() in GCodeWriter to convert slicing-frame coordinates back to machine-frame, propagated through GCode,
GCodeProcessor, and GCodeViewer
Add belt-mode UI: tilted bed visualization, slicing-direction arrow, and raw G-code toggle to switch between machine-frame and slicing-frame views
This is a combination of 6 commits.
checkpoint 1: initial MVP. Slicing functions, but rotates instead of skews are happening and a lot of other stuff too
getting somewhere, getting to the point where I need to figure out how to verify this stuff
this appears to be a dead end.
getting somewhere I think maybe
I'm pretty sure we've completely lost the plot at this point and need to restart this process...
remove slice logic in preparation for new, more invasive plan
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>
- Add all_tool_states map to IXexModesCtrl Row, storing all tool
assignments including those not currently visible due to grid size
- active_tools_string() now serializes from all_tool_states so
off-screen assignments survive the round trip through a smaller grid
- Button clicks keep all_tool_states in sync with visible btn_states
- Display anchors to the gantry row containing the Primary assignment
so reducing gantry count keeps the meaningful row visible rather
than always defaulting to row 0
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add per-plate iXex mode icon to the plate toolbar (normal, hover, dark, dark-hover SVG variants)
- Left-click cycles through available modes; right-click shows a popup menu with all modes as radio items
- Mode changes are recorded in the undo/redo snapshot system
- Fix double context menu: suppress EVT_GLCANVAS_PLATE_RIGHT_CLICK and EVT_GLCANVAS_RIGHT_CLICK when the iXex icon popup was already shown
- Remove ixex_parallel_mode combo from Print Settings > Other > Special mode (superseded by per-plate icon)
- Remove dead code: refresh_ixex_mode_combo(), m_ixex_mode_combo member, related Tab reload hook
- iXex mode persisted in 3MF project files via existing plate metadata serialization
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* 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
Config key rename (breaking for saved profiles — call out in PR):
ixex_carriage_width_x/y → ixex_nozzle_clearance_x/y
Labels updated to "Nozzle Clearance X/Y" with consistent tooltips
describing the measurement as nozzle-to-collision-side-edge distance.
Strip math fix:
Previously halved the clearance value (× 0.5) under the assumption
the nozzle was centered in the carriage. The measurement is now the
literal nozzle-to-edge distance, so the × 0.5 factor is removed.
The collision strip width now equals the configured value directly.
Carriage box visualization (GCodeViewer):
- Add per-carriage box_offset_x/y so the nozzle marker sits at the
physically correct edge of the footprint box rather than centered.
- X: zone-based by default (nozzle at inner edge facing bed center).
Copy secondaries inherit the primary's X orientation (same movement
direction). Mirror secondaries use the collision-side edge.
- Y: always row-based regardless of copy/mirror mode. Gantry is always
behind the nozzle (high-Y); front-row primaries with a back-row
secondary override to place nozzle at the low-Y edge.
- Fix stale mesh bug: GLModel::init_from() is a no-op when already
initialized. Call reset() before init_from() so mesh rebuilds
correctly when nozzle clearance values change in config.
- Remove m_ixex_toolhead_box_dims (was the now-unnecessary cache key).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Same class of bug as the TabPrinter fix: clear_pages() destroys all
page widgets but didn't null m_ixex_mode_combo, causing a SIGSEGV in
refresh_ixex_mode_combo() when load_current_preset() ran after a
page rebuild (e.g. triggered by Plater::reset()).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Arrays cannot be captured by value in C++ lambdas; replaced with a
static local declared inside the event handler.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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
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.
1. Dirty flags not showing for extruder options:
2. Crash when switching to non-first extruder tabs:
3. Modifying one extruder's parameter affects other extruders:
## Summary
- Change default bed temperature type from "By First filament" to "By Highest Temp"
- Move `bed_temperature_formula` option from develop mode to advanced mode for better accessibility
- Relocate UI control from "Basic Information - Advanced" to "Multimaterial Setup" section where it's more relevant for multi-filament printing
Using the highest temperature of all printed filaments is generally safer for bed adhesion than using the first filament's temperature, especially in multi-material prints where different filaments may require different bed temperatures.
<img width="390" height="239" alt="Screenshot 2026-02-26 at 18 44 56" src="https://github.com/user-attachments/assets/0fe9f8c7-062a-4a7a-8ab3-c52df7e74b26" />