From 085f5ccec8ebc419720056b1790ee3dc50c9cbae Mon Sep 17 00:00:00 2001 From: Clifford Garwood Date: Sat, 25 Apr 2026 00:32:22 -0400 Subject: [PATCH] refactor(imex): replace "primary" magic string with kImexPrimaryMode constant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/libslic3r/Format/bbs_3mf.cpp | 3 ++- src/libslic3r/GCode.cpp | 12 ++++++------ src/libslic3r/IMEXHelpers.hpp | 7 +++++++ src/slic3r/GUI/PartPlate.cpp | 16 ++++++++-------- src/slic3r/GUI/Plater.cpp | 8 ++++---- src/slic3r/GUI/Tab.cpp | 9 +++++---- 6 files changed, 32 insertions(+), 23 deletions(-) diff --git a/src/libslic3r/Format/bbs_3mf.cpp b/src/libslic3r/Format/bbs_3mf.cpp index dbc14848d0..28b2b22b37 100644 --- a/src/libslic3r/Format/bbs_3mf.cpp +++ b/src/libslic3r/Format/bbs_3mf.cpp @@ -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("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"; } { diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 0c8a1c1c6b..3b8168be6c 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2407,7 +2407,7 @@ static std::vector 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("imex_mode_names"); const auto* tools_opt = print.config().option("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; diff --git a/src/libslic3r/IMEXHelpers.hpp b/src/libslic3r/IMEXHelpers.hpp index aa0489d268..a276e6b011 100644 --- a/src/libslic3r/IMEXHelpers.hpp +++ b/src/libslic3r/IMEXHelpers.hpp @@ -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 diff --git a/src/slic3r/GUI/PartPlate.cpp b/src/slic3r/GUI/PartPlate.cpp index e44b177cd0..6ddc4b4811 100644 --- a/src/slic3r/GUI/PartPlate.cpp +++ b/src/slic3r/GUI/PartPlate.cpp @@ -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("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("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("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("imex_mode_names"); auto* tools = printer_cfg.option("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("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 used = get_extruders(true); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 9cda807d8c..ff6cf1266d 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -9944,7 +9944,7 @@ static std::vector 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 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("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) { diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 6f5b6655bb..c6610f9031 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -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 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));