From 79864b3cecd15cecaaeeded2d301de7b1d96511c Mon Sep 17 00:00:00 2001 From: fvogel Date: Sat, 5 Jan 2019 20:55:12 +0000 Subject: Fix [3003895fff] and [1899040fff]: TkRoundToResolution doesn't account for -from --- generic/tkScale.c | 56 ++++++++++++++++++++++++++++++++++-------------------- generic/tkScale.h | 3 ++- tests/scale.test | 46 ++++++++++++++++++++++++++++++++------------ unix/tkUnixScale.c | 8 ++++---- 4 files changed, 75 insertions(+), 38 deletions(-) diff --git a/generic/tkScale.c b/generic/tkScale.c index af45afa..547f698 100644 --- a/generic/tkScale.c +++ b/generic/tkScale.c @@ -611,7 +611,7 @@ ConfigureScale( TCL_GLOBAL_ONLY); if ((valuePtr != NULL) && (Tcl_GetDoubleFromObj(NULL, valuePtr, &value) == TCL_OK)) { - scalePtr->value = TkRoundToResolution(scalePtr, value); + scalePtr->value = TkRoundValueToResolution(scalePtr, value); } } @@ -620,10 +620,10 @@ ConfigureScale( * orientation and creating GCs. */ - scalePtr->fromValue = TkRoundToResolution(scalePtr, + scalePtr->fromValue = TkRoundValueToResolution(scalePtr, scalePtr->fromValue); - scalePtr->toValue = TkRoundToResolution(scalePtr, scalePtr->toValue); - scalePtr->tickInterval = TkRoundToResolution(scalePtr, + scalePtr->toValue = TkRoundValueToResolution(scalePtr, scalePtr->toValue); + scalePtr->tickInterval = TkRoundIntervalToResolution(scalePtr, scalePtr->tickInterval); /* @@ -1119,10 +1119,14 @@ TkEventuallyRedrawScale( /* *-------------------------------------------------------------- * - * TkRoundToResolution -- + * TkRoundValueToResolution, TkRoundIntervalToResolution -- * * Round a given floating-point value to the nearest multiple of the * scale's resolution. + * TkRoundValueToResolution rounds an absolute value based on the from + * value as a reference. + * TkRoundIntervalToResolution rounds a relative value without + * reference, i.e. it rounds an interval. * * Results: * The return value is the rounded result. @@ -1134,28 +1138,38 @@ TkEventuallyRedrawScale( */ double -TkRoundToResolution( +TkRoundValueToResolution( TkScale *scalePtr, /* Information about scale widget. */ double value) /* Value to round. */ { - double rem, rounded, tick; + double rem, start; if (scalePtr->resolution <= 0) { return value; } - tick = floor(value/scalePtr->resolution); - rounded = scalePtr->resolution * tick; - rem = value - rounded; + start = fmod(scalePtr->fromValue, scalePtr->resolution); + rem = fmod(value - start + scalePtr->resolution/2, scalePtr->resolution); if (rem < 0) { - if (rem <= -scalePtr->resolution/2) { - rounded = (tick - 1.0) * scalePtr->resolution; - } - } else { - if (rem >= scalePtr->resolution/2) { - rounded = (tick + 1.0) * scalePtr->resolution; - } + rem += scalePtr->resolution; + } + return value + scalePtr->resolution/2 - rem; +} + +double +TkRoundIntervalToResolution( + TkScale *scalePtr, /* Information about scale widget. */ + double value) /* Value to round. */ +{ + double rem; + + if (scalePtr->resolution <= 0) { + return value; + } + rem = fmod(value + scalePtr->resolution/2, scalePtr->resolution); + if (rem < 0) { + rem += scalePtr->resolution; } - return rounded; + return value + scalePtr->resolution/2 - rem; } /* @@ -1238,7 +1252,7 @@ ScaleVarProc( resultStr = "can't assign non-numeric value to scale variable"; ScaleSetVariable(scalePtr); } else { - scalePtr->value = TkRoundToResolution(scalePtr, value); + scalePtr->value = TkRoundValueToResolution(scalePtr, value); /* * This code is a bit tricky because it sets the scale's value before @@ -1282,7 +1296,7 @@ TkScaleSetValue( int invokeCommand) /* Non-zero means invoked -command option to * notify of new value, 0 means don't. */ { - value = TkRoundToResolution(scalePtr, value); + value = TkRoundValueToResolution(scalePtr, value); if ((value < scalePtr->fromValue) ^ (scalePtr->toValue < scalePtr->fromValue)) { value = scalePtr->fromValue; @@ -1402,7 +1416,7 @@ TkScalePixelToValue( } value = scalePtr->fromValue + value * (scalePtr->toValue - scalePtr->fromValue); - return TkRoundToResolution(scalePtr, value); + return TkRoundValueToResolution(scalePtr, value); } /* diff --git a/generic/tkScale.h b/generic/tkScale.h index aa0feff..6756e73 100644 --- a/generic/tkScale.h +++ b/generic/tkScale.h @@ -219,7 +219,8 @@ typedef struct TkScale { */ MODULE_SCOPE void TkEventuallyRedrawScale(TkScale *scalePtr, int what); -MODULE_SCOPE double TkRoundToResolution(TkScale *scalePtr, double value); +MODULE_SCOPE double TkRoundValueToResolution(TkScale *scalePtr, double value); +MODULE_SCOPE double TkRoundIntervalToResolution(TkScale *scalePtr, double value); MODULE_SCOPE TkScale * TkpCreateScale(Tk_Window tkwin); MODULE_SCOPE void TkpDestroyScale(TkScale *scalePtr); MODULE_SCOPE void TkpDisplayScale(ClientData clientData); diff --git a/tests/scale.test b/tests/scale.test index 79524eb..6344eef 100644 --- a/tests/scale.test +++ b/tests/scale.test @@ -1104,78 +1104,78 @@ test scale-13.6 {SetScaleValue procedure} -body { destroy .s pack [scale .s] update -test scale-14.1 {RoundToResolution procedure} -body { +test scale-14.1 {RoundValueToResolution procedure} -body { .s configure -from 0 -to 100 -sliderlength 10 -length 114 -bd 2 \ -orient horizontal -resolution 4.0 update .s get 84 152 } -result 72 -test scale-14.2 {RoundToResolution procedure} -body { +test scale-14.2 {RoundValueToResolution procedure} -body { .s configure -from 0 -to 100 -sliderlength 10 -length 114 -bd 2 \ -orient horizontal -resolution 4.0 update .s get 86 152 } -result 76 -test scale-14.3 {RoundToResolution procedure} -body { +test scale-14.3 {RoundValueToResolution procedure} -body { .s configure -from 100 -to 0 -sliderlength 10 -length 114 -bd 2 \ -orient horizontal -resolution 4.0 update .s get 84 152 } -result 28 -test scale-14.4 {RoundToResolution procedure} -body { +test scale-14.4 {RoundValueToResolution procedure} -body { .s configure -from 100 -to 0 -sliderlength 10 -length 114 -bd 2 \ -orient horizontal -resolution 4.0 update .s get 86 152 } -result 24 -test scale-14.5 {RoundToResolution procedure} -body { +test scale-14.5 {RoundValueToResolution procedure} -body { .s configure -from -100 -to 0 -sliderlength 10 -length 114 -bd 2 \ -orient horizontal -resolution 4.0 update .s get 84 152 } -result {-28} -test scale-14.6 {RoundToResolution procedure} -body { +test scale-14.6 {RoundValueToResolution procedure} -body { .s configure -from -100 -to 0 -sliderlength 10 -length 114 -bd 2 \ -orient horizontal -resolution 4.0 update .s get 86 152 } -result {-24} -test scale-14.7 {RoundToResolution procedure} -body { +test scale-14.7 {RoundValueToResolution procedure} -body { .s configure -from 0 -to -100 -sliderlength 10 -length 114 -bd 2 \ -orient horizontal -resolution 4.0 update .s get 84 152 } -result {-72} -test scale-14.8 {RoundToResolution procedure} -body { +test scale-14.8 {RoundValueToResolution procedure} -body { .s configure -from 0 -to -100 -sliderlength 10 -length 114 -bd 2 \ -orient horizontal -resolution 4.0 update .s get 86 152 } -result {-76} -test scale-14.9 {RoundToResolution procedure} -body { +test scale-14.9 {RoundValueToResolution procedure} -body { .s configure -from 0 -to 2.25 -sliderlength 10 -length 114 -bd 2 \ -orient horizontal -resolution 0 update .s get 84 152 } -result {1.64} -test scale-14.10 {RoundToResolution procedure} -body { +test scale-14.10 {RoundValueToResolution procedure} -body { .s configure -from 0 -to 2.25 -sliderlength 10 -length 114 -bd 2 \ -orient horizontal -resolution 0 update .s get 86 152 } -result {1.69} -test scale-14.11 {RoundToResolution procedure} -body { +test scale-14.11 {RoundValueToResolution procedure} -body { .s configure -from 0 -to 225 -sliderlength 10 -length 114 -bd 2 \ -orient horizontal -resolution 0 -digits 5 update .s get 84 152 } -result {164.25} -test scale-14.12 {RoundToResolution procedure} -body { +test scale-14.12 {RoundValueToResolution procedure} -body { .s configure -from 0 -to 225 -sliderlength 10 -length 114 -bd 2 \ -orient horizontal -resolution 0 -digits 5 update @@ -1183,6 +1183,28 @@ test scale-14.12 {RoundToResolution procedure} -body { } -result {168.75} destroy .s +test scale-14a.1 {RoundValueToResolution, RoundIntervalToResolution procedures} -setup { + pack [scale .s -orient horizontal] + update +} -body { + .s configure -length 400 -bd 0 -from 1 -to 9 -resolution 2 -tickinterval 1 + update + .s get 200 0 +} -cleanup { + destroy .s +} -result {5} +test scale-14a.2 {RoundValueToResolution, RoundIntervalToResolution procedures} -setup { + pack [scale .s -orient horizontal] + update +} -body { + .s configure -length 400 -bd 0 -from -1.5 -to 1.5 -resolution 1 \ + -tickinterval 1 -digits 2 + update + .s get 250 0 +} -cleanup { + destroy .s +} -result {0.5} + test scale-15.1 {ScaleVarProc procedure} -setup { deleteWindows diff --git a/unix/tkUnixScale.c b/unix/tkUnixScale.c index 8f88018..2009288 100644 --- a/unix/tkUnixScale.c +++ b/unix/tkUnixScale.c @@ -150,11 +150,11 @@ DisplayVerticalScale( for (tickValue = scalePtr->fromValue; ; tickValue += tickInterval) { /* - * The TkRoundToResolution call gets rid of accumulated + * The TkRoundValueToResolution call gets rid of accumulated * round-off errors, if any. */ - tickValue = TkRoundToResolution(scalePtr, tickValue); + tickValue = TkRoundValueToResolution(scalePtr, tickValue); if (scalePtr->toValue >= scalePtr->fromValue) { if (tickValue > scalePtr->toValue) { break; @@ -370,11 +370,11 @@ DisplayHorizontalScale( for (tickValue = scalePtr->fromValue; ; tickValue += tickInterval) { /* - * The TkRoundToResolution call gets rid of accumulated + * The TkRoundValueToResolution call gets rid of accumulated * round-off errors, if any. */ - tickValue = TkRoundToResolution(scalePtr, tickValue); + tickValue = TkRoundValueToResolution(scalePtr, tickValue); if (scalePtr->toValue >= scalePtr->fromValue) { if (tickValue > scalePtr->toValue) { break; -- cgit v0.12 From 3866cadb1cfcaf4145fed26ace23efb3a0d16254 Mon Sep 17 00:00:00 2001 From: fvogel Date: Wed, 9 Jan 2019 21:58:56 +0000 Subject: Add new test scale-14.13 to guard against regressions with [220665ffff], and duplicates [220265ffff] and [779559ffff]. This test currently fails in the present bugfix branch but passes in core-8-6-branch --- tests/scale.test | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/scale.test b/tests/scale.test index 6344eef..7fa3a62 100644 --- a/tests/scale.test +++ b/tests/scale.test @@ -1183,6 +1183,19 @@ test scale-14.12 {RoundValueToResolution procedure} -body { } -result {168.75} destroy .s +test scale-14.13 {RoundValueToResolution procedure, round-off errors} -setup { + # see [220665ffff], and duplicates [220265ffff] and [779559ffff] + set x NotSet + pack [scale .s -orient horizontal -resolution .1 -from -180 -to 180 -command "set x"] + update +} -body { + .s configure -background red + update + set x +} -cleanup { + destroy .s +} -result {NotSet} + test scale-14a.1 {RoundValueToResolution, RoundIntervalToResolution procedures} -setup { pack [scale .s -orient horizontal] update -- cgit v0.12 From cff021b2dc7bf601b37714ae36bfa4fd510dbd1d Mon Sep 17 00:00:00 2001 From: fvogel Date: Sun, 13 Jan 2019 14:35:13 +0000 Subject: Fix [3003895fff] and [1899040fff] with a different fix, this time it does not resurrect [220665ffff] or duplicates [220265ffff] [779559ffff]. All scale.test tests do pass now. --- generic/tkScale.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/generic/tkScale.c b/generic/tkScale.c index 547f698..979d39d 100644 --- a/generic/tkScale.c +++ b/generic/tkScale.c @@ -1142,17 +1142,8 @@ TkRoundValueToResolution( TkScale *scalePtr, /* Information about scale widget. */ double value) /* Value to round. */ { - double rem, start; - - if (scalePtr->resolution <= 0) { - return value; - } - start = fmod(scalePtr->fromValue, scalePtr->resolution); - rem = fmod(value - start + scalePtr->resolution/2, scalePtr->resolution); - if (rem < 0) { - rem += scalePtr->resolution; - } - return value + scalePtr->resolution/2 - rem; + return TkRoundIntervalToResolution(scalePtr, value - scalePtr->fromValue) + + scalePtr->fromValue; } double @@ -1160,16 +1151,24 @@ TkRoundIntervalToResolution( TkScale *scalePtr, /* Information about scale widget. */ double value) /* Value to round. */ { - double rem; + double rem, rounded, tick; if (scalePtr->resolution <= 0) { return value; } - rem = fmod(value + scalePtr->resolution/2, scalePtr->resolution); + tick = floor(value/scalePtr->resolution); + rounded = scalePtr->resolution * tick; + rem = value - rounded; if (rem < 0) { - rem += scalePtr->resolution; + if (rem <= -scalePtr->resolution/2) { + rounded = (tick - 1.0) * scalePtr->resolution; + } + } else { + if (rem >= scalePtr->resolution/2) { + rounded = (tick + 1.0) * scalePtr->resolution; + } } - return value + scalePtr->resolution/2 - rem; + return rounded; } /* -- cgit v0.12