feat: 4-tool support, primary row, zone sizing, and stability fixes

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>
This commit is contained in:
Clifford Garwood
2026-04-09 19:08:57 -04:00
co-authored by Claude Sonnet 4.6
parent 0d73cd13ba
commit 9a87363044
11 changed files with 220 additions and 134 deletions
+1 -1
View File
@@ -3058,7 +3058,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
// macro is active before any motion, homing, or probing in start G-code.
if (print.config().is_imex.value && !print.objects().empty()) {
const std::string& imex_mode = print.objects().front()->config().imex_parallel_mode.value;
if (!imex_mode.empty() && imex_mode != "primary") {
if (!imex_mode.empty()) {
const auto& mode_names = print.config().imex_mode_names.values;
const auto& mode_gcodes = print.config().imex_mode_gcodes.values;
for (size_t i = 0; i < mode_names.size() && i < mode_gcodes.size(); ++i) {