refactor(imex): replace "primary" magic string with kImexPrimaryMode constant

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>
This commit is contained in:
Clifford Garwood
2026-04-25 00:32:22 -04:00
co-authored by Claude Opus 4.7
parent efa9d65cb4
commit 085f5ccec8
6 changed files with 32 additions and 23 deletions
+2 -1
View File
@@ -1,5 +1,6 @@
#include "../libslic3r.h"
#include "../Exception.hpp"
#include "../IMEXHelpers.hpp"
#include "../Model.hpp"
#include "../Preset.hpp"
#include "../Utils.hpp"
@@ -7791,7 +7792,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
{
auto* imex_mode_opt = plate_data->config.option<ConfigOptionString>("imex_parallel_mode");
if (imex_mode_opt && !imex_mode_opt->value.empty() && imex_mode_opt->value != "primary")
if (imex_mode_opt && !imex_mode_opt->value.empty() && imex_mode_opt->value != kImexPrimaryMode)
stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << IMEX_PARALLEL_MODE_ATTR << "\" " << VALUE_ATTR << "=\"" << imex_mode_opt->value << "\"/>\n";
}
{
+6 -6
View File
@@ -2407,7 +2407,7 @@ static std::vector<int> get_imex_active_tools(const Print& print)
return active_tools;
const std::string& raw_mode = print.objects().front()->config().imex_parallel_mode.value;
const std::string active_mode = raw_mode.empty() ? "primary" : raw_mode;
const std::string active_mode = raw_mode.empty() ? kImexPrimaryMode : raw_mode;
const auto* mode_names_opt = print.config().option<ConfigOptionStrings>("imex_mode_names");
const auto* tools_opt = print.config().option<ConfigOptionStrings>("imex_mode_active_tools");
@@ -3101,7 +3101,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
m_imex_head_filament_map.clear();
if (print.config().is_imex.value && !print.objects().empty()) {
const std::string& raw = print.objects().front()->config().imex_parallel_mode.value;
imex_active_mode = raw.empty() ? "primary" : raw;
imex_active_mode = raw.empty() ? kImexPrimaryMode : raw;
m_imex_parallel_mode = imex_active_mode;
m_imex_head_filament_map = parse_imex_head_filament_map(
print.objects().front()->config().imex_head_filament_map.value);
@@ -3240,7 +3240,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
// In primary mode, regular tool-change PA handles each tool as it becomes active.
// In parallel modes no tool changes occur, so every carriage must be addressed
// explicitly here before printing starts.
if (!m_imex_parallel_mode.empty() && m_imex_parallel_mode != "primary"
if (!m_imex_parallel_mode.empty() && m_imex_parallel_mode != kImexPrimaryMode
&& !m_config.physical_extruder_map.values.empty()) {
// initial_physical: pem-translate the print's initial logical extruder so the
// loop can skip the primary head (which emitted PA via the normal path).
@@ -4710,7 +4710,7 @@ LayerResult GCode::process_layer(
// so they're not in m_writer.extruders() and multiple_extruders==false (max id==0).
// Use the static set_temperature path for IMEX so the T index is always emitted.
// In primary mode, fall through to the standard path — tool changes handle temps normally.
if (!m_imex_parallel_mode.empty() && m_imex_parallel_mode != "primary") {
if (!m_imex_parallel_mode.empty() && m_imex_parallel_mode != kImexPrimaryMode) {
// All active tools need explicit temps — none receive tool-change commands,
// so we can't rely on the condition used for non-IMEX (temp != initial_layer_temp).
// A tool whose initial and regular temps are the same still needs to be set here.
@@ -7599,7 +7599,7 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo
// Guard the pem lookup: PrintApply populates pem when printer_extruder_id
// is set, but defense-in-depth prevents a throw from get_at on any
// empty-pem path that might slip through in exotic profiles.
const bool imex_parallel = !m_imex_parallel_mode.empty() && m_imex_parallel_mode != "primary";
const bool imex_parallel = !m_imex_parallel_mode.empty() && m_imex_parallel_mode != kImexPrimaryMode;
const int pa_tool = (imex_parallel && !m_config.physical_extruder_map.values.empty())
? m_config.physical_extruder_map.get_at((int)new_filament_id)
: -1;
@@ -7903,7 +7903,7 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo
if (m_config.enable_pressure_advance.get_at(new_filament_id)) {
// Empty-pem guard mirrors the earlier PA site; get_at throws on empty values.
const bool imex_parallel = !m_imex_parallel_mode.empty() && m_imex_parallel_mode != "primary";
const bool imex_parallel = !m_imex_parallel_mode.empty() && m_imex_parallel_mode != kImexPrimaryMode;
const int pa_tool = (imex_parallel && !m_config.physical_extruder_map.values.empty())
? m_config.physical_extruder_map.get_at((int)new_filament_id)
: -1;
+7
View File
@@ -13,6 +13,13 @@ namespace Slic3r {
class PresetBundle;
// Reserved sentinel name for the always-present, non-deletable Primary mode in
// `imex_mode_names`. Comparison with this constant indicates "no parallel printing
// in effect" — IMEX zone visualization, ghost rendering, secondary-tool PA / temp
// emission, and the 3MF metadata serialization all short-circuit when the active
// plate mode equals this. Treat any value NOT equal to this as a parallel mode.
inline constexpr const char* kImexPrimaryMode = "primary";
// Returns the effective physical_extruder_map given an optionally-explicit map and
// the printer's `printer_extruder_id`. If `explicit_pem` has size >= 2 the caller
// authored one, and it is returned verbatim. Otherwise the map is auto-derived
+8 -8
View File
@@ -375,12 +375,12 @@ std::string PartPlate::get_imex_mode() const
if (opt && !opt->value.empty())
return opt->value;
}
return "primary";
return kImexPrimaryMode;
}
void PartPlate::set_imex_mode(const std::string& mode)
{
if (mode.empty() || mode == "primary") {
if (mode.empty() || mode == kImexPrimaryMode) {
m_config.erase("imex_parallel_mode");
} else {
m_config.set_key_value("imex_parallel_mode", new ConfigOptionString(mode));
@@ -568,13 +568,13 @@ void PartPlate::calc_imex_zones()
// Per-plate mode takes priority over the process preset.
std::string active_mode = get_imex_mode();
if (active_mode == "primary") {
if (active_mode == kImexPrimaryMode) {
const DynamicPrintConfig& process_cfg = wxGetApp().preset_bundle->prints.get_edited_preset().config;
auto* mode_opt = process_cfg.option<ConfigOptionString>("imex_parallel_mode");
if (mode_opt && !mode_opt->value.empty())
active_mode = mode_opt->value;
}
if (active_mode == "primary" || active_mode.empty())
if (active_mode == kImexPrimaryMode || active_mode.empty())
return;
// Grid dimensions and tool layout from printer config
@@ -919,7 +919,7 @@ std::string PartPlate::build_imex_cache_key() const
return "";
// Per-plate mode takes priority over process preset.
std::string active_mode = get_imex_mode();
if (active_mode == "primary") {
if (active_mode == kImexPrimaryMode) {
const DynamicPrintConfig& process_cfg = wxGetApp().preset_bundle->prints.get_edited_preset().config;
auto* mode_opt = process_cfg.option<ConfigOptionString>("imex_parallel_mode");
if (mode_opt && !mode_opt->value.empty())
@@ -1005,12 +1005,12 @@ bool PartPlate::resolve_active_mode_tools(std::string& out_tools_str, int& out_p
if (!is_imex_opt || !is_imex_opt->value) return false;
std::string active_mode = get_imex_mode();
if (active_mode == "primary" || active_mode.empty()) {
if (active_mode == kImexPrimaryMode || active_mode.empty()) {
const DynamicPrintConfig& proc_cfg = wxGetApp().preset_bundle->prints.get_edited_preset().config;
if (auto* mo = proc_cfg.option<ConfigOptionString>("imex_parallel_mode"))
active_mode = mo->value;
}
if (active_mode.empty() || active_mode == "primary") return false;
if (active_mode.empty() || active_mode == kImexPrimaryMode) return false;
auto* names = printer_cfg.option<ConfigOptionStrings>("imex_mode_names");
auto* tools = printer_cfg.option<ConfigOptionStrings>("imex_mode_active_tools");
@@ -1214,7 +1214,7 @@ bool PartPlate::has_imex_multimaterial_conflict() const
if (!pb) return false;
auto* is_imex_opt = pb->printers.get_edited_preset().config.option<ConfigOptionBool>("is_imex");
if (!is_imex_opt || !is_imex_opt->value) return false;
if (get_imex_mode() == "primary") return false;
if (get_imex_mode() == kImexPrimaryMode) return false;
// Condition 2: objects on this plate actually use more than one unique filament/extruder
std::vector<int> used = get_extruders(true);
+4 -4
View File
@@ -9944,7 +9944,7 @@ static std::vector<wxString> collect_imex_warnings(PartPlate* plate)
// Checks 2 & 3 only apply when a non-primary parallel mode is active
const std::string mode = plate->get_imex_mode();
if (mode == "primary") return warnings;
if (mode == kImexPrimaryMode) return warnings;
PresetBundle* bundle = wxGetApp().preset_bundle;
if (!bundle) return warnings;
@@ -17970,14 +17970,14 @@ int Plater::select_plate_by_hover_id(int hover_id, bool right_click, bool isModi
ret = select_plate(plate_index);
if (!ret) {
PartPlate* curr_plate = p->partplate_list.get_curr_plate();
// Build ordered mode list: "primary" first, then all named modes.
// Build ordered mode list: kImexPrimaryMode first, then all named modes.
std::vector<std::string> modes;
modes.push_back("primary");
modes.push_back(kImexPrimaryMode);
const DynamicPrintConfig& printer_cfg = wxGetApp().preset_bundle->printers.get_edited_preset().config;
auto* names_opt = printer_cfg.option<ConfigOptionStrings>("imex_mode_names");
if (names_opt) {
for (const auto& n : names_opt->values)
if (!n.empty() && n != "primary") modes.push_back(n);
if (!n.empty() && n != kImexPrimaryMode) modes.push_back(n);
}
if (right_click) {
+5 -4
View File
@@ -2,6 +2,7 @@
//#include "slic3r/Utils/Serial.hpp"
#include "Tab.hpp"
#include "PresetHints.hpp"
#include "libslic3r/IMEXHelpers.hpp"
#include "libslic3r/PresetBundle.hpp"
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/Utils.hpp"
@@ -4431,7 +4432,7 @@ public:
m_n_rows = n_rows;
m_layout = layout;
for (size_t i = 0; i < names.size(); ++i)
add_row(names[i], tools[i], gcodes[i], /*is_primary=*/(names[i] == "primary"));
add_row(names[i], tools[i], gcodes[i], /*is_primary=*/(names[i] == kImexPrimaryMode));
Layout();
}
@@ -4448,7 +4449,7 @@ public:
size_t primary_cfg_idx = std::string::npos;
if (names) {
for (size_t i = 0; i < names->values.size(); ++i) {
if (names->values[i] == "primary") {
if (names->values[i] == kImexPrimaryMode) {
primary_cfg_idx = i;
if (tools && i < tools->values.size()) primary_tools = tools->values[i];
if (gcodes && i < gcodes->values.size()) primary_gcode = gcodes->values[i];
@@ -4456,7 +4457,7 @@ public:
}
}
}
add_row("primary", primary_tools, primary_gcode, /*is_primary=*/true);
add_row(kImexPrimaryMode, primary_tools, primary_gcode, /*is_primary=*/true);
size_t n = names ? names->values.size() : 0;
for (size_t i = 0; i < n; ++i) {
@@ -4472,7 +4473,7 @@ public:
get_mode_data() const {
std::vector<std::string> names, tools, gcodes;
for (auto& r : m_rows) {
std::string nm = r.is_primary ? "primary" : r.name->GetValue().ToStdString();
std::string nm = r.is_primary ? std::string(kImexPrimaryMode) : r.name->GetValue().ToStdString();
if (nm.empty()) continue;
names.push_back(nm);
tools.push_back(active_tools_string(r));