Refactor config comparison logic: Enhance equality checks for ConfigOption instances by including type comparison. Update related methods in ConfigBase, DynamicConfig, and Preset classes to ensure accurate configuration diffs and equality assessments. Improve logging in MixedFilamentManager for better tracking of custom entry loading and processing.

This commit is contained in:
Rad
2026-02-11 16:08:02 +01:00
parent 112e2e5ef5
commit 2d6ec834ec
9 changed files with 317 additions and 68 deletions

View File

@@ -473,8 +473,12 @@ bool ConfigBase::equals(const ConfigBase &other, const std::set<std::string>* sk
continue;
const ConfigOption *this_opt = this->option(opt_key);
const ConfigOption *other_opt = other.option(opt_key);
if (this_opt != nullptr && other_opt != nullptr && *this_opt != *other_opt)
return false;
if (this_opt != nullptr && other_opt != nullptr) {
if (this_opt->type() != other_opt->type())
return false;
if (*this_opt != *other_opt)
return false;
}
}
return true;
}
@@ -486,8 +490,10 @@ t_config_option_keys ConfigBase::diff(const ConfigBase &other) const
for (const t_config_option_key &opt_key : this->keys()) {
const ConfigOption *this_opt = this->option(opt_key);
const ConfigOption *other_opt = other.option(opt_key);
if (this_opt != nullptr && other_opt != nullptr && *this_opt != *other_opt)
diff.emplace_back(opt_key);
if (this_opt != nullptr && other_opt != nullptr) {
if (this_opt->type() != other_opt->type() || *this_opt != *other_opt)
diff.emplace_back(opt_key);
}
}
return diff;
}
@@ -499,7 +505,8 @@ t_config_option_keys ConfigBase::equal(const ConfigBase &other) const
for (const t_config_option_key &opt_key : this->keys()) {
const ConfigOption *this_opt = this->option(opt_key);
const ConfigOption *other_opt = other.option(opt_key);
if (this_opt != nullptr && other_opt != nullptr && *this_opt == *other_opt)
if (this_opt != nullptr && other_opt != nullptr &&
this_opt->type() == other_opt->type() && *this_opt == *other_opt)
equal.emplace_back(opt_key);
}
return equal;
@@ -1763,7 +1770,11 @@ static inline bool dynamic_config_iterate(const DynamicConfig &lhs, const Dynami
bool DynamicConfig::equals(const DynamicConfig &other, const std::set<std::string>* skipped_keys) const
{
return ! dynamic_config_iterate(*this, other,
[](const t_config_option_key & /* key */, const ConfigOption *l, const ConfigOption *r) { return *l != *r; },
[](const t_config_option_key & /* key */, const ConfigOption *l, const ConfigOption *r) {
if (l->type() != r->type())
return true;
return *l != *r;
},
skipped_keys);
}
@@ -1773,7 +1784,7 @@ t_config_option_keys DynamicConfig::diff(const DynamicConfig &other) const
t_config_option_keys diff;
dynamic_config_iterate(*this, other,
[&diff](const t_config_option_key &key, const ConfigOption *l, const ConfigOption *r) {
if (*l != *r)
if (l->type() != r->type() || *l != *r)
diff.emplace_back(key);
// Continue iterating.
return false;
@@ -1787,7 +1798,7 @@ t_config_option_keys DynamicConfig::equal(const DynamicConfig &other) const
t_config_option_keys equal;
dynamic_config_iterate(*this, other,
[&equal](const t_config_option_key &key, const ConfigOption *l, const ConfigOption *r) {
if (*l == *r)
if (l->type() == r->type() && *l == *r)
equal.emplace_back(key);
// Continue iterating.
return false;

View File

@@ -1,6 +1,7 @@
#include "MixedFilament.hpp"
#include <algorithm>
#include <boost/log/trivial.hpp>
#include <cmath>
#include <cstdio>
#include <sstream>
@@ -440,23 +441,43 @@ std::string MixedFilamentManager::serialize_custom_entries() const
void MixedFilamentManager::load_custom_entries(const std::string &serialized, const std::vector<std::string> &filament_colours)
{
const size_t n = filament_colours.size();
if (serialized.empty() || n < 2)
if (serialized.empty() || n < 2) {
BOOST_LOG_TRIVIAL(debug) << "MixedFilamentManager::load_custom_entries skipped"
<< ", serialized_empty=" << (serialized.empty() ? 1 : 0)
<< ", physical_count=" << n;
return;
}
size_t parsed_rows = 0;
size_t loaded_rows = 0;
size_t updated_auto = 0;
size_t skipped_rows = 0;
std::stringstream all(serialized);
std::string row;
while (std::getline(all, row, ';')) {
if (row.empty())
continue;
++parsed_rows;
unsigned int a = 0;
unsigned int b = 0;
bool enabled = true;
bool custom = true;
int mix = 50;
if (!parse_row_definition(row, a, b, enabled, custom, mix))
if (!parse_row_definition(row, a, b, enabled, custom, mix)) {
++skipped_rows;
BOOST_LOG_TRIVIAL(warning) << "MixedFilamentManager::load_custom_entries invalid row format: " << row;
continue;
if (a == 0 || b == 0 || a > n || b > n || a == b)
}
if (a == 0 || b == 0 || a > n || b > n || a == b) {
++skipped_rows;
BOOST_LOG_TRIVIAL(warning) << "MixedFilamentManager::load_custom_entries row rejected"
<< ", row=" << row
<< ", a=" << a
<< ", b=" << b
<< ", physical_count=" << n;
continue;
}
if (!custom) {
auto it_auto = std::find_if(m_mixed.begin(), m_mixed.end(), [a, b](const MixedFilament &mf) {
@@ -465,6 +486,7 @@ void MixedFilamentManager::load_custom_entries(const std::string &serialized, co
if (it_auto != m_mixed.end()) {
it_auto->enabled = enabled;
it_auto->mix_b_percent = mix;
++updated_auto;
continue;
}
}
@@ -478,8 +500,16 @@ void MixedFilamentManager::load_custom_entries(const std::string &serialized, co
mf.enabled = enabled;
mf.custom = custom;
m_mixed.push_back(std::move(mf));
++loaded_rows;
}
refresh_display_colors(filament_colours);
BOOST_LOG_TRIVIAL(info) << "MixedFilamentManager::load_custom_entries"
<< ", physical_count=" << n
<< ", parsed_rows=" << parsed_rows
<< ", loaded_rows=" << loaded_rows
<< ", updated_auto_rows=" << updated_auto
<< ", skipped_rows=" << skipped_rows
<< ", mixed_total=" << m_mixed.size();
}
unsigned int MixedFilamentManager::resolve(unsigned int filament_id,

View File

@@ -111,7 +111,7 @@ static inline bool opts_equal(const DynamicConfig &config_old, const DynamicConf
const ConfigOption *opt_old = config_old.option(opt_key);
const ConfigOption *opt_new = config_new.option(opt_key);
assert(opt_new != nullptr);
return opt_old != nullptr && *opt_new == *opt_old;
return opt_old != nullptr && opt_new->type() == opt_old->type() && *opt_new == *opt_old;
}
std::vector<std::string> PlaceholderParser::config_diff(const DynamicPrintConfig &rhs)

View File

@@ -550,12 +550,35 @@ void Preset::save(DynamicPrintConfig* parent_config)
//BBS: only save difference if it has parent
if (parent_config) {
auto option_differs_from_default = [](const ConfigBase &cfg, const t_config_option_key &opt_key, const ConfigOption *opt) {
if (opt == nullptr)
return false;
const ConfigDef *def = cfg.def();
if (def == nullptr)
return true;
const ConfigOptionDef *opt_def = def->get(opt_key);
if (opt_def == nullptr || opt_def->default_value.get() == nullptr)
return true;
if (opt->type() != opt_def->default_value->type())
return true;
return *opt != *opt_def->default_value;
};
DynamicPrintConfig temp_config;
std::vector<std::string> dirty_options = config.diff(*parent_config);
for (const t_config_option_key &opt_key : config.keys()) {
const ConfigOption *opt_src = config.option(opt_key);
if (opt_src != nullptr && parent_config->option(opt_key) == nullptr &&
option_differs_from_default(config, opt_key, opt_src))
dirty_options.emplace_back(opt_key);
}
std::sort(dirty_options.begin(), dirty_options.end());
dirty_options.erase(std::unique(dirty_options.begin(), dirty_options.end()), dirty_options.end());
for (auto option: dirty_options)
{
ConfigOption *opt_src = config.option(option);
for (const std::string &option : dirty_options) {
const ConfigOption *opt_src = config.option(option);
if (opt_src == nullptr)
continue;
ConfigOption *opt_dst = temp_config.option(option, true);
opt_dst->set(opt_src);
}
@@ -1298,12 +1321,35 @@ Preset* PresetCollection::get_preset_differed_for_save(Preset& preset)
parent_preset = this->find_preset(inherits, false, true);
}
if (parent_preset) {
auto option_differs_from_default = [](const ConfigBase &cfg, const t_config_option_key &opt_key, const ConfigOption *opt) {
if (opt == nullptr)
return false;
const ConfigDef *def = cfg.def();
if (def == nullptr)
return true;
const ConfigOptionDef *opt_def = def->get(opt_key);
if (opt_def == nullptr || opt_def->default_value.get() == nullptr)
return true;
if (opt->type() != opt_def->default_value->type())
return true;
return *opt != *opt_def->default_value;
};
DynamicPrintConfig temp_config;
std::vector<std::string> dirty_options = preset.config.diff(parent_preset->config);
for (const t_config_option_key &opt_key : preset.config.keys()) {
const ConfigOption *opt_src = preset.config.option(opt_key);
if (opt_src != nullptr && parent_preset->config.option(opt_key) == nullptr &&
option_differs_from_default(preset.config, opt_key, opt_src))
dirty_options.emplace_back(opt_key);
}
std::sort(dirty_options.begin(), dirty_options.end());
dirty_options.erase(std::unique(dirty_options.begin(), dirty_options.end()), dirty_options.end());
for (auto option: dirty_options)
{
ConfigOption *opt_src = preset.config.option(option);
for (const std::string &option : dirty_options) {
const ConfigOption *opt_src = preset.config.option(option);
if (opt_src == nullptr)
continue;
ConfigOption *opt_dst = temp_config.option(option, true);
opt_dst->set(opt_src);
}
@@ -1329,12 +1375,33 @@ int PresetCollection::get_differed_values_to_update(Preset& preset, std::map<std
parent_preset = this->find_preset(inherit_preset, false, true);
}
if (parent_preset) {
auto option_differs_from_default = [](const ConfigBase &cfg, const t_config_option_key &opt_key, const ConfigOption *opt) {
if (opt == nullptr)
return false;
const ConfigDef *def = cfg.def();
if (def == nullptr)
return true;
const ConfigOptionDef *opt_def = def->get(opt_key);
if (opt_def == nullptr || opt_def->default_value.get() == nullptr)
return true;
if (opt->type() != opt_def->default_value->type())
return true;
return *opt != *opt_def->default_value;
};
DynamicPrintConfig temp_config;
std::vector<std::string> dirty_options = preset.config.diff(parent_preset->config);
for (const t_config_option_key &opt_key : preset.config.keys()) {
const ConfigOption *opt_src = preset.config.option(opt_key);
if (opt_src != nullptr && parent_preset->config.option(opt_key) == nullptr &&
option_differs_from_default(preset.config, opt_key, opt_src))
dirty_options.emplace_back(opt_key);
}
std::sort(dirty_options.begin(), dirty_options.end());
dirty_options.erase(std::unique(dirty_options.begin(), dirty_options.end()), dirty_options.end());
for (auto option: dirty_options)
{
ConfigOption *opt_src = preset.config.option(option);
for (const std::string &option : dirty_options) {
const ConfigOption *opt_src = preset.config.option(option);
if (opt_src)
key_values[option] = opt_src->serialize();
}
@@ -1930,7 +1997,7 @@ std::pair<Preset*, bool> PresetCollection::load_external_preset(
continue;
ConfigOption *opt_src = iter->config.option(opt);
ConfigOption *opt_dst = cfg.option(opt);
if (opt_src && opt_dst && (*opt_src != *opt_dst)) {
if (opt_src && opt_dst && opt_src->type() == opt_dst->type() && (*opt_src != *opt_dst)) {
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" change key %1% from old_value %2% to inherit's value %3%, preset_name %4%, inherits_name %5%")
%opt %(opt_dst->serialize()) %(opt_src->serialize()) %original_name %inherits;
opt_dst->set(opt_src);
@@ -1944,7 +2011,7 @@ std::pair<Preset*, bool> PresetCollection::load_external_preset(
continue;
ConfigOption *opt_src = it->config.option(opt);
ConfigOption *opt_dst = cfg.option(opt);
if (opt_src && opt_dst && (*opt_src != *opt_dst)) {
if (opt_src && opt_dst && opt_src->type() == opt_dst->type() && (*opt_src != *opt_dst)) {
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" change key %1% from old_value %2% to new_value %3%, preset_name %4%")
%opt %(opt_dst->serialize()) %(opt_src->serialize()) %original_name;
opt_dst->set(opt_src);
@@ -2720,6 +2787,42 @@ void add_correct_opts_to_diff(const std::string &opt_key, t_config_option_keys&
}
}
static bool option_differs_from_default(const ConfigBase &cfg, const t_config_option_key &opt_key, const ConfigOption *opt)
{
if (opt == nullptr)
return false;
const ConfigDef *def = cfg.def();
if (def == nullptr)
return true;
const ConfigOptionDef *opt_def = def->get(opt_key);
if (opt_def == nullptr || opt_def->default_value.get() == nullptr)
return true;
if (opt->type() != opt_def->default_value->type())
return true;
return *opt != *opt_def->default_value;
}
static void append_missing_nondefault_options(const ConfigBase &edited, const ConfigBase &reference,
t_config_option_keys &diff, const std::set<std::string> *skip = nullptr)
{
for (const t_config_option_key &opt_key : edited.keys()) {
if (skip && skip->count(opt_key) != 0)
continue;
const ConfigOption *edited_opt = edited.option(opt_key);
if (edited_opt == nullptr || reference.option(opt_key) != nullptr)
continue;
if (option_differs_from_default(edited, opt_key, edited_opt))
diff.emplace_back(opt_key);
}
}
static bool has_missing_nondefault_option(const ConfigBase &edited, const ConfigBase &reference, const std::set<std::string> *skip = nullptr)
{
t_config_option_keys missing;
append_missing_nondefault_options(edited, reference, missing, skip);
return !missing.empty();
}
// Use deep_diff to correct return of changed options, considering individual options for each extruder.
inline t_config_option_keys deep_diff(const ConfigBase &config_this, const ConfigBase &config_other)
{
@@ -2727,8 +2830,13 @@ inline t_config_option_keys deep_diff(const ConfigBase &config_this, const Confi
for (const t_config_option_key &opt_key : config_this.keys()) {
const ConfigOption *this_opt = config_this.option(opt_key);
const ConfigOption *other_opt = config_other.option(opt_key);
if (this_opt != nullptr && other_opt != nullptr && *this_opt != *other_opt)
{
if (this_opt != nullptr && other_opt != nullptr) {
if (this_opt->type() != other_opt->type()) {
diff.emplace_back(opt_key);
continue;
}
if (*this_opt == *other_opt)
continue;
//BBS: add bed_exclude_area
if (opt_key == "printable_area" || opt_key == "bed_exclude_area" || opt_key == "compatible_prints" || opt_key == "compatible_printers" || opt_key == "thumbnails") {
// Scalar variable, or a vector variable, which is independent from number of extruders,
@@ -2762,6 +2870,9 @@ inline t_config_option_keys deep_diff(const ConfigBase &config_this, const Confi
}
}
}
append_missing_nondefault_options(config_this, config_other, diff);
std::sort(diff.begin(), diff.end());
diff.erase(std::unique(diff.begin(), diff.end()), diff.end());
return diff;
}
@@ -2775,6 +2886,8 @@ bool PresetCollection::is_dirty(const Preset *edited, const Preset *reference)
// Only compares options existing in both configs.
if (! reference->config.equals(edited->config, &skipped_in_dirty))
return true;
if (has_missing_nondefault_option(edited->config, reference->config, &skipped_in_dirty))
return true;
// The "compatible_printers" option key is handled differently from the others:
// It is not mandatory. If the key is missing, it means it is compatible with any printer.
// If the key exists and it is empty, it means it is compatible with no printer.
@@ -2793,12 +2906,15 @@ std::vector<std::string> PresetCollection::dirty_options(const Preset *edited, c
changed = deep_compare ?
deep_diff(edited->config, reference->config) :
reference->config.diff(edited->config);
append_missing_nondefault_options(edited->config, reference->config, changed);
// The "compatible_printers" option key is handled differently from the others:
// It is not mandatory. If the key is missing, it means it is compatible with any printer.
// If the key exists and it is empty, it means it is compatible with no printer.
for (auto &opt_key : optional_keys)
if (reference->config.has(opt_key) != edited->config.has(opt_key))
changed.emplace_back(opt_key);
std::sort(changed.begin(), changed.end());
changed.erase(std::unique(changed.begin(), changed.end()), changed.end());
}
return changed;
}
@@ -2812,6 +2928,7 @@ std::vector<std::string> PresetCollection::dirty_options_without_option_list(con
changed = deep_compare ?
deep_diff(edited->config, reference->config) :
reference->config.diff(edited->config);
append_missing_nondefault_options(edited->config, reference->config, changed);
// The "compatible_printers" option key is handled differently from the others:
// It is not mandatory. If the key is missing, it means it is compatible with any printer.
// If the key exists and it is empty, it means it is compatible with no printer.
@@ -2819,6 +2936,8 @@ std::vector<std::string> PresetCollection::dirty_options_without_option_list(con
if (reference->config.has(opt_key) != edited->config.has(opt_key))
changed.emplace_back(opt_key);
}
std::sort(changed.begin(), changed.end());
changed.erase(std::unique(changed.begin(), changed.end()), changed.end());
auto iter = changed.begin();
while (iter != changed.end()) {
if (option_ignore_list.find(*iter) != option_ignore_list.end()) {

View File

@@ -41,6 +41,18 @@ static std::vector<std::string> s_project_options {
"wipe_tower_rotation_angle",
"curr_bed_type",
"flush_multiplier",
// Mixed filament / local-Z settings
"mixed_filament_gradient_mode",
"mixed_filament_height_lower_bound",
"mixed_filament_height_upper_bound",
"mixed_filament_cycle_layers",
"mixed_filament_advanced_dithering",
"mixed_filament_definitions",
"mixed_color_layer_height_a",
"mixed_color_layer_height_b",
"dithering_z_step_size",
"dithering_local_z_mode",
"dithering_step_painted_zones_only",
};
// SM_FEATURE: add Snapmaker machine as default
@@ -3276,41 +3288,53 @@ void PresetBundle::update_multi_material_filament_presets(size_t to_delete_filam
if (color_opt) {
DynamicPrintConfig &print_cfg = this->prints.get_edited_preset().config;
auto get_mixed_int = [this, &print_cfg](const std::string &key, int fallback) {
if (this->project_config.has(key))
return this->project_config.opt_int(key);
if (print_cfg.has(key))
return print_cfg.opt_int(key);
return this->project_config.has(key) ? this->project_config.opt_int(key) : fallback;
return fallback;
};
auto get_mixed_bool = [this, &print_cfg](const std::string &key, bool fallback) {
if (const ConfigOptionBool *opt = print_cfg.option<ConfigOptionBool>(key))
return opt->value;
if (const ConfigOptionInt *opt = print_cfg.option<ConfigOptionInt>(key))
return opt->value != 0;
if (const ConfigOptionBool *opt = this->project_config.option<ConfigOptionBool>(key))
return opt->value;
if (const ConfigOptionInt *opt = this->project_config.option<ConfigOptionInt>(key))
return opt->value != 0;
if (const ConfigOptionBool *opt = print_cfg.option<ConfigOptionBool>(key))
return opt->value;
if (const ConfigOptionInt *opt = print_cfg.option<ConfigOptionInt>(key))
return opt->value != 0;
return fallback;
};
auto get_mixed_mode = [this, &print_cfg](bool fallback) {
if (const ConfigOptionBool *opt = print_cfg.option<ConfigOptionBool>("mixed_filament_gradient_mode"))
return opt->value;
if (const ConfigOptionInt *opt = print_cfg.option<ConfigOptionInt>("mixed_filament_gradient_mode"))
return opt->value != 0;
if (const ConfigOptionBool *opt = this->project_config.option<ConfigOptionBool>("mixed_filament_gradient_mode"))
return opt->value;
if (const ConfigOptionInt *opt = this->project_config.option<ConfigOptionInt>("mixed_filament_gradient_mode"))
return opt->value != 0;
if (const ConfigOptionBool *opt = print_cfg.option<ConfigOptionBool>("mixed_filament_gradient_mode"))
return opt->value;
if (const ConfigOptionInt *opt = print_cfg.option<ConfigOptionInt>("mixed_filament_gradient_mode"))
return opt->value != 0;
return fallback;
};
auto get_mixed_float = [this, &print_cfg](const std::string &key, float fallback) {
if (this->project_config.has(key))
return float(this->project_config.opt_float(key));
if (print_cfg.has(key))
return float(print_cfg.opt_float(key));
return this->project_config.has(key) ? float(this->project_config.opt_float(key)) : fallback;
return fallback;
};
auto get_mixed_string = [this, &print_cfg](const std::string &key) {
if (print_cfg.has(key))
return print_cfg.opt_string(key);
return this->project_config.has(key) ? this->project_config.opt_string(key) : std::string();
std::string project_value;
if (this->project_config.has(key))
project_value = this->project_config.opt_string(key);
if (!project_value.empty())
return project_value;
if (print_cfg.has(key)) {
const std::string print_value = print_cfg.opt_string(key);
if (!print_value.empty())
return print_value;
}
return project_value;
};
auto set_mixed_string = [this, &print_cfg](const std::string &key, const std::string &value) {
if (ConfigOptionString *opt = print_cfg.option<ConfigOptionString>(key))

View File

@@ -219,6 +219,15 @@ static bool custom_per_printz_gcodes_tool_changes_differ(const std::vector<Custo
// Collect changes to print config, account for overrides of extruder retract values by filament presets.
//BBS: add plate index
static inline bool config_options_equal(const ConfigOption *lhs, const ConfigOption *rhs)
{
if (lhs == nullptr || rhs == nullptr)
return false;
if (lhs->type() != rhs->type())
return false;
return *lhs == *rhs;
}
static t_config_option_keys print_config_diffs(
const PrintConfig &current_config,
const DynamicPrintConfig &new_full_config,
@@ -245,12 +254,12 @@ static t_config_option_keys print_config_diffs(
if (opt_new_filament != nullptr && ! opt_new_filament->is_nil()) {
// An extruder retract override is available at some of the filament presets.
bool overriden = opt_new->overriden_by(opt_new_filament);
if (overriden || *opt_old != *opt_new) {
if (overriden || !config_options_equal(opt_old, opt_new)) {
auto opt_copy = opt_new->clone();
if (!((opt_key == "long_retractions_when_cut" || opt_key == "retraction_distances_when_cut")
&& new_full_config.option<ConfigOptionInt>("enable_long_retraction_when_cut")->value != LongRectrationLevel::EnableFilament)) // ugly code, remove it later if firmware supports
opt_copy->apply_override(opt_new_filament);
bool changed = *opt_old != *opt_copy;
bool changed = !config_options_equal(opt_old, opt_copy);
if (changed)
print_diff.emplace_back(opt_key);
if (changed || overriden) {
@@ -262,7 +271,7 @@ static t_config_option_keys print_config_diffs(
} else
delete opt_copy;
}
} else if (*opt_new != *opt_old) {
} else if (!config_options_equal(opt_new, opt_old)) {
//BBS: add plate_index logic for wipe_tower_x/wipe_tower_y
if (!opt_key.compare("wipe_tower_x") || !opt_key.compare("wipe_tower_y")) {
const ConfigOptionFloats* option_new = dynamic_cast<const ConfigOptionFloats*>(opt_new);
@@ -293,7 +302,7 @@ static t_config_option_keys full_print_config_diffs(const DynamicPrintConfig &cu
for (const t_config_option_key &opt_key : new_full_config.keys()) {
const ConfigOption *opt_old = current_full_config.option(opt_key);
const ConfigOption *opt_new = new_full_config.option(opt_key);
if (opt_old == nullptr || *opt_new != *opt_old) {
if (opt_old == nullptr || !config_options_equal(opt_new, opt_old)) {
//BBS: add plate_index logic for wipe_tower_x/wipe_tower_y
if (opt_old && (!opt_key.compare("wipe_tower_x") || !opt_key.compare("wipe_tower_y"))) {
const ConfigOptionFloats* option_new = dynamic_cast<const ConfigOptionFloats*>(opt_new);
@@ -1249,6 +1258,15 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
mixed_height_upper = std::max(mixed_height_lower, mixed_height_upper);
mixed_cycle_layers = std::max(2, mixed_cycle_layers);
BOOST_LOG_TRIVIAL(info) << "Print::apply mixed settings"
<< ", gradient_mode=" << mixed_gradient_mode
<< ", lower=" << mixed_height_lower
<< ", upper=" << mixed_height_upper
<< ", cycle_layers=" << mixed_cycle_layers
<< ", advanced_dither=" << (mixed_advanced_dither ? 1 : 0)
<< ", custom_definitions_len=" << mixed_custom_definitions.size()
<< ", physical_extruders=" << num_extruders;
// Regenerate mixed (virtual) filaments from physical filament colours and
// re-apply user custom mixed definitions.
std::vector<std::string> physical_filament_colors = m_config.filament_colour.values;
@@ -1261,6 +1279,15 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
mixed_height_upper,
mixed_cycle_layers,
mixed_advanced_dither);
size_t mixed_custom_count = 0;
for (const auto &mf : m_mixed_filament_mgr.mixed_filaments())
if (mf.custom)
++mixed_custom_count;
BOOST_LOG_TRIVIAL(info) << "Print::apply mixed manager state"
<< ", mixed_total=" << m_mixed_filament_mgr.mixed_filaments().size()
<< ", mixed_enabled=" << m_mixed_filament_mgr.enabled_count()
<< ", mixed_custom=" << mixed_custom_count;
// Total filaments = physical extruders + enabled mixed (virtual) filaments.
// Used for extruder ID clamping so that virtual IDs are accepted.
size_t num_total_filaments = m_mixed_filament_mgr.total_filaments(num_extruders);

View File

@@ -2,6 +2,7 @@
#include "I18N.hpp"
#include <atomic>
#include <cstdlib>
#include <locale>
#include <ctime>
#include <cstdarg>
@@ -337,11 +338,17 @@ void set_log_path_and_level(const std::string& file, unsigned int level)
}
#endif
//BBS log file at C:\\Users\\[yourname]\\AppData\\Roaming\\Snapmaker_Orca\\log\\[log_filename].log
auto log_folder = boost::filesystem::path(g_data_dir) / "log";
if (!boost::filesystem::exists(log_folder)) {
boost::filesystem::create_directory(log_folder);
}
// Prefer LOCALAPPDATA on Windows so runtime logs are written under:
// C:\\Users\\[user]\\AppData\\Local\\Snapmaker_Orca\\log\\*.log
// Keep g_data_dir fallback for non-Windows or missing environment.
boost::filesystem::path log_folder = boost::filesystem::path(g_data_dir) / "log";
#ifdef _WIN32
if (const char *local_appdata = std::getenv("LOCALAPPDATA"); local_appdata != nullptr && *local_appdata != '\0')
log_folder = boost::filesystem::path(local_appdata) / "Snapmaker_Orca" / "log";
#endif
if (!boost::filesystem::exists(log_folder)) {
boost::filesystem::create_directories(log_folder);
}
auto full_path = (log_folder / file).make_preferred();
g_log_sink = boost::log::add_file_log(

View File

@@ -2143,8 +2143,14 @@ void Sidebar::on_filaments_change(size_t num_filaments)
{
auto& choices = combos_filament();
if (num_filaments == choices.size())
if (num_filaments == choices.size()) {
// Project load may keep the same physical filament count while mixed
// definitions changed. Refresh mixed panel even without count changes.
update_ui_from_settings();
update_dynamic_filament_list();
update_mixed_filament_panel();
return;
}
if (choices.size() == 1 || num_filaments == 1)
choices[0]->GetDropDown().Invalidate();
@@ -2341,45 +2347,57 @@ void Sidebar::update_mixed_filament_panel()
physical_colors.resize(num_physical, "#26A69A");
auto get_mixed_int = [preset_bundle, print_cfg](const std::string &key, int fallback) {
if (preset_bundle->project_config.has(key))
return preset_bundle->project_config.opt_int(key);
if (print_cfg && print_cfg->has(key))
return print_cfg->opt_int(key);
return preset_bundle->project_config.has(key) ? preset_bundle->project_config.opt_int(key) : fallback;
return fallback;
};
auto get_mixed_bool = [preset_bundle, print_cfg](const std::string &key, bool fallback) {
if (const ConfigOptionBool *opt = preset_bundle->project_config.option<ConfigOptionBool>(key))
return opt->value;
if (const ConfigOptionInt *opt = preset_bundle->project_config.option<ConfigOptionInt>(key))
return opt->value != 0;
if (print_cfg) {
if (const ConfigOptionBool *opt = print_cfg->option<ConfigOptionBool>(key))
return opt->value;
if (const ConfigOptionInt *opt = print_cfg->option<ConfigOptionInt>(key))
return opt->value != 0;
}
if (const ConfigOptionBool *opt = preset_bundle->project_config.option<ConfigOptionBool>(key))
return opt->value;
if (const ConfigOptionInt *opt = preset_bundle->project_config.option<ConfigOptionInt>(key))
return opt->value != 0;
return fallback;
};
auto get_mixed_mode = [preset_bundle, print_cfg](bool fallback) {
if (const ConfigOptionBool *opt = preset_bundle->project_config.option<ConfigOptionBool>("mixed_filament_gradient_mode"))
return opt->value;
if (const ConfigOptionInt *opt = preset_bundle->project_config.option<ConfigOptionInt>("mixed_filament_gradient_mode"))
return opt->value != 0;
if (print_cfg) {
if (const ConfigOptionBool *opt = print_cfg->option<ConfigOptionBool>("mixed_filament_gradient_mode"))
return opt->value;
if (const ConfigOptionInt *opt = print_cfg->option<ConfigOptionInt>("mixed_filament_gradient_mode"))
return opt->value != 0;
}
if (const ConfigOptionBool *opt = preset_bundle->project_config.option<ConfigOptionBool>("mixed_filament_gradient_mode"))
return opt->value;
if (const ConfigOptionInt *opt = preset_bundle->project_config.option<ConfigOptionInt>("mixed_filament_gradient_mode"))
return opt->value != 0;
return fallback;
};
auto get_mixed_float = [preset_bundle, print_cfg](const std::string &key, float fallback) {
if (preset_bundle->project_config.has(key))
return float(preset_bundle->project_config.opt_float(key));
if (print_cfg && print_cfg->has(key))
return float(print_cfg->opt_float(key));
return preset_bundle->project_config.has(key) ? float(preset_bundle->project_config.opt_float(key)) : fallback;
return fallback;
};
auto get_mixed_string = [preset_bundle, print_cfg](const std::string &key, const std::string &fallback = std::string()) {
if (print_cfg && print_cfg->has(key))
return print_cfg->opt_string(key);
return preset_bundle->project_config.has(key) ? preset_bundle->project_config.opt_string(key) : fallback;
std::string project_value;
if (preset_bundle->project_config.has(key))
project_value = preset_bundle->project_config.opt_string(key);
if (!project_value.empty())
return project_value;
if (print_cfg && print_cfg->has(key)) {
const std::string print_value = print_cfg->opt_string(key);
if (!print_value.empty())
return print_value;
}
return project_value.empty() ? fallback : project_value;
};
auto set_mixed_int = [preset_bundle, print_cfg](const std::string &key, int value) {
if (print_cfg) {
@@ -2456,11 +2474,16 @@ void Sidebar::update_mixed_filament_panel()
mixed_mgr.load_custom_entries(mixed_definitions, physical_colors);
mixed_mgr.apply_gradient_settings(gradient_mode, lower_bound, upper_bound, cycle_layers, advanced_dithering);
set_mixed_mode(height_weighted_mode);
set_mixed_float("mixed_filament_height_lower_bound", lower_bound);
set_mixed_float("mixed_filament_height_upper_bound", upper_bound);
set_mixed_int("mixed_filament_cycle_layers", cycle_layers);
set_mixed_string("mixed_filament_definitions", mixed_mgr.serialize_custom_entries());
// During project load, sidebar may refresh before physical filament combos
// finish syncing. Avoid overwriting persisted mixed definitions while the
// physical filament set is incomplete.
if (num_physical >= 2) {
set_mixed_mode(height_weighted_mode);
set_mixed_float("mixed_filament_height_lower_bound", lower_bound);
set_mixed_float("mixed_filament_height_upper_bound", upper_bound);
set_mixed_int("mixed_filament_cycle_layers", cycle_layers);
set_mixed_string("mixed_filament_definitions", mixed_mgr.serialize_custom_entries());
}
auto &mixed = mixed_mgr.mixed_filaments();

View File

@@ -1195,10 +1195,13 @@ static wxString get_full_label(std::string opt_key, const DynamicPrintConfig& co
{
opt_key = get_pure_opt_key(opt_key);
if (config.option(opt_key)->is_nil())
const ConfigOption *raw_opt = config.option(opt_key);
if (raw_opt == nullptr || raw_opt->is_nil())
return _L("N/A");
const ConfigOptionDef* opt = config.def()->get(opt_key);
if (opt == nullptr)
return from_u8(opt_key);
return opt->full_label.empty() ? opt->label : opt->full_label;
}
@@ -1215,12 +1218,17 @@ static wxString get_string_value(std::string opt_key, const DynamicPrintConfig&
opt_idx = orig_opt_idx >= 0 ? orig_opt_idx : 0;
opt_key = get_pure_opt_key(opt_key);
if (config.option(opt_key)->is_nil())
const ConfigOption *raw_opt = config.option(opt_key);
if (raw_opt == nullptr || raw_opt->is_nil())
return _L("N/A");
wxString out;
const ConfigOptionDef* opt = config.def()->get(opt_key);
if (opt == nullptr)
return from_u8(raw_opt->serialize());
if (raw_opt->type() != opt->type)
return from_u8(raw_opt->serialize());
bool is_nullable = opt->nullable;
switch (opt->type) {