diff --git a/src/OrcaSlicer.cpp b/src/OrcaSlicer.cpp index 992a9c9bc2..b1cdc6e7f8 100644 --- a/src/OrcaSlicer.cpp +++ b/src/OrcaSlicer.cpp @@ -2823,6 +2823,37 @@ int CLI::run(int argc, char **argv) fetch_compatible_values = true; } + // Refresh every project filament no loaded filament replaces from its current system preset, as the GUI + // does when it loads the project; the filament merge below keeps the keys the project lists as changed. + // Entries stay in slot order, which the merge's variant bookkeeping relies on. + std::vector load_filaments_refresh(load_filaments_config.size(), false); + if (is_bbl_3mf && new_printer_name.empty()) { + const ConfigOptionStrings *project_filament_ids = m_print_config.option("filament_ids"); + for (size_t index = 0; index < current_filaments_system_name.size(); index++) { + const int slot = static_cast(index) + 1; + if (std::find(load_filaments_index.begin(), load_filaments_index.end(), slot) != load_filaments_index.end()) + continue; + std::string system_name = current_filaments_system_name[index]; + if (system_name.empty()) + continue; + PresetBundle::convert_filament_preset_name(current_printer_name, system_name); + DynamicPrintConfig config; + std::string error; + if (!ensure_system_preset_resolver().resolve_system_preset(config, Preset::TYPE_FILAMENT, system_name, config_substitution_rule, error)) { + BOOST_LOG_TRIVIAL(warning) << boost::format("CLI: system filament preset '%1%' not resolved (%2%); filament %3% keeps its values") % system_name % error % slot; + continue; + } + const size_t at = std::upper_bound(load_filaments_index.begin(), load_filaments_index.end(), slot) - load_filaments_index.begin(); + load_filaments_id.insert(load_filaments_id.begin() + at, + project_filament_ids != nullptr && index < project_filament_ids->size() ? project_filament_ids->values[index] : std::string()); + load_filaments_name.insert(load_filaments_name.begin() + at, system_name); + load_filaments_config.insert(load_filaments_config.begin() + at, std::move(config)); + load_filaments_index.insert(load_filaments_index.begin() + at, slot); + load_filaments_inherit.insert(load_filaments_inherit.begin() + at, system_name); + load_filaments_refresh.insert(load_filaments_refresh.begin() + at, true); + } + } + //fetch upward_compatible_machine if (fetch_upward_values) { if (!current_printer_system_name.empty()) { @@ -3513,7 +3544,7 @@ int CLI::run(int argc, char **argv) } //set the filament settings into print config - if ((load_filament_count > 0) || (up_config_to_date)) + if ((load_filament_count > 0) || (up_config_to_date) || !load_filaments_config.empty()) { //std::vector filament_variant_count(filament_count, 1); std::vector old_start_indice(filament_count, 0); @@ -3572,6 +3603,8 @@ int CLI::run(int argc, char **argv) for (int index = 0; index < load_filaments_config.size(); index++) { DynamicPrintConfig& config = load_filaments_config[index]; int filament_index = load_filaments_index[index]; + // A filament given with --load-filaments replaces the slot; a refreshed one keeps the project's changed keys. + const bool loaded = load_filament_count > 0 && !load_filaments_refresh[index]; std::vector different_keys; //ORCA: diff before load_default_gcodes_to_config, the way the process and machine @@ -3581,12 +3614,12 @@ int CLI::run(int argc, char **argv) // compared" to "compared as empty against the parent" and land in the column // as an override the user never made. std::string filament_different_settings; - if (load_filament_count > 0) + if (loaded) filament_different_settings = cli_different_settings(config, load_filaments_inherit[index], Preset::TYPE_FILAMENT); load_default_gcodes_to_config(config, Preset::TYPE_FILAMENT); - if (load_filament_count > 0) { + if (loaded) { ConfigOptionStrings *opt_filament_settings = static_cast (m_print_config.option("filament_settings_id", true)); std::string& filament_name = load_filaments_name[index]; ConfigOptionString* filament_name_setting = new ConfigOptionString(filament_name); @@ -3658,7 +3691,7 @@ int CLI::run(int argc, char **argv) flush_and_exit(CLI_CONFIG_FILE_ERROR); } - if ((load_filament_count == 0) && !different_keys_set.empty()) + if (!loaded && !different_keys_set.empty()) { std::set::iterator iter = different_keys_set.find(opt_key); if ( iter != different_keys_set.end()) { diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index 87ffa1a822..768321958d 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -5310,7 +5310,7 @@ static void apply_receiver_mix_relocations(DynamicPrintConfig& //convert the old filament preset to new one after split -static void convert_filament_preset_name(std::string& machine_name, std::string& filament_name) +void PresetBundle::convert_filament_preset_name(const std::string& machine_name, std::string& filament_name) { auto machine_iter = filament_preset_convert.find(machine_name); if (machine_iter != filament_preset_convert.end()) diff --git a/src/libslic3r/PresetBundle.hpp b/src/libslic3r/PresetBundle.hpp index 46d81a9052..5050696459 100644 --- a/src/libslic3r/PresetBundle.hpp +++ b/src/libslic3r/PresetBundle.hpp @@ -247,6 +247,9 @@ public: // Keys a project keeps when its presets are loaded: those listed in its escaped // "different_settings_to_system" entry for the preset, plus the preset bookkeeping keys. static std::set project_different_keys(const std::string &different_settings); + // A project filament saved under a name the current presets split per nozzle (e.g. H2D 0.6) is loaded from + // the preset that now holds its values. + static void convert_filament_preset_name(const std::string& machine_name, std::string& filament_name); PresetBundle(); PresetBundle(const PresetBundle &rhs); diff --git a/tests/cli/test_cli_project_missing_keys.sh b/tests/cli/test_cli_project_missing_keys.sh index 4c55709ea0..f20a2eb822 100644 --- a/tests/cli/test_cli_project_missing_keys.sh +++ b/tests/cli/test_cli_project_missing_keys.sh @@ -1,11 +1,13 @@ #!/usr/bin/env bash -# End-to-end check that the CLI loads a project's printer and process settings as the GUI does. +# End-to-end check that the CLI loads a project's printer, process and filament settings as the GUI does. # # The GUI takes every key a project does not list as changed from the project's current system preset: # keys saved before an option existed, and keys holding an older system value. Keys the project lists # in different_settings_to_system keep the project's value. A project is exported from the shipped -# Bambu Lab P1S presets; one printer key and one process key are removed, one printer key and one -# process key are changed without being listed, one key is changed and listed, and it is sliced again. +# Bambu Lab P1S presets with two filaments; one printer key and one process key are removed, one printer +# key, one process key and two filament keys (one per filament, one per extruder variant) are changed +# without being listed, one process key and the first filament's density are changed and listed, and it +# is sliced again: as is, with --uptodate, and with --load-filaments replacing only the second filament. # # usage: test_cli_project_missing_keys.sh set -u @@ -45,7 +47,7 @@ slice() { slice base "$WORK/cube.stl" \ --load-settings "$PROFILES/machine/Bambu Lab P1S 0.4 nozzle.json;$PROFILES/process/0.20mm Standard @BBL X1C.json" \ - --load-filaments "$PROFILES/filament/Bambu PLA Basic @BBL P1S 0.4 nozzle.json" + --load-filaments "$PROFILES/filament/Bambu PLA Basic @BBL P1S 0.4 nozzle.json;$PROFILES/filament/Bambu PLA Basic @BBL P1S 0.4 nozzle.json" # The removed keys, with their option defaults from PrintConfig.cpp; stale keys changed without being # listed as different, which must come back with the system value; and a listed key the project keeps. @@ -67,30 +69,47 @@ with zipfile.ZipFile(src) as zin, zipfile.ZipFile(dst, "w", zipfile.ZIP_DEFLATED for key in ("top_shell_layers", "extruder_clearance_height_to_rod"): expected[key] = config[key] config[key] = str(int(float(config[key])) + 1) + for key in ("filament_cost", "filament_max_volumetric_speed"): + expected[key] = config[key] + config[key] = [str(float(v) + 1) for v in config[key]] expected["wall_loops"] = str(int(config["wall_loops"]) + 1) config["wall_loops"] = expected["wall_loops"] + expected["filament_density"] = [str(float(config["filament_density"][0]) + 1)] + config["filament_density"][1:] + config["filament_density"] = [expected["filament_density"][0]] + [str(float(v) + 1) for v in config["filament_density"][1:]] + # One entry for the process, one per filament, one for the printer. different = config["different_settings_to_system"] different[0] = ";".join([k for k in different[0].split(";") if k] + ["wall_loops"]) + different[1] = ";".join([k for k in different[1].split(";") if k] + ["filament_density"]) data = json.dumps(config, indent=4) zout.writestr(item, data) with open(dst + ".expected.json", "w") as f: json.dump(expected, f) EOF -slice project "$WORK/old.3mf" - -"$PY" - "$WORK/project/out.3mf" "$WORK/old.3mf.expected.json" <<'EOF' +check() { + if ! "$PY" - "$WORK/$1/out.3mf" "$WORK/old.3mf.expected.json" "$1" <<'EOF' import json, sys, zipfile with zipfile.ZipFile(sys.argv[1]) as z: config = json.loads(z.read("Metadata/project_settings.config")) with open(sys.argv[2]) as f: expected = json.load(f) -errors = ["%s is %r, want %r" % (key, config.get(key), want) for key, want in expected.items() if config.get(key) != want] +errors = ["%s: %s is %r, want %r" % (sys.argv[3], key, config.get(key), want) for key, want in expected.items() if config.get(key) != want] for e in errors: print("FAIL: " + e) sys.exit(1 if errors else 0) EOF -status=$? -[ "$status" -eq 0 ] || { tail -n 40 "$WORK/project/log"; exit 1; } + then + tail -n 40 "$WORK/$1/log" + exit 1 + fi +} + +slice project "$WORK/old.3mf" +check project +slice uptodate "$WORK/old.3mf" --uptodate +check uptodate +# The replaced second filament takes the system values the refresh would have given it. +slice partial "$WORK/old.3mf" --load-filaments ";$PROFILES/filament/Bambu PLA Basic @BBL P1S 0.4 nozzle.json" +check partial echo "PASS"