diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index 1fe4816ad..c8a3835dd 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -426,7 +426,19 @@ void ConfigBase::apply_only(const ConfigBase &other, const t_config_option_keys } } -// this will *ignore* options not present in both configs +// Are the two configs equal? Ignoring options not present in both configs. +bool ConfigBase::equals(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) + return false; + } + return true; +} + +// Returns options differing in the two configs, ignoring options not present in both configs. t_config_option_keys ConfigBase::diff(const ConfigBase &other) const { t_config_option_keys diff; @@ -439,6 +451,7 @@ t_config_option_keys ConfigBase::diff(const ConfigBase &other) const return diff; } +// Returns options being equal in the two configs, ignoring options not present in both configs. t_config_option_keys ConfigBase::equal(const ConfigBase &other) const { t_config_option_keys equal; @@ -1190,6 +1203,65 @@ t_config_option_keys StaticConfig::keys() const return keys; } +// Iterate over the pairs of options with equal keys, call the fn. +// Returns true on early exit by fn(). +template +static inline bool dynamic_config_iterate(const DynamicConfig &lhs, const DynamicConfig &rhs, Fn fn) +{ + std::map>::const_iterator i = lhs.cbegin(); + std::map>::const_iterator j = rhs.cbegin(); + while (i != lhs.cend() && j != rhs.cend()) + if (i->first < j->first) + ++ i; + else if (i->first > j->first) + ++ j; + else { + assert(i->first == j->first); + if (fn(i->first, i->second.get(), j->second.get())) + // Early exit by fn. + return true; + ++ i; + ++ j; + } + // Finished to the end. + return false; +} + +// Are the two configs equal? Ignoring options not present in both configs. +bool DynamicConfig::equals(const DynamicConfig &other) const +{ + return ! dynamic_config_iterate(*this, other, + [](const t_config_option_key & /* key */, const ConfigOption *l, const ConfigOption *r) { return *l != *r; }); +} + +// Returns options differing in the two configs, ignoring options not present in both configs. +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) + diff.emplace_back(key); + // Continue iterating. + return false; + }); + return diff; +} + +// Returns options being equal in the two configs, ignoring options not present in both configs. +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) + equal.emplace_back(key); + // Continue iterating. + return false; + }); + return equal; +} + } #include diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 750b9411c..6439e4632 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -1893,8 +1893,8 @@ public: // The configuration definition is static: It does not carry the actual configuration values, // but it carries the defaults of the configuration values. - ConfigBase() {} - ~ConfigBase() override {} + ConfigBase() = default; + ~ConfigBase() override = default; // Virtual overridables: public: @@ -1953,8 +1953,11 @@ public: // An UnknownOptionException is thrown in case some option keys are not defined by this->def(), // or this ConfigBase is of a StaticConfig type and it does not support some of the keys, and ignore_nonexistent is not set. void apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false); - bool equals(const ConfigBase &other) const { return this->diff(other).empty(); } + // Are the two configs equal? Ignoring options not present in both configs. + bool equals(const ConfigBase &other) const; + // Returns options differing in the two configs, ignoring options not present in both configs. t_config_option_keys diff(const ConfigBase &other) const; + // Returns options being equal in the two configs, ignoring options not present in both configs. t_config_option_keys equal(const ConfigBase &other) const; std::string opt_serialize(const t_config_option_key &opt_key) const; @@ -2022,12 +2025,12 @@ private: class DynamicConfig : public virtual ConfigBase { public: - DynamicConfig() {} + DynamicConfig() = default; DynamicConfig(const DynamicConfig &rhs) { *this = rhs; } DynamicConfig(DynamicConfig &&rhs) noexcept : options(std::move(rhs.options)) { rhs.options.clear(); } explicit DynamicConfig(const ConfigBase &rhs, const t_config_option_keys &keys); explicit DynamicConfig(const ConfigBase& rhs) : DynamicConfig(rhs, rhs.keys()) {} - virtual ~DynamicConfig() override { clear(); } + virtual ~DynamicConfig() override = default; // Copy a content of one DynamicConfig to another DynamicConfig. // If rhs.def() is not null, then it has to be equal to this->def(). @@ -2144,6 +2147,13 @@ public: } } + // Are the two configs equal? Ignoring options not present in both configs. + bool equals(const DynamicConfig &other) const; + // Returns options differing in the two configs, ignoring options not present in both configs. + t_config_option_keys diff(const DynamicConfig &other) const; + // Returns options being equal in the two configs, ignoring options not present in both configs. + t_config_option_keys equal(const DynamicConfig &other) const; + std::string& opt_string(const t_config_option_key &opt_key, bool create = false) { return this->option(opt_key, create)->value; } const std::string& opt_string(const t_config_option_key &opt_key) const { return const_cast(this)->opt_string(opt_key); } std::string& opt_string(const t_config_option_key &opt_key, unsigned int idx) { return this->option(opt_key)->get_at(idx); } diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 61ae78b5e..c0a956154 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1194,21 +1194,38 @@ inline t_config_option_keys deep_diff(const ConfigBase &config_this, const Confi return diff; } +static constexpr const std::initializer_list optional_keys { "compatible_prints", "compatible_printers" }; + +bool PresetCollection::is_dirty(const Preset *edited, const Preset *reference) +{ + if (edited != nullptr && reference != nullptr) { + // Only compares options existing in both configs. + if (! reference->config.equals(edited->config)) + 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. + for (auto &opt_key : optional_keys) + if (reference->config.has(opt_key) != edited->config.has(opt_key)) + return true; + } + return false; +} + std::vector PresetCollection::dirty_options(const Preset *edited, const Preset *reference, const bool deep_compare /*= false*/) { std::vector changed; if (edited != nullptr && reference != nullptr) { + // Only compares options existing in both configs. changed = deep_compare ? deep_diff(edited->config, reference->config) : reference->config.diff(edited->config); // 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. - std::initializer_list optional_keys { "compatible_prints", "compatible_printers" }; - for (auto &opt_key : optional_keys) { + for (auto &opt_key : optional_keys) if (reference->config.has(opt_key) != edited->config.has(opt_key)) changed.emplace_back(opt_key); - } } return changed; } diff --git a/src/libslic3r/Preset.hpp b/src/libslic3r/Preset.hpp index 4897f504c..d0e7561db 100644 --- a/src/libslic3r/Preset.hpp +++ b/src/libslic3r/Preset.hpp @@ -463,7 +463,8 @@ public: size_t num_visible() const { return std::count_if(m_presets.begin(), m_presets.end(), [](const Preset &preset){return preset.is_visible;}); } // Compare the content of get_selected_preset() with get_edited_preset() configs, return true if they differ. - bool current_is_dirty() const { return ! this->current_dirty_options().empty(); } + bool current_is_dirty() const + { return is_dirty(&this->get_edited_preset(), &this->get_selected_preset()); } // Compare the content of get_selected_preset() with get_edited_preset() configs, return the list of keys where they differ. std::vector current_dirty_options(const bool deep_compare = false) const { return dirty_options(&this->get_edited_preset(), &this->get_selected_preset(), deep_compare); } @@ -472,10 +473,11 @@ public: { return dirty_options(&this->get_edited_preset(), this->get_selected_preset_parent(), deep_compare); } // Compare the content of get_saved_preset() with get_edited_preset() configs, return true if they differ. - bool saved_is_dirty() const { return !this->saved_dirty_options().empty(); } + bool saved_is_dirty() const + { return is_dirty(&this->get_edited_preset(), &this->get_saved_preset()); } // Compare the content of get_saved_preset() with get_edited_preset() configs, return the list of keys where they differ. - std::vector saved_dirty_options(const bool deep_compare = false) const - { return dirty_options(&this->get_edited_preset(), &this->get_saved_preset(), deep_compare); } +// std::vector saved_dirty_options() const +// { return dirty_options(&this->get_edited_preset(), &this->get_saved_preset(), /* deep_compare */ false); } // Copy edited preset into saved preset. void update_saved_preset_from_current_preset() { m_saved_preset = m_edited_preset; } @@ -552,7 +554,8 @@ private: size_t update_compatible_internal(const PresetWithVendorProfile &active_printer, const PresetWithVendorProfile *active_print, PresetSelectCompatibleType unselect_if_incompatible); public: - static std::vector dirty_options(const Preset *edited, const Preset *reference, const bool is_printer_type = false); + static bool is_dirty(const Preset *edited, const Preset *reference); + static std::vector dirty_options(const Preset *edited, const Preset *reference, const bool deep_compare = false); private: // Type of this PresetCollection: TYPE_PRINT, TYPE_FILAMENT or TYPE_PRINTER. Preset::Type m_type;