Import project

This commit is contained in:
Ian Bassi
2026-08-14 11:52:47 -03:00
committed by SoftFever
parent 42f708bbb6
commit 3c37bf9ca4
6 changed files with 293 additions and 44 deletions

View File

@@ -2715,38 +2715,76 @@ void PresetBundle::load_installed_sla_materials(AppConfig &config)
preset.set_visible_from_appconfig(config);
}
// Restore the mixed-color filament metadata written by export_selections(). Every array is
// resized to the filament count so a project saved with a different filament count, or one
// predating these keys, still yields well-formed parallel arrays.
static void load_mixed_filament_settings(DynamicPrintConfig &project_config, AppConfig &config,
const std::string &printer_name, size_t n_filaments)
// Mixed-color filament metadata is project state, carried in the 3mf's project_settings.config.
// As in BambuStudio it also gets a single GLOBAL app-config snapshot, restored once at startup so
// the last session's mixes are there before any project is opened; a project load then overwrites
// them through s_project_options. It is deliberately not a per-printer snapshot: the component ids
// in filament_mixed_components are 1-based indices into the project's filament list, so re-applying
// a printer's copy on every printer change would silently replace a loaded project's mixes.
// Mirrors PresetBundle::load_selections in BambuStudio.
static void load_mixed_filament_settings(DynamicPrintConfig &project_config, const AppConfig &config, size_t n_filaments)
{
std::vector<std::string> parts;
auto load_bools = [&](const char *key, const char *opt_key) {
auto &vals = project_config.option<ConfigOptionBools>(opt_key)->values;
if (config.has_printer_setting(printer_name, key)) {
boost::algorithm::split(parts, config.get_printer_setting(printer_name, key), boost::algorithm::is_any_of(","));
auto load_bools = [&](const char *key) {
auto &vals = project_config.option<ConfigOptionBools>(key)->values;
if (config.has("presets", key)) {
boost::algorithm::split(parts, config.get("presets", key), boost::algorithm::is_any_of(","));
vals.clear();
for (const auto &p : parts) vals.push_back(p == "1");
}
vals.resize(n_filaments, false);
};
auto load_strings = [&](const char *key, const char *opt_key) {
auto &vals = project_config.option<ConfigOptionStrings>(opt_key)->values;
if (config.has_printer_setting(printer_name, key)) {
boost::algorithm::split(parts, config.get_printer_setting(printer_name, key), boost::algorithm::is_any_of("|"));
auto load_strings = [&](const char *key) {
auto &vals = project_config.option<ConfigOptionStrings>(key)->values;
if (config.has("presets", key)) {
boost::algorithm::split(parts, config.get("presets", key), boost::algorithm::is_any_of("|"));
vals = parts;
}
vals.resize(n_filaments, std::string{});
};
load_bools("filament_is_mixed", "filament_is_mixed");
load_strings("filament_mixed_components", "filament_mixed_components");
load_strings("filament_mixed_sublayer_ratios", "filament_mixed_sublayer_ratios");
load_bools("filament_mixed_gradient", "filament_mixed_gradient");
load_strings("filament_mixed_gradient_range", "filament_mixed_gradient_range");
load_strings("filament_mixed_gradient_curve", "filament_mixed_gradient_curve");
load_bools("filament_mixed_gradient_per_part", "filament_mixed_gradient_per_part");
load_bools("filament_is_mixed");
load_strings("filament_mixed_components");
load_strings("filament_mixed_sublayer_ratios");
load_bools("filament_mixed_gradient");
load_strings("filament_mixed_gradient_range");
load_bools("filament_mixed_gradient_per_part");
// The gradient curve is the one array whose values contain '|' themselves (it separates the
// control points), so it is stored C-style escaped rather than '|'-joined.
{
auto &vals = project_config.option<ConfigOptionStrings>("filament_mixed_gradient_curve")->values;
if (config.has("presets", "filament_mixed_gradient_curve")) {
std::vector<std::string> curves;
if (unescape_strings_cstyle(config.get("presets", "filament_mixed_gradient_curve"), curves))
vals = std::move(curves);
}
vals.resize(n_filaments, std::string{});
}
}
// Orca's per-printer preset memory (update_selections, which BambuStudio has no equivalent of)
// rebuilds the filament list wholesale from that printer's snapshot, presets and colours included.
// Any existing mix then describes filaments that are no longer there, so clear the arrays and size
// them to the new filament count rather than carrying stale component indices across.
static void reset_mixed_filament_settings(DynamicPrintConfig &project_config, size_t n_filaments)
{
auto reset_bools = [&](const char *opt_key) {
auto &vals = project_config.option<ConfigOptionBools>(opt_key)->values;
vals.assign(n_filaments, false);
};
auto reset_strings = [&](const char *opt_key) {
auto &vals = project_config.option<ConfigOptionStrings>(opt_key)->values;
vals.assign(n_filaments, std::string{});
};
reset_bools("filament_is_mixed");
reset_strings("filament_mixed_components");
reset_strings("filament_mixed_sublayer_ratios");
reset_bools("filament_mixed_gradient");
reset_strings("filament_mixed_gradient_range");
reset_strings("filament_mixed_gradient_curve");
reset_bools("filament_mixed_gradient_per_part");
}
void PresetBundle::update_selections(AppConfig &config)
@@ -2829,7 +2867,7 @@ void PresetBundle::update_selections(AppConfig &config)
auto flush_multipliers = matrix | boost::adaptors::transformed(boost::lexical_cast<double, std::string>);
project_config.option<ConfigOptionFloats>("flush_multiplier")->values = std::vector<double>(flush_multipliers.begin(), flush_multipliers.end());
}
load_mixed_filament_settings(project_config, config, initial_printer_profile_name, filament_presets.size());
reset_mixed_filament_settings(project_config, filament_presets.size());
// Update visibility of presets based on their compatibility with the active printer.
// Always try to select a compatible print and filament preset to the current printer preset,
@@ -2980,7 +3018,7 @@ void PresetBundle::load_selections(AppConfig &config, const PresetPreferences& p
auto flush_multipliers = matrix | boost::adaptors::transformed(boost::lexical_cast<double, std::string>);
project_config.option<ConfigOptionFloats>("flush_multiplier")->values = std::vector<double>(flush_multipliers.begin(), flush_multipliers.end());
}
load_mixed_filament_settings(project_config, config, initial_printer_profile_name, filament_presets.size());
load_mixed_filament_settings(project_config, config, filament_presets.size());
// Update visibility of presets based on their compatibility with the active printer.
// Always try to select a compatible print and filament preset to the current printer preset,
@@ -3115,8 +3153,11 @@ void PresetBundle::export_selections(AppConfig &config)
"|");
config.set_printer_setting(printer_name, "flush_multiplier", flush_multiplier_str);
// Mixed-color filament metadata. Bools are joined with ',' and strings with '|' because
// the component/ratio/curve strings themselves contain commas.
// Mixed-color filament metadata: a single global snapshot, restored by load_selections at
// startup (see the comment there). Written to the shared "presets" section rather than to this
// printer's settings on purpose — a per-printer copy is re-applied on every printer change and
// replaces a loaded project's mixes. Bools are ','-joined; the component/ratio/range strings
// are '|'-joined; the gradient curve is escaped instead, because its values contain '|'.
auto join_bools = [](const std::vector<unsigned char> &vals) {
std::string s;
for (size_t i = 0; i < vals.size(); ++i) {
@@ -3126,19 +3167,19 @@ void PresetBundle::export_selections(AppConfig &config)
return s;
};
if (auto *opt = project_config.option<ConfigOptionBools>("filament_is_mixed"))
config.set_printer_setting(printer_name, "filament_is_mixed", join_bools(opt->values));
config.set("presets", "filament_is_mixed", join_bools(opt->values));
if (auto *opt = project_config.option<ConfigOptionStrings>("filament_mixed_components"))
config.set_printer_setting(printer_name, "filament_mixed_components", boost::algorithm::join(opt->values, "|"));
config.set("presets", "filament_mixed_components", boost::algorithm::join(opt->values, "|"));
if (auto *opt = project_config.option<ConfigOptionStrings>("filament_mixed_sublayer_ratios"))
config.set_printer_setting(printer_name, "filament_mixed_sublayer_ratios", boost::algorithm::join(opt->values, "|"));
config.set("presets", "filament_mixed_sublayer_ratios", boost::algorithm::join(opt->values, "|"));
if (auto *opt = project_config.option<ConfigOptionBools>("filament_mixed_gradient"))
config.set_printer_setting(printer_name, "filament_mixed_gradient", join_bools(opt->values));
config.set("presets", "filament_mixed_gradient", join_bools(opt->values));
if (auto *opt = project_config.option<ConfigOptionStrings>("filament_mixed_gradient_range"))
config.set_printer_setting(printer_name, "filament_mixed_gradient_range", boost::algorithm::join(opt->values, "|"));
config.set("presets", "filament_mixed_gradient_range", boost::algorithm::join(opt->values, "|"));
if (auto *opt = project_config.option<ConfigOptionStrings>("filament_mixed_gradient_curve"))
config.set_printer_setting(printer_name, "filament_mixed_gradient_curve", boost::algorithm::join(opt->values, "|"));
config.set("presets", "filament_mixed_gradient_curve", escape_strings_cstyle(opt->values));
if (auto *opt = project_config.option<ConfigOptionBools>("filament_mixed_gradient_per_part"))
config.set_printer_setting(printer_name, "filament_mixed_gradient_per_part", join_bools(opt->values));
config.set("presets", "filament_mixed_gradient_per_part", join_bools(opt->values));
// BBS
//config.set("presets", "sla_print", sla_prints.get_selected_preset_name());
@@ -3361,6 +3402,12 @@ bool PresetBundle::is_mixed_filament(size_t idx) const
return opt && idx < opt->values.size() && opt->values[idx];
}
size_t PresetBundle::num_mixed_filaments() const
{
auto *opt = project_config.option<ConfigOptionBools>("filament_is_mixed");
return opt == nullptr ? 0 : size_t(std::count(opt->values.begin(), opt->values.end(), true));
}
std::vector<size_t> PresetBundle::physical_filament_config_indices() const
{
std::vector<size_t> indices;

View File

@@ -500,6 +500,9 @@ public:
// Mixed-color filament slots: virtual slots realized from 2-3 physical filaments.
bool is_mixed_filament(size_t idx) const;
std::vector<size_t> physical_filament_config_indices() const;
// How many slots are mixed. They sit at the tail of the filament list and have no nozzle of
// their own, so any resize driven by the printer's extruder count has to add this on top.
size_t num_mixed_filaments() const;
void on_extruders_count_changed(int extruder_count);

View File

@@ -8905,7 +8905,11 @@ void GUI_App::load_current_presets(bool active_preset_combox/*= false*/, bool ch
if (printer_technology == ptFFF && !edited_printer_preset.config.opt_bool("single_extruder_multi_material")) {
auto* nozzle_diameter = edited_printer_preset.config.option<ConfigOptionFloats>("nozzle_diameter");
if (nozzle_diameter) {
preset_bundle->set_num_filaments(nozzle_diameter->values.size());
// Mixed-color slots are virtual filaments kept at the tail of the list, so they have no
// nozzle of their own. Sizing to the nozzle count alone truncates them away — and this
// runs right after a project is loaded, so it would silently drop the project's mixes
// and then let update_extruder_count() strip every painted facet above the new count.
preset_bundle->set_num_filaments(nozzle_diameter->values.size() + preset_bundle->num_mixed_filaments());
}
}
this->plater()->set_printer_technology(printer_technology);

View File

@@ -2182,8 +2182,11 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
std::string new_color = new_col.GetAsString(wxC2S_HTML_SYNTAX).ToStdString();
new_colors.push_back(new_color);
}
wxGetApp().preset_bundle->set_num_filaments(num_extruder, new_colors);
wxGetApp().plater()->on_filament_count_change(num_extruder);
// Mixed-color slots are virtual filaments at the tail of the list with no nozzle of their
// own, so they are carried on top of the new extruder count instead of being truncated.
const size_t total_filaments = num_extruder + wxGetApp().preset_bundle->num_mixed_filaments();
wxGetApp().preset_bundle->set_num_filaments(total_filaments, new_colors);
wxGetApp().plater()->on_filament_count_change(total_filaments);
wxGetApp().get_tab(Preset::TYPE_PRINT)->update();
wxGetApp().preset_bundle->export_selections(*wxGetApp().app_config);
}