Commit Graph
8 Commits
Author SHA1 Message Date
Clifford GarwoodandClaude Opus 4.7 2c604173d3 fix(imex): block multi-color slicing for single-gantry "fake IMEX" modes
Catches the case where a non-primary IMEX mode's active tools all sit on
one gantry — e.g. mode "0:P,1:C" on a 2x2 IQEX where T0 and T1 share
gantry 0. The "Copy"/"Mirror" label is decorative there: nothing actually
parallel-prints, but the user's mode_gcode still fires and emits firmware
setup that doesn't apply, while the slicer treats it as a multi-color
parallel print. Conceptually it's just a regular multi-tool single-gantry
print and belongs in Primary mode.

Changes:
- imex_multicolor_block_reason now collects the set of distinct gantries
  spanned by the active tools and short-circuits with a clear "single
  gantry — not a parallel-print scenario" message before falling through
  to the existing within-gantry-swap check.
- Drops the redundant pre-slice "Multi-material objects detected" soft
  warning from collect_imex_warnings — the slice-time block surfaces a
  more specific message at the right moment, and the soft warning was
  vague handwaving in front of it. Bed-temp + filament-type checks stay.
- New unit test covering the single-gantry block.

Behavior matrix on a 2x2 IQEX with multi-color:
  "0:P,1:C"           single gantry         -> BLOCK (new)
  "0:P,2:C"           dual gantry, 1 each   -> BLOCK (existing within-gantry-swap)
  "0:P,1:C,2:M,3:M"   dual gantry, 2 each   -> ALLOW
  multiple filaments to same physical via pem -> BLOCK (existing MMU sharing)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 22:53:33 -04:00
Clifford GarwoodandClaude Opus 4.7 66891899e9 refactor(imex): drop unused Split mode-type infrastructure
The mode-type tag (imex_mode_types config + imex_mode_type_for helper +
Split sentinel) was added in 0bb1cef as scaffolding for the Split rendering
work that landed in 4370cca and then got reverted in d9be71b. With Phase 2-4
gone, this scaffolding is now unused dead code — and the design we settled
on instead is to leave topology entirely implicit (parsed from active_tools_str)
rather than carrying a per-mode type tag the user would otherwise have to
manage explicitly.

The multi-color slicing block + bare T<n> suppression that were the actual
substance of the safeguards work stay in place:
- imex_multicolor_block_reason still allows multi-color exactly when 2+ tools
  are active on the primary's gantry — Felix's hypothetical IQEX paired-gantry
  case works through this path, no new mode type required.
- Slicer-side: bare T<n> stays suppressed at print-start in IMEX parallel modes;
  mid-print T<n> emits naturally for the legitimate IQEX 4-tool-active scenario.

Removed:
- ConfigOptionStrings imex_mode_types (PrintConfig.hpp/cpp + Preset.cpp key list)
- imex_mode_type_for helper + kImexModeType{Primary,Copy,Mirror,Split} sentinels
- mode_type parameter on imex_multicolor_block_reason and the Split short-circuit
- mode_type plumbing in Print::validate and PartPlate::has_imex_multimaterial_conflict
- Three unit tests for imex_mode_type_for + two Split-specific multicolor block tests

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-28 00:48:39 -04:00
Clifford GarwoodandClaude Opus 4.7 0bb1cef78e feat(imex): polish UI per PR feedback + add parallel-mode safeguards
Felix14-v2 PR review feedback (https://github.com/OrcaSlicer/OrcaSlicer/pull/13086#issuecomment-4323696312)
plus the slice-time validation work that follows from his bug list.

UI polish:
- Capitalize "Primary" in display (right-click mode menu, plate tooltip).
  Sentinel stays lowercase for wire compatibility.
- Pre-slice-warnings checkbox now uses Orca's ::CheckBox so it matches the
  green toggle style of the rest of the IDEX/IQEX configuration page.
- DPI-scaled the IMEXModesCtrl (modes editor) sub-panels, button grid,
  text wraps, gcode textarea, and the ghost-tooltip swatch (imgui.scaled).
  Legend swatches sized to body-text height for visual balance.
- Primary mode tool buttons in the modes editor are now disabled (read-
  only): cycling roles on the IMEX-off mode is a no-op and confusing.
- Modes editor sub-panels now explicitly inherit the app's window-default
  dark colour so chromeless ScalableButtons don't render with a visible
  light box around their icons on GTK dark themes.
- Per-mode-line reset arrows in the modes editor: each row gets a small
  reset bitmap that snaps that row's name+tools+gcode triplet back to the
  saved preset's value. matches_config() guard on the page-level reload
  prevents the textbox-being-typed-into from being destroyed mid-keystroke.
- New View menu item "Show IDEX/IQEX Toolhead" — toggles the per-carriage
  footprint boxes during G-code preview playback. Gated to Preview tab +
  IMEX printer; backed by app_config so it persists.

Coordinated config migrations:
- imex_tool_layout and imex_viz_theme migrate from coString to coEnum
  (ImexToolLayout / ImexVizTheme). Existing wire format preserved so
  saved presets and 3MFs deserialize unchanged. Side-benefit: both now
  pick up standard Field rendering and so finally show reset arrows.

Slice-time safeguards (the validation half):
- imex_suppresses_bare_toolchange(parallel_mode, count): suppresses the
  slicer's bare T<n> at print-start in any IMEX parallel mode (the user's
  imex_mode_gcode + machine_start_gcode owns tool activation there). Mid-
  print T<n> emits normally — Print::validate blocks the configurations
  where mid-print T<n> wouldn't make sense. Applied to both code paths
  inside GCode::set_extruder (the long multi-extruder path AND the
  single-extruder path that fires when multiple_extruders=false).
- imex_multicolor_block_reason(): hard-stop validator returning a user-
  facing reason string when the active IMEX configuration can't physically
  support multi-color. Catches IDEX (1 tool/gantry), 2-tool-active IQEX
  (no within-gantry swap topology), and any MMU/AFC lane sharing among
  used filaments. Wired into Print::validate as a slice blocker, and into
  PartPlate::has_imex_multimaterial_conflict so the plater badge agrees
  with the slice block (no more false positives where the badge warns but
  the slice goes through).
- New imex_mode_types config option (parallel array to imex_mode_names)
  and imex_mode_type_for() helper. Mode-type tag drives behaviour: zones,
  ghosts, and validation interpret modes differently per type. Initial
  types: "primary", "copy", "mirror", "split". Split modes are explicitly
  designed for paired-gantry IQEX multi-color and bypass the gantry-pair
  check in imex_multicolor_block_reason (MMU sharing still blocks them).

Test coverage:
- New unit tests cover imex_suppresses_bare_toolchange (4 cases),
  imex_multicolor_block_reason (8 cases including IDEX, IQEX 2-/4-tool-
  active, MMU sharing, missing primary, Split type), and imex_mode_type_for
  (3 cases including legacy fallback). Total: 186 IMEX assertions across
  71 test cases, all passing.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-27 22:51:05 -04:00
Clifford GarwoodandClaude Opus 4.7 41457635e9 test(imex): extract physical→logical helpers + cover them with unit tests
Three of the four IMEX physical-vs-logical fixes landed earlier on this
branch (fbc58d2a1d, fa048babeb, a38b95bf45, e11e7d46df) used inline
lambdas / direct loops to translate physical extruder indices to logical
filament slots.  No test coverage existed for the specific composition,
even though the underlying primitives (resolve_filament_for_head,
first_filament_for_physical_head) were tested.

Pull two patterns out of Plater.cpp and GCode.cpp into IMEXHelpers as
named helpers, then unit-test them:

  imex_primary_logical_from_objects(used_slots_1b, pem, primary_physical)
    Walks the plate's used filament slots (1-based) and returns the
    first one whose pem entry maps to the primary's physical extruder.
    This is what the warning's `logical_for_primary` now delegates to —
    moves the "look at object assignments, not pem first-routed default"
    behavior introduced in fa048babeb out of the lambda and into a
    separately-testable function.

  imex_secondary_logical_slots(active_physicals, primary_physical,
                               plate_head_filament_map, pem)
    Iterates IMEX active physicals, skips the one matching primary,
    resolves each remainder via resolve_filament_for_head (per-plate
    override + first-routed fallback), deduplicates, drops -1 entries.
    Replaces the inline loop in GCode::_do_export's is_extruder_used
    marking (a38b95bf45 + e11e7d46df).

10 new test cases in test_imex_helpers.cpp cover the cases that
correspond directly to bugs hit:

  imex_primary_logical_from_objects:
    - AFC primary picks the object's slot (the user's specific bug)
    - Multi-color AFC primary returns first input-order match
    - Direct extruder primary unambiguous (no MMU)
    - No object routed to primary returns -1
    - Empty inputs (no objects, empty pem)

  imex_secondary_logical_slots:
    - Copy mode skips primary, falls back to first-routed
    - IQEX 4-mode enumerates all three secondaries
    - Per-plate override wins over first-routed
    - Drops unrouted physicals + deduplicates
    - Only-primary-active returns empty

