mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-10 17:21:10 +00:00
Migrate the IDEX/IQEX config keys testers already have
The parallel printing options shipped to testers as is_ixex and ixex_*, became is_imex and imex_* when the feature stopped being called iXex, and the two clearance keys were renamed once more to say what they measure: nozzle to carriage edge on the collision side, not the carriage's full width. Nothing translated any of it, so loading an existing printer profile dropped every one of these values - the keys are unknown and cleared. is_ixex is the one that matters most, because without it the others migrate into a feature that stays switched off, leaving settings that look configured and do nothing. Per-plate mode is persisted twice and only one path went through handle_legacy. Plate metadata in a 3MF is matched by exact attribute name and written with set_key_value, so a project saved between the per-plate mode landing and the rename loaded every plate back on Primary and sliced single-carriage with no warning. The loader now accepts the old attribute name. ixex_primary_col and ixex_primary_row are dropped rather than mapped: the primary is a role inside the mode's active-tools string now, not a grid coordinate, and they were never in an option list, so no saved file carries them. The test drives the full era-1 key list and asserts the enum values rather than non-nullness, since a forward-compatible substitution would otherwise hide a failed deserialize behind a default. handle_legacy had no test before this. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
f7a08b0868
commit
b6f690a65c
@@ -311,6 +311,10 @@ static constexpr const char* OTHER_LAYERS_PRINT_SEQUENCE_ATTR = "other_layers_pr
|
||||
static constexpr const char* OTHER_LAYERS_PRINT_SEQUENCE_NUMS_ATTR = "other_layers_print_sequence_nums";
|
||||
static constexpr const char* SPIRAL_VASE_MODE = "spiral_mode";
|
||||
static constexpr const char* IMEX_PARALLEL_MODE_ATTR = "imex_parallel_mode";
|
||||
// The same attribute before the feature was renamed IMEX. Plate metadata is matched by exact
|
||||
// string and written with set_key_value, so it never passes through handle_legacy: without this
|
||||
// a project saved in that window loads every plate back on the Primary mode, silently.
|
||||
static constexpr const char* IXEX_PARALLEL_MODE_ATTR_LEGACY = "ixex_parallel_mode";
|
||||
static constexpr const char* IMEX_HEAD_FILAMENT_MAP_ATTR = "imex_head_filament_map";
|
||||
static constexpr const char* FILAMENT_MAP_MODE_ATTR = "filament_map_mode";
|
||||
static constexpr const char* FILAMENT_MAP_ATTR = "filament_maps";
|
||||
@@ -4484,7 +4488,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
std::istringstream(value) >> std::boolalpha >> spiral_mode;
|
||||
m_curr_plater->config.set_key_value("spiral_mode", new ConfigOptionBool(spiral_mode));
|
||||
}
|
||||
else if (key == IMEX_PARALLEL_MODE_ATTR) {
|
||||
else if (key == IMEX_PARALLEL_MODE_ATTR || key == IXEX_PARALLEL_MODE_ATTR_LEGACY) {
|
||||
m_curr_plater->config.set_key_value("imex_parallel_mode", new ConfigOptionString(value));
|
||||
}
|
||||
else if (key == IMEX_HEAD_FILAMENT_MAP_ATTR) {
|
||||
|
||||
@@ -9331,6 +9331,24 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
|
||||
else if (opt_key == "wall_direction" && value == "auto") {
|
||||
value = "ccw";
|
||||
}
|
||||
// Orca: the IDEX/IQEX parallel printing keys shipped to testers as ixex_* before the feature
|
||||
// was renamed IMEX, and the two clearance keys were renamed again to say what they measure:
|
||||
// nozzle to carriage edge on the collision side, not the carriage's full width. Without this
|
||||
// an existing printer profile loses every one of these values silently.
|
||||
// is_ixex is the master gate: without it every other key below migrates into a feature that
|
||||
// stays switched off, which is worse than losing them all, because the settings then look
|
||||
// configured. ixex_primary_col/_row are the only era-1 keys with no modern counterpart (the
|
||||
// primary is a role in the mode's tools string now); they were never in an option list, so no
|
||||
// saved file carries them, and the has() check at the end of this function drops them anyway.
|
||||
else if (opt_key == "is_ixex") {
|
||||
opt_key = "is_imex";
|
||||
} else if (opt_key.compare(0, 5, "ixex_") == 0) {
|
||||
opt_key = "imex_" + opt_key.substr(5);
|
||||
if (opt_key == "imex_carriage_width_x")
|
||||
opt_key = "imex_nozzle_clearance_x";
|
||||
else if (opt_key == "imex_carriage_width_y")
|
||||
opt_key = "imex_nozzle_clearance_y";
|
||||
}
|
||||
|
||||
// Ignore the following obsolete configuration keys:
|
||||
static std::set<std::string> ignore = {
|
||||
|
||||
@@ -1298,3 +1298,69 @@ TEST_CASE("min_object_distance yields no floor when an FFF config lacks the opti
|
||||
CHECK_THAT(min_object_distance(c), Catch::Matchers::WithinAbs(12., 1e-9));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("handle_legacy migrates every IDEX/IQEX key testers already have", "[Config]") {
|
||||
// Three eras of saved profiles: the feature shipped as iXex (is_ixex + ixex_*), the clearance
|
||||
// pair was renamed to say what it measures, then the whole prefix became imex_. Driven from the
|
||||
// full era-1 key list, because the gap this test was rewritten to catch was a key the earlier
|
||||
// hand-picked version simply did not mention.
|
||||
ConfigSubstitutionContext ctxt(ForwardCompatibilitySubstitutionRule::Enable);
|
||||
|
||||
DynamicPrintConfig era1;
|
||||
era1.set_deserialize("is_ixex", "1", ctxt);
|
||||
era1.set_deserialize("ixex_gantry_count", "2", ctxt);
|
||||
era1.set_deserialize("ixex_tools_per_gantry", "2", ctxt);
|
||||
era1.set_deserialize("ixex_carriage_width_x", "12.5", ctxt);
|
||||
era1.set_deserialize("ixex_carriage_width_y", "7.25", ctxt);
|
||||
era1.set_deserialize("ixex_carriage_margin", "1.5", ctxt);
|
||||
era1.set_deserialize("ixex_tool_layout", "front-right", ctxt);
|
||||
era1.set_deserialize("ixex_viz_theme", "deuteranopia", ctxt);
|
||||
era1.set_deserialize("ixex_parallel_mode", "copy", ctxt);
|
||||
era1.set_deserialize("ixex_mode_names", "\"Duplicate\";\"Mirror\"", ctxt);
|
||||
era1.set_deserialize("ixex_mode_active_tools", "\"0:P,1:C\";\"0:P,1:M\"", ctxt);
|
||||
era1.set_deserialize("ixex_mode_gcodes", "\"M118 copy\";\"M118 mirror\"", ctxt);
|
||||
|
||||
// The gate first: every other value is inert without it, and a populated but disabled config is
|
||||
// worse than an empty one because it looks configured.
|
||||
REQUIRE(era1.option("is_imex") != nullptr);
|
||||
CHECK(era1.opt_bool("is_imex"));
|
||||
|
||||
CHECK(era1.opt_int("imex_gantry_count") == 2);
|
||||
CHECK(era1.opt_int("imex_tools_per_gantry") == 2);
|
||||
CHECK(era1.opt_float("imex_nozzle_clearance_x") == Catch::Approx(12.5));
|
||||
CHECK(era1.opt_float("imex_nozzle_clearance_y") == Catch::Approx(7.25));
|
||||
CHECK(era1.opt_float("imex_carriage_margin") == Catch::Approx(1.5));
|
||||
CHECK(era1.opt_string("imex_parallel_mode") == "copy");
|
||||
|
||||
// Both enums changed type from coString, and a forward-compatible substitution would hand back
|
||||
// a default rather than fail, so assert the value and not merely that the option exists.
|
||||
CHECK(era1.opt_enum<ImexToolLayout>("imex_tool_layout") == ImexToolLayout::FrontRight);
|
||||
CHECK(era1.opt_enum<ImexVizTheme>("imex_viz_theme") == ImexVizTheme::Deuteranopia);
|
||||
|
||||
// The three lists carry escaped, semicolon-separated values - the likeliest place for a silent
|
||||
// change - so check both elements survive in order.
|
||||
CHECK(era1.opt_string("imex_mode_names", 0u) == "Duplicate");
|
||||
CHECK(era1.opt_string("imex_mode_names", 1u) == "Mirror");
|
||||
CHECK(era1.opt_string("imex_mode_active_tools", 0u) == "0:P,1:C");
|
||||
CHECK(era1.opt_string("imex_mode_gcodes", 1u) == "M118 mirror");
|
||||
|
||||
// Era 2: renamed clearance keys, still under the old prefix.
|
||||
DynamicPrintConfig era2;
|
||||
era2.set_deserialize("ixex_nozzle_clearance_x", "3.5", ctxt);
|
||||
CHECK(era2.opt_float("imex_nozzle_clearance_x") == Catch::Approx(3.5));
|
||||
|
||||
// Era 3 (current) is untouched by the branch.
|
||||
DynamicPrintConfig era3;
|
||||
era3.set_deserialize("imex_nozzle_clearance_x", "9.0", ctxt);
|
||||
CHECK(era3.opt_float("imex_nozzle_clearance_x") == Catch::Approx(9.0));
|
||||
|
||||
// The two keys with no modern counterpart are dropped and reported, not mapped onto a key that
|
||||
// does not exist.
|
||||
ConfigSubstitutionContext obsolete_ctxt(ForwardCompatibilitySubstitutionRule::Enable);
|
||||
DynamicPrintConfig obsolete;
|
||||
obsolete.set_deserialize("ixex_primary_col", "1", obsolete_ctxt);
|
||||
obsolete.set_deserialize("ixex_primary_row", "0", obsolete_ctxt);
|
||||
CHECK(obsolete.option("imex_primary_col") == nullptr);
|
||||
CHECK(obsolete.option("imex_primary_row") == nullptr);
|
||||
CHECK(obsolete_ctxt.unrecogized_keys.size() == 2);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user