Fix float number not working properly for option min/max (#11211)

* ConfigOptionDef: min/max values type are changed from INT to FLOAT.

(cherry picked from commit f277bc80c22e0c9a067481a4301922e2c96aed47)

* Fix infinite loop and crash when `fuzzy_skin_point_distance` = 0 (SoftFever/OrcaSlicer#11069)

* Fix Linux build issue

* Fix float comparison due to precision loss
This commit is contained in:
Noisyfox
2026-01-01 05:23:46 +08:00
committed by GitHub
parent 32cf44fc0a
commit 69861b57f9
5 changed files with 38 additions and 20 deletions

View File

@@ -318,6 +318,19 @@ ConfigOption* ConfigOptionDef::create_default_option() const
return this->create_empty_option(); return this->create_empty_option();
} }
bool ConfigOptionDef::is_value_valid(const double value, const int max_precision /*= 4*/) const
{
if (this->min == 0.f && value < 0) { // Special handling of 0
return false;
}
const double ep = std::pow(0.1, max_precision);
if (is_approx(value, (double) this->min, ep) || is_approx(value, (double) this->max, ep))
return true;
return this->min <= value && value <= this->max;
}
// Assignment of the serialization IDs is not thread safe. The Defs shall be initialized from the main thread! // Assignment of the serialization IDs is not thread safe. The Defs shall be initialized from the main thread!
ConfigOptionDef* ConfigDef::add(const t_config_option_key &opt_key, ConfigOptionType type) ConfigOptionDef* ConfigDef::add(const t_config_option_key &opt_key, ConfigOptionType type)
{ {

View File

@@ -4,6 +4,7 @@
#include <assert.h> #include <assert.h>
#include <map> #include <map>
#include <climits> #include <climits>
#include <cfloat>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <functional> #include <functional>
@@ -2455,10 +2456,11 @@ public:
// Optional width of an input field. // Optional width of an input field.
int width = -1; int width = -1;
// <min, max> limit of a numeric input. // <min, max> limit of a numeric input.
// If not set, the <min, max> is set to <INT_MIN, INT_MAX> // If not set, the <min, max> is set to <-FLT_MAX, FLT_MAX>
// By setting min=0, only nonnegative input is allowed. // By setting min=0, only nonnegative input is allowed.
int min = INT_MIN; float min = -FLT_MAX;
int max = INT_MAX; float max = FLT_MAX;
bool is_value_valid(double value, int max_precision = 4) const;
// To check if it's not a typo and a % is missing // To check if it's not a typo and a % is missing
double max_literal = 1; double max_literal = 1;
ConfigOptionMode mode = comSimple; ConfigOptionMode mode = comSimple;

View File

@@ -2150,7 +2150,7 @@ void PrintConfigDef::init_fff_params()
"\n\nThe final object flow ratio is this value multiplied by the filament flow ratio."); "\n\nThe final object flow ratio is this value multiplied by the filament flow ratio.");
def->mode = comAdvanced; def->mode = comAdvanced;
def->max = 2; def->max = 2;
def->min = 0.01; def->min = 0.01f;
def->set_default_value(new ConfigOptionFloat(1)); def->set_default_value(new ConfigOptionFloat(1));
def = this->add("enable_pressure_advance", coBools); def = this->add("enable_pressure_advance", coBools);
@@ -3245,7 +3245,7 @@ void PrintConfigDef::init_fff_params()
def->category = L("Others"); def->category = L("Others");
def->tooltip = L("The average distance between the random points introduced on each line segment."); def->tooltip = L("The average distance between the random points introduced on each line segment.");
def->sidetext = L("mm"); // milimeters, CIS languages need translation def->sidetext = L("mm"); // milimeters, CIS languages need translation
def->min = 0; def->min = 0.01f; // point distance cannot be 0! Otherwise we get infinite loop + OOM due to infinite line division.
def->max = 5; def->max = 5;
def->mode = comSimple; def->mode = comSimple;
def->set_default_value(new ConfigOptionFloat(0.3)); def->set_default_value(new ConfigOptionFloat(0.3));
@@ -3308,7 +3308,7 @@ void PrintConfigDef::init_fff_params()
def->category = L("Others"); def->category = L("Others");
def->tooltip = L("The base size of the coherent noise features, in mm. Higher values will result in larger features."); def->tooltip = L("The base size of the coherent noise features, in mm. Higher values will result in larger features.");
def->sidetext = L("mm"); // milimeters, CIS languages need translation def->sidetext = L("mm"); // milimeters, CIS languages need translation
def->min = 0.1; def->min = 0.1f;
def->max = 500; def->max = 500;
def->mode = comAdvanced; def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(1.0)); def->set_default_value(new ConfigOptionFloat(1.0));
@@ -3326,7 +3326,7 @@ void PrintConfigDef::init_fff_params()
def->label = L("Fuzzy skin noise persistence"); def->label = L("Fuzzy skin noise persistence");
def->category = L("Others"); def->category = L("Others");
def->tooltip = L("The decay rate for higher octaves of the coherent noise. Lower values will result in smoother noise."); def->tooltip = L("The decay rate for higher octaves of the coherent noise. Lower values will result in smoother noise.");
def->min = 0.01; def->min = 0.01f;
def->max = 1; def->max = 1;
def->mode = comAdvanced; def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.5)); def->set_default_value(new ConfigOptionFloat(0.5));
@@ -9555,13 +9555,13 @@ std::map<std::string, std::string> validate(const FullPrintConfig &cfg, bool und
case coFloatOrPercent: case coFloatOrPercent:
{ {
auto *fopt = static_cast<const ConfigOptionFloat*>(opt); auto *fopt = static_cast<const ConfigOptionFloat*>(opt);
out_of_range = fopt->value < optdef->min || fopt->value > optdef->max; out_of_range = !optdef->is_value_valid(fopt->value);
break; break;
} }
case coFloats: case coFloats:
case coPercents: case coPercents:
for (double v : static_cast<const ConfigOptionVector<double>*>(opt)->values) for (double v : static_cast<const ConfigOptionVector<double>*>(opt)->values)
if (v < optdef->min || v > optdef->max) { if (!optdef->is_value_valid(v)) {
out_of_range = true; out_of_range = true;
break; break;
} }
@@ -9569,12 +9569,12 @@ std::map<std::string, std::string> validate(const FullPrintConfig &cfg, bool und
case coInt: case coInt:
{ {
auto *iopt = static_cast<const ConfigOptionInt*>(opt); auto *iopt = static_cast<const ConfigOptionInt*>(opt);
out_of_range = iopt->value < optdef->min || iopt->value > optdef->max; out_of_range = !optdef->is_value_valid(iopt->value);
break; break;
} }
case coInts: case coInts:
for (int v : static_cast<const ConfigOptionVector<int>*>(opt)->values) for (int v : static_cast<const ConfigOptionVector<int>*>(opt)->values)
if (v < optdef->min || v > optdef->max) { if (!optdef->is_value_valid(v)) {
out_of_range = true; out_of_range = true;
break; break;
} }

View File

@@ -309,7 +309,7 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true
show_error(m_parent, _(L("Invalid numeric."))); show_error(m_parent, _(L("Invalid numeric.")));
set_value(double_to_string(val), true); set_value(double_to_string(val), true);
} }
if (m_opt.min > val || val > m_opt.max) if (!m_opt.is_value_valid(val))
{ {
if (!check_value) { if (!check_value) {
m_value.clear(); m_value.clear();
@@ -1107,8 +1107,8 @@ void SpinCtrl::BUILD() {
break; break;
} }
const int min_val = m_opt.min == INT_MIN ? 0 : m_opt.min; const int min_val = m_opt.min == -FLT_MAX ? 0 : (int)m_opt.min;
const int max_val = m_opt.max < 2147483647 ? m_opt.max : 2147483647; const int max_val = m_opt.max < FLT_MAX ? (int)m_opt.max : INT_MAX;
static Builder<SpinInput> builder; static Builder<SpinInput> builder;
auto temp = builder.build(m_parent, "", "", wxDefaultPosition, size, auto temp = builder.build(m_parent, "", "", wxDefaultPosition, size,
@@ -1164,7 +1164,7 @@ void SpinCtrl::BUILD() {
if (!parsed || value < INT_MIN || value > INT_MAX) if (!parsed || value < INT_MIN || value > INT_MAX)
tmp_value = UNDEF_VALUE; tmp_value = UNDEF_VALUE;
else { else {
tmp_value = std::min(std::max((int)value, m_opt.min), m_opt.max); tmp_value = std::min(std::max((int)value, temp->GetMin()), temp->GetMax());
#ifdef __WXOSX__ #ifdef __WXOSX__
#ifdef UNDEFINED__WXOSX__ // BBS #ifdef UNDEFINED__WXOSX__ // BBS
// Forcibly set the input value for SpinControl, since the value // Forcibly set the input value for SpinControl, since the value
@@ -1217,7 +1217,7 @@ void SpinCtrl::set_value(const boost::any& value, bool change_event) {
m_disable_change_event = !change_event; m_disable_change_event = !change_event;
m_value = value; m_value = value;
if (value.empty()) { // BBS: null value if (value.empty()) { // BBS: null value
dynamic_cast<SpinInput*>(window)->SetValue(m_opt.min); dynamic_cast<SpinInput*>(window)->SetValue(dynamic_cast<SpinInput*>(window)->GetMin());
dynamic_cast<SpinInput*>(window)->GetTextCtrl()->SetValue(""); dynamic_cast<SpinInput*>(window)->GetTextCtrl()->SetValue("");
} }
else { else {
@@ -2158,8 +2158,8 @@ boost::any& PointCtrl::get_value()
show_error(m_parent, _L("Invalid numeric.")); show_error(m_parent, _L("Invalid numeric."));
} }
else else
if (m_opt.min > x || x > m_opt.max || if (!m_opt.is_value_valid(x) ||
m_opt.min > y || y > m_opt.max) !m_opt.is_value_valid(y))
{ {
if (m_opt.min > x) x = m_opt.min; if (m_opt.min > x) x = m_opt.min;
if (x > m_opt.max) x = m_opt.max; if (x > m_opt.max) x = m_opt.max;
@@ -2218,8 +2218,8 @@ void SliderCtrl::BUILD()
auto temp = new wxBoxSizer(wxHORIZONTAL); auto temp = new wxBoxSizer(wxHORIZONTAL);
auto def_val = m_opt.get_default_value<ConfigOptionInt>()->value; auto def_val = m_opt.get_default_value<ConfigOptionInt>()->value;
auto min = m_opt.min == INT_MIN ? 0 : m_opt.min; auto min = m_opt.min == -FLT_MAX ? 0 : (int)m_opt.min;
auto max = m_opt.max == INT_MAX ? 100 : m_opt.max; auto max = m_opt.max == FLT_MAX ? 100 : INT_MAX;
m_slider = new wxSlider(m_parent, wxID_ANY, def_val * m_scale, m_slider = new wxSlider(m_parent, wxID_ANY, def_val * m_scale,
min * m_scale, max * m_scale, min * m_scale, max * m_scale,

View File

@@ -78,6 +78,9 @@ public:
void SetRange(int min, int max); void SetRange(int min, int max);
int GetMin() const { return this->min; }
int GetMax() const { return this->max; }
protected: protected:
void DoSetToolTipText(wxString const &tip) override; void DoSetToolTipText(wxString const &tip) override;