From eee7ce635b523ac573f4f820909a51f3bb108de5 Mon Sep 17 00:00:00 2001 From: Aaron Franke Date: Tue, 29 Jul 2025 15:30:05 -0700 Subject: [PATCH 1/2] Fix snapping logic in Range --- doc/classes/Range.xml | 2 +- scene/gui/range.cpp | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/classes/Range.xml b/doc/classes/Range.xml index 716587d5464..80265b2475f 100644 --- a/doc/classes/Range.xml +++ b/doc/classes/Range.xml @@ -64,7 +64,7 @@ - If greater than 0, [member value] will always be rounded to a multiple of this property's value. If [member rounded] is also [code]true[/code], [member value] will first be rounded to a multiple of this property's value, then rounded to the nearest integer. + If greater than 0, [member value] will always be rounded to a multiple of this property's value above [member min_value]. For example, if [member min_value] is [code]0.1[/code] and step is 0.2, then [member value] is limited to [code]0.1[/code], [code]0.3[/code], [code]0.5[/code], and so on. If [member rounded] is also [code]true[/code], [member value] will first be rounded to a multiple of this property's value, then rounded to the nearest integer. Range's current value. Changing this property (even via code) will trigger [signal value_changed] signal. Use [method set_value_no_signal] if you want to avoid it. diff --git a/scene/gui/range.cpp b/scene/gui/range.cpp index 8b69e923c8c..bfce675a04c 100644 --- a/scene/gui/range.cpp +++ b/scene/gui/range.cpp @@ -140,7 +140,9 @@ void Range::_set_value_no_signal(double p_val) { double Range::_calc_value(double p_val, double p_step) const { if (p_step > 0) { - p_val = Math::round((p_val - shared->min) / p_step) * p_step + shared->min; + // TODO: In the future, change `step` to a more suitable type for more robust precise calculations. + // Subtract min to support cases like min = 0.1, step = 0.2, snaps to 0.1, 0.3, 0.5, etc. + p_val = Math::snapped(p_val - shared->min, p_step) + shared->min; } if (_rounded_values) { From af8bdac5a9fadda049acc3f40cbcbc5f43fffe7d Mon Sep 17 00:00:00 2001 From: Aaron Franke Date: Tue, 12 Aug 2025 04:02:26 -0700 Subject: [PATCH 2/2] Use r128.h for high precision snapping --- scene/gui/range.cpp | 25 +++++++++++++++++++++++-- scene/gui/range.h | 1 + 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/scene/gui/range.cpp b/scene/gui/range.cpp index bfce675a04c..b3d9b17a02e 100644 --- a/scene/gui/range.cpp +++ b/scene/gui/range.cpp @@ -30,6 +30,28 @@ #include "range.h" +#include "thirdparty/misc/r128.h" + +double Range::_snapped_r128(double p_value, double p_step) { + if (p_step != 0) { + // All these lines are the equivalent of: p_value = Math::floor(p_value / p_step + 0.5) * p_step; + // Convert to String to force rounding to a decimal value (not a binary one). + String step_str = String::num(p_step); + String value_str = String::num(p_value); + R128 step_r128; + R128 value_r128; + const R128 half_r128 = R128(0.5); + r128FromString(&step_r128, step_str.ascii().get_data(), nullptr); + r128FromString(&value_r128, value_str.ascii().get_data(), nullptr); + r128Div(&value_r128, &value_r128, &step_r128); + r128Add(&value_r128, &value_r128, &half_r128); + r128Floor(&value_r128, &value_r128); + r128Mul(&value_r128, &value_r128, &step_r128); + p_value = value_r128; + } + return p_value; +} + PackedStringArray Range::get_configuration_warnings() const { PackedStringArray warnings = Control::get_configuration_warnings(); @@ -140,9 +162,8 @@ void Range::_set_value_no_signal(double p_val) { double Range::_calc_value(double p_val, double p_step) const { if (p_step > 0) { - // TODO: In the future, change `step` to a more suitable type for more robust precise calculations. // Subtract min to support cases like min = 0.1, step = 0.2, snaps to 0.1, 0.3, 0.5, etc. - p_val = Math::snapped(p_val - shared->min, p_step) + shared->min; + p_val = _snapped_r128(p_val - shared->min, p_step) + shared->min; } if (_rounded_values) { diff --git a/scene/gui/range.h b/scene/gui/range.h index 7ebda8ded0f..8cb3dc3e7d8 100644 --- a/scene/gui/range.h +++ b/scene/gui/range.h @@ -62,6 +62,7 @@ class Range : public Control { void _set_value_no_signal(double p_val); protected: + static double _snapped_r128(double p_value, double p_step); double _calc_value(double p_val, double p_step) const; virtual void _value_changed(double p_value); void _notify_shared_value_changed() { shared->emit_value_changed(); }