mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-01 21:01:12 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
64460d7f85 |
@@ -588,13 +588,15 @@ const PresetBundle *PresetBundle::load_source_vendor(const boost::filesystem::pa
|
||||
return it->second.get();
|
||||
|
||||
// The library loads with no base of its own, so the tree a vendor inherits from
|
||||
// is the same one that resolves the library's own presets.
|
||||
const std::string library_file = std::string(ORCA_FILAMENT_LIBRARY);
|
||||
const PresetBundle *library = nullptr;
|
||||
if (vendor_id != ORCA_FILAMENT_LIBRARY &&
|
||||
(boost::filesystem::is_regular_file(root_dir / (library_file + ".json")) ||
|
||||
(allow_cache && boost::filesystem::is_regular_file(root_dir / (library_file + ".opc"))))) {
|
||||
library = load_source_vendor(root_dir, ORCA_FILAMENT_LIBRARY, compatibility_rule, error, allow_cache);
|
||||
// is the same one that resolves the library's own presets. It is only a base, so
|
||||
// it comes from its cache whenever that is all that is installed, even when the
|
||||
// vendor itself is parsed (a vendor updated over the air).
|
||||
const std::string library_file = std::string(ORCA_FILAMENT_LIBRARY);
|
||||
const bool library_json = boost::filesystem::is_regular_file(root_dir / (library_file + ".json"));
|
||||
const bool library_cache_only = !library_json && boost::filesystem::is_regular_file(root_dir / (library_file + ".opc"));
|
||||
const PresetBundle *library = nullptr;
|
||||
if (vendor_id != ORCA_FILAMENT_LIBRARY && (library_json || library_cache_only)) {
|
||||
library = load_source_vendor(root_dir, ORCA_FILAMENT_LIBRARY, compatibility_rule, error, allow_cache || library_cache_only);
|
||||
if (library == nullptr) {
|
||||
error = "OrcaFilamentLibrary contains invalid presets";
|
||||
return nullptr;
|
||||
@@ -2527,7 +2529,7 @@ void PresetBundle::clear_printer_hold_aliases()
|
||||
|
||||
//BBS: add json related logic, load system presets from json
|
||||
std::pair<PresetsConfigSubstitutions, std::string> PresetBundle::load_system_presets_from_json(
|
||||
ForwardCompatibilitySubstitutionRule compatibility_rule, bool allow_cache)
|
||||
ForwardCompatibilitySubstitutionRule compatibility_rule, bool write_caches)
|
||||
{
|
||||
//BBS: add config related logs
|
||||
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" enter, compatibility_rule %1%")%compatibility_rule;
|
||||
@@ -2547,14 +2549,14 @@ std::pair<PresetsConfigSubstitutions, std::string> PresetBundle::load_system_pre
|
||||
// The vendors below are loaded whole and against each other — the filament
|
||||
// library first, then every other vendor with it as the base — so each parse
|
||||
// is complete enough to be worth caching.
|
||||
m_generate_vendor_caches = allow_cache && (m_generate_vendor_caches || !validation_mode);
|
||||
m_generate_vendor_caches = write_caches && (m_generate_vendor_caches || !validation_mode);
|
||||
|
||||
// Sorted, so any duplicate-preset warning comes out in the same order on every run.
|
||||
std::vector<VendorSource> vendors;
|
||||
for (const std::string& name : vendor_names_in(dir))
|
||||
if (name == ORCA_FILAMENT_LIBRARY || !(validation_mode && !vendor_to_validate.empty() && name != vendor_to_validate))
|
||||
vendors.push_back({ name, dir });
|
||||
auto result = this->load_vendors(vendors, compatibility_rule, allow_cache);
|
||||
auto result = this->load_vendors(vendors, compatibility_rule, true);
|
||||
|
||||
this->update_system_maps();
|
||||
|
||||
|
||||
@@ -839,7 +839,9 @@ private:
|
||||
|
||||
//std::pair<PresetsConfigSubstitutions, std::string> load_system_presets(ForwardCompatibilitySubstitutionRule compatibility_rule);
|
||||
//BBS: add json related logic
|
||||
std::pair<PresetsConfigSubstitutions, std::string> load_system_presets_from_json(ForwardCompatibilitySubstitutionRule compatibility_rule, bool allow_cache = true);
|
||||
// Reads each vendor from its preset cache where one covers the profile, as every load does.
|
||||
// write_caches = false keeps a read-only load from writing caches into the data directory.
|
||||
std::pair<PresetsConfigSubstitutions, std::string> load_system_presets_from_json(ForwardCompatibilitySubstitutionRule compatibility_rule, bool write_caches = true);
|
||||
// Update the multicolor information for filaments.
|
||||
void update_filament_multi_color();
|
||||
// Update renamed_from and alias maps of system profiles.
|
||||
|
||||
@@ -11582,20 +11582,8 @@ void DynamicPrintConfig::update_diff_values_to_child_config(DynamicPrintConfig&
|
||||
else
|
||||
variant_index.resize(1, 0);
|
||||
|
||||
// A parent variant the child does not list (the parent gained it after the child was saved, or the
|
||||
// child lists none) takes the child's first variant of the same extruder, as slicing does in
|
||||
// get_config_index_base. Left unmatched, the parent's value would silently replace the user's.
|
||||
auto same_extruder = [&](int i, int child_extruder_id) {
|
||||
return cur_extruder_ids.empty() || child_extruder_id < 0 ||
|
||||
(i < int(cur_extruder_ids.size()) && cur_extruder_ids[i] == child_extruder_id);
|
||||
};
|
||||
|
||||
if (target_variant_count == 0) {
|
||||
// The child's one value belongs to the extruder of the parent's first variant.
|
||||
const int child_extruder_id = cur_extruder_ids.empty() ? -1 : cur_extruder_ids[0];
|
||||
for (int i = 0; i < int(variant_index.size()); i++)
|
||||
if (i == 0 || same_extruder(i, child_extruder_id))
|
||||
variant_index[i] = 0;
|
||||
variant_index[0] = 0;
|
||||
}
|
||||
else if ((cur_extruder_ids.size() > 0) && cur_variant_count != cur_extruder_ids.size()){
|
||||
//should not happen
|
||||
@@ -11613,22 +11601,13 @@ void DynamicPrintConfig::update_diff_values_to_child_config(DynamicPrintConfig&
|
||||
for (int j = 0; j < target_variant_count; j++)
|
||||
{
|
||||
if ((cur_extruder_variants[i] == target_extruder_variants[j])
|
||||
&&(cur_extruder_ids.empty() || target_extruder_ids.empty() || (cur_extruder_ids[i] == target_extruder_ids[j])))
|
||||
&&(cur_extruder_ids.empty() || (cur_extruder_ids[i] == target_extruder_ids[j])))
|
||||
{
|
||||
variant_index[i] = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < cur_variant_count; i++) {
|
||||
if (variant_index[i] != -1)
|
||||
continue;
|
||||
for (int j = 0; j < target_variant_count; j++)
|
||||
if (same_extruder(i, target_extruder_ids.empty() ? -1 : target_extruder_ids[j])) {
|
||||
variant_index[i] = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const t_config_option_keys &keys = new_config.keys();
|
||||
|
||||
@@ -421,81 +421,6 @@ SCENARIO("update_diff_values_to_child_config tolerates legacy machine-limit vect
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("update_diff_values_to_child_config keeps a child's values on variants it does not list",
|
||||
"[Config][Variant]") {
|
||||
std::set<std::string> no_keys;
|
||||
auto variants = [](std::initializer_list<std::string> names) { return new Slic3r::ConfigOptionStrings(names); };
|
||||
|
||||
GIVEN("A filament parent with three variants") {
|
||||
Slic3r::DynamicPrintConfig parent;
|
||||
parent.set_key_value("filament_extruder_variant",
|
||||
variants({"Direct Drive Standard", "Bowden Standard", "Direct Drive High Flow"}));
|
||||
parent.set_deserialize_strict("nozzle_temperature", "220,220,220");
|
||||
|
||||
WHEN("the child was saved when the parent had only its first variant") {
|
||||
Slic3r::DynamicPrintConfig child;
|
||||
child.set_key_value("filament_extruder_variant", variants({"Direct Drive Standard"}));
|
||||
child.set_deserialize_strict("nozzle_temperature", "199");
|
||||
parent.update_diff_values_to_child_config(child, "", "filament_extruder_variant",
|
||||
Slic3r::filament_options_with_variant, no_keys);
|
||||
THEN("the child's value applies to every variant") {
|
||||
REQUIRE(parent.opt_serialize("nozzle_temperature") == "199,199,199");
|
||||
}
|
||||
}
|
||||
WHEN("the child lists every variant, in another order") {
|
||||
Slic3r::DynamicPrintConfig child;
|
||||
child.set_key_value("filament_extruder_variant",
|
||||
variants({"Bowden Standard", "Direct Drive High Flow", "Direct Drive Standard"}));
|
||||
child.set_deserialize_strict("nozzle_temperature", "190,205,199");
|
||||
parent.update_diff_values_to_child_config(child, "", "filament_extruder_variant",
|
||||
Slic3r::filament_options_with_variant, no_keys);
|
||||
THEN("each variant keeps its own value") {
|
||||
REQUIRE(parent.opt_serialize("nozzle_temperature") == "199,190,205");
|
||||
}
|
||||
}
|
||||
WHEN("the child lists no variants") {
|
||||
Slic3r::DynamicPrintConfig child;
|
||||
child.set_deserialize_strict("nozzle_temperature", "199");
|
||||
parent.update_diff_values_to_child_config(child, "", "filament_extruder_variant",
|
||||
Slic3r::filament_options_with_variant, no_keys);
|
||||
THEN("the child's value applies to every variant") {
|
||||
REQUIRE(parent.opt_serialize("nozzle_temperature") == "199,199,199");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("A two-extruder printer parent with two variants per extruder") {
|
||||
Slic3r::DynamicPrintConfig parent;
|
||||
parent.set_key_value("printer_extruder_variant",
|
||||
variants({"Direct Drive Standard", "Direct Drive High Flow", "Direct Drive Standard", "Direct Drive High Flow"}));
|
||||
parent.set_key_value("printer_extruder_id", new Slic3r::ConfigOptionInts({1, 1, 2, 2}));
|
||||
parent.set_deserialize_strict("retraction_length", "0.8,0.8,0.8,0.8");
|
||||
|
||||
WHEN("the child lists only the Standard variant of each extruder") {
|
||||
Slic3r::DynamicPrintConfig child;
|
||||
child.set_key_value("printer_extruder_variant", variants({"Direct Drive Standard", "Direct Drive Standard"}));
|
||||
child.set_key_value("printer_extruder_id", new Slic3r::ConfigOptionInts({1, 2}));
|
||||
child.set_deserialize_strict("retraction_length", "1.1,2.2");
|
||||
parent.update_diff_values_to_child_config(child, "printer_extruder_id", "printer_extruder_variant",
|
||||
Slic3r::printer_options_with_variant_1,
|
||||
Slic3r::printer_options_with_variant_2);
|
||||
THEN("each extruder's High Flow variant takes that extruder's value") {
|
||||
REQUIRE(parent.opt_serialize("retraction_length") == "1.1,1.1,2.2,2.2");
|
||||
}
|
||||
}
|
||||
WHEN("the child lists no variants") {
|
||||
Slic3r::DynamicPrintConfig child;
|
||||
child.set_deserialize_strict("retraction_length", "1.1");
|
||||
parent.update_diff_values_to_child_config(child, "printer_extruder_id", "printer_extruder_variant",
|
||||
Slic3r::printer_options_with_variant_1,
|
||||
Slic3r::printer_options_with_variant_2);
|
||||
THEN("only the first extruder's variants take the child's value") {
|
||||
REQUIRE(parent.opt_serialize("retraction_length") == "1.1,1.1,0.8,0.8");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SCENARIO("DynamicPrintConfig JSON serialization", "[Config]") {
|
||||
// WHEN("DynamicPrintConfig is serialized and deserialized") {
|
||||
// auto now = std::chrono::high_resolution_clock::now();
|
||||
|
||||
@@ -5788,6 +5788,131 @@ TEST_CASE("A system preset no vendor lists is not resolved", "[Preset][Bundle]")
|
||||
|
||||
namespace {
|
||||
|
||||
// Writes each vendor's preset cache into dir, then deletes its profile JSONs: what a release build installs.
|
||||
void reduce_vendors_to_caches(const fs::path &dir, const std::vector<std::string> &vendor_ids)
|
||||
{
|
||||
const std::string lib(PresetBundle::ORCA_FILAMENT_LIBRARY);
|
||||
PresetBundle library;
|
||||
if (fs::exists(dir / (lib + ".json"))) {
|
||||
library.set_generate_vendor_caches(true);
|
||||
library.load_vendor_configs_from_json(dir.string(), lib, PresetBundle::LoadSystem,
|
||||
ForwardCompatibilitySubstitutionRule::EnableSilent);
|
||||
}
|
||||
for (const std::string &vendor_id : vendor_ids) {
|
||||
if (vendor_id == lib)
|
||||
continue;
|
||||
PresetBundle writer;
|
||||
writer.set_generate_vendor_caches(true);
|
||||
writer.load_vendor_configs_from_json(dir.string(), vendor_id, PresetBundle::LoadSystem,
|
||||
ForwardCompatibilitySubstitutionRule::EnableSilent, &library);
|
||||
}
|
||||
for (const std::string &vendor_id : vendor_ids) {
|
||||
REQUIRE(fs::exists(dir / (vendor_id + ".opc")));
|
||||
fs::remove(dir / (vendor_id + ".json"));
|
||||
fs::remove_all(dir / vendor_id);
|
||||
}
|
||||
}
|
||||
|
||||
// The filament library with one abstract base filament, and an "Acme" vendor whose one filament inherits it.
|
||||
void write_library_and_acme_filament(const fs::path &root)
|
||||
{
|
||||
const std::string lib(PresetBundle::ORCA_FILAMENT_LIBRARY);
|
||||
fs::create_directories(root / lib / "filament");
|
||||
std::ofstream((root / (lib + ".json")).string())
|
||||
<< R"({"version":"1.0.0","name":")" << lib << R"(",)"
|
||||
<< R"("filament_list":[{"name":"Generic PLA","sub_path":"filament/generic_pla.json"}]})";
|
||||
std::ofstream((root / lib / "filament" / "generic_pla.json").string())
|
||||
<< R"({"type":"filament","name":"Generic PLA","from":"system","instantiation":"false","filament_id":"GFL99","filament_cost":"27"})";
|
||||
fs::create_directories(root / "Acme" / "filament");
|
||||
std::ofstream((root / "Acme.json").string())
|
||||
<< R"({"version":"1.0.0","name":"Acme","filament_list":[{"name":"Acme PLA","sub_path":"filament/pla.json"}]})";
|
||||
std::ofstream((root / "Acme" / "filament" / "pla.json").string())
|
||||
<< R"({"type":"filament","name":"Acme PLA","from":"system","instantiation":"true","inherits":"Generic PLA"})";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("A read-only load resolves a user preset against vendors installed as their cache alone", "[Preset][Bundle][Regression]")
|
||||
{
|
||||
ScopedTemporaryDir temp_dir;
|
||||
const fs::path data = temp_dir.path() / "data";
|
||||
const fs::path system = data / PRESET_SYSTEM_DIR;
|
||||
ScopedDataDir scoped_data(data);
|
||||
ScopedResourcesDir scoped_resources(temp_dir.path() / "resources");
|
||||
write_acme_printer_vendor(system, 33.);
|
||||
reduce_vendors_to_caches(system, {"Acme"});
|
||||
|
||||
fs::create_directories(data / PRESET_USER_DIR / DEFAULT_USER_FOLDER_NAME / PRESET_PRINTER_NAME);
|
||||
std::ofstream((data / PRESET_USER_DIR / DEFAULT_USER_FOLDER_NAME / PRESET_PRINTER_NAME / "My Acme.json").string())
|
||||
<< R"({"type":"machine","name":"My Acme","from":"User","version":"2.3.0.0","inherits":"Acme Printer","printable_height":"123"})";
|
||||
|
||||
AppConfig app_config;
|
||||
PresetBundle bundle;
|
||||
std::string errors;
|
||||
bundle.load_presets(app_config, ForwardCompatibilitySubstitutionRule::EnableSilent, PresetBundle::PresetPreferences(),
|
||||
&errors, true);
|
||||
CHECK(errors.empty());
|
||||
const Preset *preset = bundle.printers.find_preset("My Acme");
|
||||
REQUIRE(preset != nullptr);
|
||||
CHECK_THAT(preset->config.opt_float("printable_height"), Catch::Matchers::WithinAbs(123., 1e-6));
|
||||
CHECK_THAT(preset->config.opt_float("extruder_clearance_dist_to_rod"), Catch::Matchers::WithinAbs(33., 1e-6));
|
||||
}
|
||||
|
||||
TEST_CASE("A read-only load writes no preset cache", "[Preset][Bundle][Regression]")
|
||||
{
|
||||
ScopedTemporaryDir temp_dir;
|
||||
const fs::path system = temp_dir.path() / "data" / PRESET_SYSTEM_DIR;
|
||||
ScopedDataDir scoped_data(temp_dir.path() / "data");
|
||||
ScopedResourcesDir scoped_resources(temp_dir.path() / "resources");
|
||||
write_acme_printer_vendor(system, 33.);
|
||||
|
||||
AppConfig app_config;
|
||||
PresetBundle bundle;
|
||||
std::string errors;
|
||||
bundle.load_presets(app_config, ForwardCompatibilitySubstitutionRule::EnableSilent, PresetBundle::PresetPreferences(),
|
||||
&errors, true);
|
||||
CHECK(errors.empty());
|
||||
CHECK(bundle.printers.find_preset("Acme Printer") != nullptr);
|
||||
CHECK_FALSE(fs::exists(system / "Acme.opc"));
|
||||
}
|
||||
|
||||
TEST_CASE("A vendor updated over the air resolves against the library installed as its cache alone", "[Preset][Bundle][Regression]")
|
||||
{
|
||||
ScopedTemporaryDir temp_dir;
|
||||
const fs::path system = temp_dir.path() / "data" / PRESET_SYSTEM_DIR;
|
||||
ScopedDataDir scoped_data(temp_dir.path() / "data");
|
||||
ScopedResourcesDir scoped_resources(temp_dir.path() / "resources");
|
||||
// System presets are found by name through the bundled profiles.
|
||||
write_library_and_acme_filament(temp_dir.path() / "resources" / PRESET_PROFILES_DIR);
|
||||
// The release install, then an update that brings Acme back as JSONs while the library stays a cache.
|
||||
write_library_and_acme_filament(system);
|
||||
reduce_vendors_to_caches(system, {PresetBundle::ORCA_FILAMENT_LIBRARY, "Acme"});
|
||||
write_library_and_acme_filament(temp_dir.path() / "update");
|
||||
fs::copy_file(temp_dir.path() / "update" / "Acme.json", system / "Acme.json");
|
||||
fs::create_directories(system / "Acme" / "filament");
|
||||
fs::copy_file(temp_dir.path() / "update" / "Acme" / "filament" / "pla.json", system / "Acme" / "filament" / "pla.json");
|
||||
|
||||
SECTION("by name") {
|
||||
PresetBundle bundle;
|
||||
DynamicPrintConfig config;
|
||||
std::string error;
|
||||
REQUIRE(bundle.resolve_system_preset(config, Preset::TYPE_FILAMENT, "Acme PLA",
|
||||
ForwardCompatibilitySubstitutionRule::EnableSilent, error));
|
||||
CHECK_THAT(config.opt<ConfigOptionFloats>("filament_cost")->values.front(), Catch::Matchers::WithinAbs(27., 1e-6));
|
||||
}
|
||||
SECTION("by its source file") {
|
||||
PresetBundle bundle;
|
||||
DynamicPrintConfig config;
|
||||
config.option<ConfigOptionString>(BBL_JSON_KEY_INHERITS, true)->value = "Generic PLA";
|
||||
std::string error;
|
||||
REQUIRE(bundle.resolve_preset_config(config, Preset::TYPE_FILAMENT, (system / "Acme" / "filament" / "pla.json").string(),
|
||||
ForwardCompatibilitySubstitutionRule::EnableSilent, error));
|
||||
CHECK_THAT(config.opt<ConfigOptionFloats>("filament_cost")->values.front(), Catch::Matchers::WithinAbs(27., 1e-6));
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// A default preset config for type, built the way PresetBundle builds its default presets.
|
||||
DynamicPrintConfig external_default_config(Preset::Type type)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user