Browse Source

Use r128.h for high precision snapping

Aaron Franke 1 week ago
parent
commit
af8bdac5a9
2 changed files with 24 additions and 2 deletions
  1. 23 2
      scene/gui/range.cpp
  2. 1 0
      scene/gui/range.h

+ 23 - 2
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) {

+ 1 - 0
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(); }