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>
This commit is contained in:
Clifford Garwood
2026-04-21 22:51:31 -04:00
co-authored by Claude Opus 4.7
parent b2128cc330
commit 6a3de6a28f
7 changed files with 104 additions and 32 deletions
+3 -5
View File
@@ -1,4 +1,5 @@
#include "ClipperUtils.hpp"
#include "IMEXHelpers.hpp"
#include "Model.hpp"
#include "Print.hpp"
@@ -1168,11 +1169,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
{
auto* pem = new_full_config.option<ConfigOptionInts>("physical_extruder_map", true);
const auto* pei = new_full_config.option<ConfigOptionInts>("printer_extruder_id");
if (pem && pei && !pei->values.empty() && pem->values.size() <= 1) {
pem->values.clear();
for (int v : pei->values)
pem->values.push_back(v - 1); // convert 1-indexed extruder IDs to 0-indexed
}
if (pem)
pem->values = effective_physical_extruder_map(pem, pei).values;
}
m_ori_full_print_config = new_full_config;