All [IMEX] + [Variant] + [3mf] regression: 166 assertions / 61 cases.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 05:07:34 -04:00
Clifford GarwoodandClaude Opus 4.7 c2492ccc47 refactor(imex): extract imex_pem_tool_for helper + unit tests
The physical_extruder_map translation used by IMEX per-tool PA emission was
inlined identically at two sites in GCode.cpp (tool-change and second-layer
transition). Extract to IMEXHelpers so the routing rule ("parallel mode AND
populated pem → physical index, else -1") is testable in isolation and the
call sites read as intent rather than re-deriving the conditional.

Production change is behavior-preserving:
- Same predicate (`!mode.empty() && mode != "primary"`)
- Same empty-pem short-circuit returning -1
- Same get_at() dispatch on hit
- Both call sites replaced with a single call

Four unit tests in [IMEX] cover the routing matrix:
  - non-IMEX ("") and primary mode short-circuit
  - parallel mode + empty pem short-circuits (defense-in-depth; get_at would
    throw on empty values otherwise)
  - identity pem (non-MMU IDEX) routes filament to itself
  - MMU collapse routes multiple logical slots to one physical (7-slot profile
    with 4-lane MMU on physical 0 and direct drives on 1/2/3)

All IMEX + Variant regression suites pass post-refactor (133 assertions / 48
cases under libslic3r, 25 assertions / 6 cases under fff_print [PressureAdvance]).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-24 23:07:23 -04:00
Clifford GarwoodandClaude Opus 4.7 9a87b96ded refactor(imex): Mirror reflects about zone-boundary plane, not primary origin
Replace imex_head_transform's fifth argument (Vec3d primary_origin) with a
Vec2d primary_zone_center and rewrite the Mirror branch as a true reflection
about the plane x = primary_zone_center.x + gantry_offset.x/2. Previous math
flipped about the primary's current origin, which:
  * let the ghost drift out of the target zone as the primary moved, and
  * made mirrored drag motion track 1:1 with the primary instead of reflecting.

The new transform places the ghost at the mirrored position within the target
zone (matching where the mirror tool actually prints) and reflects drag so
primary +X → ghost -X while Y tracks 1:1 — i.e. the ghost stays a true
mirror while the user drags. Off-row Mirror targets (e.g. T3 on a 2x2) still
reflect across the same X-plane as on-row peers.

PartPlate::calc_imex_ghosts and update_imex_ghost_transforms now feed primary_off
(the primary head's zone center) instead of an instance-space Vec3d.

Mirror tests rewritten against the new geometric contract: ghost origin at the
reflected position, primary drag deltas reflected across the zone-boundary plane.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 22:51:56 -04:00
Clifford GarwoodandClaude Opus 4.7 6a3de6a28f feat(imex): effective_physical_extruder_map helper + diagnostic no-filament tooltip
Centralize the project→printer→printer_extruder_id fallback for the physical
extruder map. PrintApply, PartPlate (ghost color + cache key), Plater (tooltip
+ click gate) all previously open-coded the three-step lookup, and each handled
the "pem unset, derive from pei" case slightly differently — an IDEX printer
without an explicit pem could paint an UNPRINTABLE_COLOR ghost even though the
slicer would have derived a valid mapping.

- IMEXHelpers: add effective_physical_extruder_map(explicit_pem, pei) and a
  PresetBundle overload that wraps the project→printer precedence.
- PrintApply: use the helper in place of the inline pei→pem normalization.
- PartPlate / Plater: call the PresetBundle overload at every ghost-color,
  ghost-cache-key, tooltip, and click-gate site.
- Plater::format_imex_ghost_tooltip: when no filament resolves to a head,
  surface an actionable message directing the user to extend the extruder
  count in the Machine tab, instead of the generic "(no filament routed)".
- IMEXFilamentPickerPopover: hold m_pem by value so callers can pass a
  stack-local derived pem without lifetime worries.
- Tests: 5 new cases covering explicit-wins, default-pem fallback, null
  inputs, and the IDEX ghost-color regression that motivated this.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 22:51:31 -04:00
Clifford Garwood 77c32a2e15 feat(imex): plate ghost renderer + per-plate filament map
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.
2026-04-21 14:56:22 -04:00