From 76436bb29a7b2baa7e1e6ca0dc61efa4b1161428 Mon Sep 17 00:00:00 2001 From: Valerii Bokhan <80919135+valerii-bokhan@users.noreply.github.com> Date: Thu, 26 Feb 2026 13:28:45 +0100 Subject: [PATCH] Fix: Fixed the values comparison for Float-based config options (#12478) --- src/libslic3r/Config.hpp | 22 +++++++++++++++------- src/slic3r/GUI/Field.cpp | 9 +++++++-- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 44d5e9bbc3..df2c6039a1 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -37,9 +37,9 @@ namespace Slic3r { template void serialize(Archive& ar) { ar(this->value); ar(this->percent); } }; - inline bool operator==(const FloatOrPercent& l, const FloatOrPercent& r) throw() { return l.value == r.value && l.percent == r.percent; } + inline bool operator==(const FloatOrPercent& l, const FloatOrPercent& r) throw() { return is_approx(l.value, r.value) && l.percent == r.percent; } inline bool operator!=(const FloatOrPercent& l, const FloatOrPercent& r) throw() { return !(l == r); } - inline bool operator< (const FloatOrPercent& l, const FloatOrPercent& r) throw() { return l.value < r.value || (l.value == r.value && int(l.percent) < int(r.percent)); } + inline bool operator< (const FloatOrPercent& l, const FloatOrPercent& r) throw() { return l.value < r.value || (is_approx(l.value, r.value) && int(l.percent) < int(r.percent)); } } namespace std { @@ -762,8 +762,8 @@ public: ConfigOptionType type() const override { return static_type(); } double getFloat() const override { return this->value; } ConfigOption* clone() const override { return new ConfigOptionFloat(*this); } - bool operator==(const ConfigOptionFloat &rhs) const throw() { return this->value == rhs.value; } - bool operator< (const ConfigOptionFloat &rhs) const throw() { return this->value < rhs.value; } + bool operator==(const ConfigOptionFloat &rhs) const throw() { return is_approx(this->value, rhs.value); } + bool operator< (const ConfigOptionFloat &rhs) const throw() { return this->value < rhs.value; } std::string serialize() const override { @@ -780,6 +780,14 @@ public: return !iss.fail(); } + bool operator==(const ConfigOption &rhs) const override + { + if (rhs.type() != this->type()) + throw ConfigurationError("ConfigOptionFloat: Comparing incompatible types"); + assert(dynamic_cast(&rhs)); + return *this == *static_cast(&rhs); + } + ConfigOptionFloat& operator=(const ConfigOption *opt) { this->set(opt); @@ -906,7 +914,7 @@ protected: if (v1.size() != v2.size()) return false; for (auto it1 = v1.begin(), it2 = v2.begin(); it1 != v1.end(); ++ it1, ++ it2) - if (! ((std::isnan(*it1) && std::isnan(*it2)) || *it1 == *it2)) + if (! ((std::isnan(*it1) && std::isnan(*it2)) || is_approx(*it1, *it2))) return false; return true; } else @@ -1258,11 +1266,11 @@ public: return *this == *static_cast(&rhs); } bool operator==(const ConfigOptionFloatOrPercent &rhs) const throw() - { return this->value == rhs.value && this->percent == rhs.percent; } + { return is_approx(this->value, rhs.value) && this->percent == rhs.percent; } size_t hash() const throw() override { size_t seed = std::hash{}(this->value); return this->percent ? seed ^ 0x9e3779b9 : seed; } bool operator< (const ConfigOptionFloatOrPercent &rhs) const throw() - { return this->value < rhs.value || (this->value == rhs.value && int(this->percent) < int(rhs.percent)); } + { return this->value < rhs.value || (is_approx(this->value, rhs.value) && int(this->percent) < int(rhs.percent)); } double get_abs_value(double ratio_over) const { return this->percent ? (ratio_over * this->value / 100) : this->value; } diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 41a4bcee35..4da5f8b092 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -883,13 +883,18 @@ bool TextCtrl::value_was_changed() case coInt: return boost::any_cast(m_value) != boost::any_cast(val); case coPercent: - case coPercents: + case coPercents: { + if (m_opt.nullable && std::isnan(boost::any_cast(m_value)) && + std::isnan(boost::any_cast(val))) + return false; + return boost::any_cast(m_value) != boost::any_cast(val); + } case coFloats: case coFloat: { if (m_opt.nullable && std::isnan(boost::any_cast(m_value)) && std::isnan(boost::any_cast(val))) return false; - return boost::any_cast(m_value) != boost::any_cast(val); + return !is_approx(boost::any_cast(m_value), boost::any_cast(val)); } case coString: case coStrings: