From 31b66a6295cd71b19bc085fd8c1b6b660c327ad3 Mon Sep 17 00:00:00 2001 From: jal_frezie Date: Wed, 20 Feb 2019 11:51:53 +0000 Subject: Eliminate trailing zeros from scale tick values. This is done by calculating a separate format string for these values, rather than using the same format string as for the current value. The basis is the same, i.e., enough digits are displayed to distinguish any pair of adjacent tick values, and all tick values have the same number of decimals so some may still have trailing zeros. Code for laying out vertical scales has been adjusted to take account of the fact that the tick column may now be narrower than the value column. --- generic/tkScale.c | 148 ++++++++++++++++++++++++++++++++++------------------- generic/tkScale.h | 4 +- unix/tkUnixScale.c | 28 +++++----- 3 files changed, 114 insertions(+), 66 deletions(-) diff --git a/generic/tkScale.c b/generic/tkScale.c index 29f8722..455c656 100644 --- a/generic/tkScale.c +++ b/generic/tkScale.c @@ -157,7 +157,7 @@ enum command { * Forward declarations for procedures defined later in this file: */ -static void ComputeFormat(TkScale *scalePtr); +static void ComputeFormat(TkScale *scalePtr, int forTicks); static void ComputeScaleGeometry(TkScale *scalePtr); static int ConfigureScale(Tcl_Interp *interp, TkScale *scalePtr, int objc, Tcl_Obj *const objv[]); @@ -430,7 +430,7 @@ ScaleWidgetObjCmd( } value = TkScalePixelToValue(scalePtr, x, y); } - Tcl_SetObjResult(interp, Tcl_ObjPrintf(scalePtr->format, value)); + Tcl_SetObjResult(interp, Tcl_ObjPrintf(scalePtr->valueFormat, value)); break; } case COMMAND_IDENTIFY: { @@ -636,7 +636,8 @@ ConfigureScale( scalePtr->tickInterval = -scalePtr->tickInterval; } - ComputeFormat(scalePtr); + ComputeFormat(scalePtr, 0); + ComputeFormat(scalePtr, 1); scalePtr->labelLength = scalePtr->label ? (int)strlen(scalePtr->label) : 0; @@ -764,22 +765,22 @@ ScaleWorldChanged( * * ComputeFormat -- * - * This procedure is invoked to recompute the "format" field of a scale's - * widget record, which determines how the value of the scale is - * converted to a string. + * This procedure is invoked to recompute the "valueFormat" or + * "tickFormat" field of a scale's widget record, which determines how + * the value of the scale or one of its ticks is converted to a string. * * Results: * None. * - * Side effects: - * The format field of scalePtr is modified. + * Side effects: The valueFormat or tickFormat field of scalePtr is modified. * *---------------------------------------------------------------------- */ static void ComputeFormat( - TkScale *scalePtr) /* Information about scale widget. */ + TkScale *scalePtr, /* Information about scale widget. */ + int forTicks) /* Do for ticks rather than value */ { double maxValue, x; int mostSigDigit, numDigits, leastSigDigit, afterDecimal; @@ -800,46 +801,60 @@ ComputeFormat( } mostSigDigit = (int) floor(log10(maxValue)); - /* - * If the number of significant digits wasn't specified explicitly, - * compute it. It's the difference between the most significant digit - * needed to represent any number on the scale and the most significant - * digit of the smallest difference between numbers on the scale. In other - * words, display enough digits so that at least one digit will be - * different between any two adjacent positions of the scale. - */ - - numDigits = scalePtr->digits; - if (numDigits > TCL_MAX_PREC) { - numDigits = 0; - } - if (numDigits <= 0) { - if (scalePtr->resolution > 0) { - /* - * A resolution was specified for the scale, so just use it. - */ + if (forTicks) { + /* + * Display only enough digits to ensure adjacent ticks have different + * values + */ - leastSigDigit = (int) floor(log10(scalePtr->resolution)); + if (scalePtr->tickInterval > 0) { + numDigits = 1 + mostSigDigit - (int) floor(log10(scalePtr->tickInterval)); } else { - /* - * No resolution was specified, so compute the difference in value - * between adjacent pixels and use it for the least significant - * digit. - */ + numDigits = 1; + } + } else { + /* + * If the number of significant digits wasn't specified explicitly, + * compute it. It's the difference between the most significant digit + * needed to represent any number on the scale and the most + * significant digit of the smallest difference between numbers on the + * scale. In other words, display enough digits so that at least one + * digit will be different between any two adjacent positions of the + * scale. + */ - x = fabs(scalePtr->fromValue - scalePtr->toValue); - if (scalePtr->length > 0) { - x /= scalePtr->length; - } - if (x > 0){ - leastSigDigit = (int) floor(log10(x)); + numDigits = scalePtr->digits; + if (numDigits > TCL_MAX_PREC) { + numDigits = 0; + } + if (numDigits <= 0) { + if (scalePtr->resolution > 0) { + /* + * A resolution was specified for the scale, so just use it. + */ + + leastSigDigit = (int) floor(log10(scalePtr->resolution)); } else { - leastSigDigit = 0; + /* + * No resolution was specified, so compute the difference in + * value between adjacent pixels and use it for the least + * significant digit. + */ + + x = fabs(scalePtr->fromValue - scalePtr->toValue); + if (scalePtr->length > 0) { + x /= scalePtr->length; + } + if (x > 0){ + leastSigDigit = (int) floor(log10(x)); + } else { + leastSigDigit = 0; + } + } + numDigits = mostSigDigit - leastSigDigit + 1; + if (numDigits < 1) { + numDigits = 1; } - } - numDigits = mostSigDigit - leastSigDigit + 1; - if (numDigits < 1) { - numDigits = 1; } } @@ -863,10 +878,18 @@ ComputeFormat( if (mostSigDigit < 0) { fDigits++; /* Zero to left of decimal point. */ } - if (fDigits <= eDigits) { - sprintf(scalePtr->format, "%%.%df", afterDecimal); + if (forTicks) { + if (fDigits <= eDigits) { + sprintf(scalePtr->tickFormat, "%%.%df", afterDecimal); + } else { + sprintf(scalePtr->tickFormat, "%%.%de", numDigits-1); + } } else { - sprintf(scalePtr->format, "%%.%de", numDigits-1); + if (fDigits <= eDigits) { + sprintf(scalePtr->valueFormat, "%%.%df", afterDecimal); + } else { + sprintf(scalePtr->valueFormat, "%%.%de", numDigits-1); + } } } @@ -894,7 +917,7 @@ ComputeScaleGeometry( register TkScale *scalePtr) /* Information about widget. */ { char valueString[TCL_DOUBLE_SPACE]; - int tmp, valuePixels, x, y, extraSpace; + int tmp, valuePixels, tickPixels, x, y, extraSpace; Tk_FontMetrics fm; Tk_GetFontMetrics(scalePtr->tkfont, &fm); @@ -940,13 +963,13 @@ ComputeScaleGeometry( * whichever length is longer. */ - if (snprintf(valueString, TCL_DOUBLE_SPACE, scalePtr->format, + if (snprintf(valueString, TCL_DOUBLE_SPACE, scalePtr->valueFormat, scalePtr->fromValue) < 0) { valueString[TCL_DOUBLE_SPACE - 1] = '\0'; } valuePixels = Tk_TextWidth(scalePtr->tkfont, valueString, -1); - if (snprintf(valueString, TCL_DOUBLE_SPACE, scalePtr->format, + if (snprintf(valueString, TCL_DOUBLE_SPACE, scalePtr->valueFormat, scalePtr->toValue) < 0) { valueString[TCL_DOUBLE_SPACE - 1] = '\0'; } @@ -956,18 +979,37 @@ ComputeScaleGeometry( } /* + * Now do the same thing for the tick values + */ + + if (snprintf(valueString, TCL_DOUBLE_SPACE, scalePtr->tickFormat, + scalePtr->fromValue) < 0) { + valueString[TCL_DOUBLE_SPACE - 1] = '\0'; + } + tickPixels = Tk_TextWidth(scalePtr->tkfont, valueString, -1); + + if (snprintf(valueString, TCL_DOUBLE_SPACE, scalePtr->tickFormat, + scalePtr->toValue) < 0) { + valueString[TCL_DOUBLE_SPACE - 1] = '\0'; + } + tmp = Tk_TextWidth(scalePtr->tkfont, valueString, -1); + if (tickPixels < tmp) { + tickPixels = tmp; + } + + /* * Assign x-locations to the elements of the scale, working from left to * right. */ x = scalePtr->inset; if ((scalePtr->tickInterval != 0) && (scalePtr->showValue)) { - scalePtr->vertTickRightX = x + SPACING + valuePixels; + scalePtr->vertTickRightX = x + SPACING + tickPixels; scalePtr->vertValueRightX = scalePtr->vertTickRightX + valuePixels + fm.ascent/2; x = scalePtr->vertValueRightX + SPACING; } else if (scalePtr->tickInterval != 0) { - scalePtr->vertTickRightX = x + SPACING + valuePixels; + scalePtr->vertTickRightX = x + SPACING + tickPixels; scalePtr->vertValueRightX = scalePtr->vertTickRightX; x = scalePtr->vertTickRightX + SPACING; } else if (scalePtr->showValue) { @@ -1350,7 +1392,7 @@ ScaleSetVariable( if (scalePtr->varNamePtr != NULL) { char string[TCL_DOUBLE_SPACE]; - if (snprintf(string, TCL_DOUBLE_SPACE, scalePtr->format, + if (snprintf(string, TCL_DOUBLE_SPACE, scalePtr->valueFormat, scalePtr->value) < 0) { string[TCL_DOUBLE_SPACE - 1] = '\0'; } diff --git a/generic/tkScale.h b/generic/tkScale.h index d44e65b..4250b06 100644 --- a/generic/tkScale.h +++ b/generic/tkScale.h @@ -73,8 +73,10 @@ typedef struct TkScale { * values. 0 means we get to choose the number * based on resolution and/or the range of the * scale. */ - char format[16]; /* Sprintf conversion specifier computed from + char valueFormat[16]; /* Sprintf conversion specifier computed from * digits and other information. */ + char tickFormat[16]; /* Sprintf conversion specifier computed from + * tick interval. */ double bigIncrement; /* Amount to use for large increments to scale * value. (0 means we pick a value). */ char *command; /* Command prefix to use when invoking Tcl diff --git a/unix/tkUnixScale.c b/unix/tkUnixScale.c index 2009288..f5e3fc2 100644 --- a/unix/tkUnixScale.c +++ b/unix/tkUnixScale.c @@ -24,11 +24,13 @@ static void DisplayHorizontalScale(TkScale *scalePtr, Drawable drawable, XRectangle *drawnAreaPtr); static void DisplayHorizontalValue(TkScale *scalePtr, - Drawable drawable, double value, int top); + Drawable drawable, double value, + int top, char* format); static void DisplayVerticalScale(TkScale *scalePtr, Drawable drawable, XRectangle *drawnAreaPtr); static void DisplayVerticalValue(TkScale *scalePtr, - Drawable drawable, double value, int rightEdge); + Drawable drawable, double value, + int rightEdge, char* format); /* *---------------------------------------------------------------------- @@ -165,7 +167,7 @@ DisplayVerticalScale( } } DisplayVerticalValue(scalePtr, drawable, tickValue, - scalePtr->vertTickRightX); + scalePtr->vertTickRightX, scalePtr->tickFormat); } } } @@ -176,7 +178,7 @@ DisplayVerticalScale( if (scalePtr->showValue) { DisplayVerticalValue(scalePtr, drawable, scalePtr->value, - scalePtr->vertValueRightX); + scalePtr->vertValueRightX, scalePtr->valueFormat); } /* @@ -261,8 +263,9 @@ DisplayVerticalValue( double value, /* Y-coordinate of number to display, * specified in application coords, not in * pixels (we'll compute pixels). */ - int rightEdge) /* X-coordinate of right edge of text, + int rightEdge, /* X-coordinate of right edge of text, * specified in pixels. */ + char* format) /* Format string to use for the value */ { register Tk_Window tkwin = scalePtr->tkwin; int y, width, length; @@ -271,7 +274,7 @@ DisplayVerticalValue( Tk_GetFontMetrics(scalePtr->tkfont, &fm); y = TkScaleValueToPixel(scalePtr, value) + fm.ascent/2; - if (snprintf(valueString, TCL_DOUBLE_SPACE, scalePtr->format, value) < 0) { + if (snprintf(valueString, TCL_DOUBLE_SPACE, format, value) < 0) { valueString[TCL_DOUBLE_SPACE - 1] = '\0'; } length = (int) strlen(valueString); @@ -358,7 +361,7 @@ DisplayHorizontalScale( ticks = fabs((scalePtr->toValue - scalePtr->fromValue) / tickInterval); - if (snprintf(valueString, TCL_DOUBLE_SPACE, scalePtr->format, + if (snprintf(valueString, TCL_DOUBLE_SPACE, scalePtr->tickFormat, scalePtr->fromValue) < 0) { valueString[TCL_DOUBLE_SPACE - 1] = '\0'; } @@ -385,7 +388,7 @@ DisplayHorizontalScale( } } DisplayHorizontalValue(scalePtr, drawable, tickValue, - scalePtr->horizTickY); + scalePtr->horizTickY, scalePtr->tickFormat); } } } @@ -396,7 +399,7 @@ DisplayHorizontalScale( if (scalePtr->showValue) { DisplayHorizontalValue(scalePtr, drawable, scalePtr->value, - scalePtr->horizValueY); + scalePtr->horizValueY, scalePtr->valueFormat); } /* @@ -482,8 +485,9 @@ DisplayHorizontalValue( double value, /* X-coordinate of number to display, * specified in application coords, not in * pixels (we'll compute pixels). */ - int top) /* Y-coordinate of top edge of text, specified + int top, /* Y-coordinate of top edge of text, specified * in pixels. */ + char* format) /* Format string to use for the value */ { register Tk_Window tkwin = scalePtr->tkwin; int x, y, length, width; @@ -493,7 +497,7 @@ DisplayHorizontalValue( x = TkScaleValueToPixel(scalePtr, value); Tk_GetFontMetrics(scalePtr->tkfont, &fm); y = top + fm.ascent; - if (snprintf(valueString, TCL_DOUBLE_SPACE, scalePtr->format, value) < 0) { + if (snprintf(valueString, TCL_DOUBLE_SPACE, format, value) < 0) { valueString[TCL_DOUBLE_SPACE - 1] = '\0'; } length = (int) strlen(valueString); @@ -562,7 +566,7 @@ TkpDisplayScale( Tcl_Preserve(scalePtr); if ((scalePtr->flags & INVOKE_COMMAND) && (scalePtr->command != NULL)) { Tcl_Preserve(interp); - if (snprintf(string, TCL_DOUBLE_SPACE, scalePtr->format, + if (snprintf(string, TCL_DOUBLE_SPACE, scalePtr->valueFormat, scalePtr->value) < 0) { string[TCL_DOUBLE_SPACE - 1] = '\0'; } -- cgit v0.12 From 7883dcdedb3af8454956e7599b139e350dbddfdf Mon Sep 17 00:00:00 2001 From: jaspert Date: Thu, 28 Feb 2019 15:52:15 +0000 Subject: Added a procedure to increase the number of digits in the tick values until the maximum rounding error goes below a certain value, currently 0.2x the tick interval. Also fixed it to work with scales from larger to smaller values. --- generic/tkScale.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/generic/tkScale.c b/generic/tkScale.c index 455c656..95cc399 100644 --- a/generic/tkScale.c +++ b/generic/tkScale.c @@ -759,6 +759,46 @@ ScaleWorldChanged( TkEventuallyRedrawScale(scalePtr, REDRAW_ALL); } + + + /* + *---------------------------------------------------------------------- + * + * MaxTickRoundingError -- + * + * Given the separation between values that can be displayed on ticks, + * this calculates the maximum magnitude of error for the displayed + * value. Tries to be clever by working out the increment in error + * between ticks rather than testing all of them, so may overestimate + * error if it is greater than 0.25 x the value separation. + * + * Results: + * Maximum error magnitude of tick numbers. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static double +MaxTickRoundingError( + TkScale *scalePtr, /* Information about scale widget. */ + double tickResolution) /* Separation between displayable values. */ +{ + double tickPosn, firstTickError, lastTickError, intervalError; + int tickCount; + + tickPosn = scalePtr->fromValue/tickResolution; + firstTickError = tickPosn - round(tickPosn); + tickPosn = scalePtr->tickInterval/tickResolution; + intervalError = tickPosn - round(tickPosn); + tickCount = (int)((scalePtr->toValue-scalePtr->fromValue) / + scalePtr->tickInterval); // not including first + lastTickError = fmin(fabs(firstTickError + tickCount*intervalError), 0.5); + return fmax(fabs(firstTickError),lastTickError)*tickResolution; +} + /* *---------------------------------------------------------------------- @@ -782,7 +822,7 @@ ComputeFormat( TkScale *scalePtr, /* Information about scale widget. */ int forTicks) /* Do for ticks rather than value */ { - double maxValue, x; + double maxValue, x, tickResolution; int mostSigDigit, numDigits, leastSigDigit, afterDecimal; int eDigits, fDigits; @@ -807,8 +847,16 @@ ComputeFormat( * values */ - if (scalePtr->tickInterval > 0) { - numDigits = 1 + mostSigDigit - (int) floor(log10(scalePtr->tickInterval)); + if (scalePtr->tickInterval != 0) { + leastSigDigit = (int) floor(log10(fabs(scalePtr->tickInterval))); + /* + * Now add more digits until max error is less than 0.2 intervals + */ + while (MaxTickRoundingError(scalePtr, pow(10,leastSigDigit)) > + fabs(0.2*scalePtr->tickInterval)) + --leastSigDigit; + + numDigits = 1 + mostSigDigit - leastSigDigit; } else { numDigits = 1; } -- cgit v0.12 From d7c4676109faa97abf627a58d578ba2c0a40c978 Mon Sep 17 00:00:00 2001 From: jaspert Date: Thu, 28 Feb 2019 16:37:23 +0000 Subject: Trimmed an unused variable --- generic/tkScale.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generic/tkScale.c b/generic/tkScale.c index 95cc399..5ed6618 100644 --- a/generic/tkScale.c +++ b/generic/tkScale.c @@ -822,7 +822,7 @@ ComputeFormat( TkScale *scalePtr, /* Information about scale widget. */ int forTicks) /* Do for ticks rather than value */ { - double maxValue, x, tickResolution; + double maxValue, x; int mostSigDigit, numDigits, leastSigDigit, afterDecimal; int eDigits, fDigits; -- cgit v0.12 From 7f0376c51034471e24aa2b44dd0b1aec38ef03b0 Mon Sep 17 00:00:00 2001 From: fvogel Date: Mon, 4 Mar 2019 20:36:04 +0000 Subject: Make the branch build with MSVC by replacing calls to C99 functions round(), fmin() and fmax() by equivalent macros. --- generic/tkScale.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/generic/tkScale.c b/generic/tkScale.c index 5ed6618..0d9bc10 100644 --- a/generic/tkScale.c +++ b/generic/tkScale.c @@ -185,6 +185,10 @@ static const Tk_ClassProcs scaleClass = { NULL, /* createProc */ NULL /* modalProc */ }; + +#define ROUND(d) ((int) floor((d) + 0.5)) +#define MIN(a, b) ((a) < (b)? (a): (b)) +#define MAX(a, b) ((a) > (b)? (a): (b)) /* *-------------------------------------------------------------- @@ -790,13 +794,13 @@ MaxTickRoundingError( int tickCount; tickPosn = scalePtr->fromValue/tickResolution; - firstTickError = tickPosn - round(tickPosn); + firstTickError = tickPosn - ROUND(tickPosn); tickPosn = scalePtr->tickInterval/tickResolution; - intervalError = tickPosn - round(tickPosn); + intervalError = tickPosn - ROUND(tickPosn); tickCount = (int)((scalePtr->toValue-scalePtr->fromValue) / scalePtr->tickInterval); // not including first - lastTickError = fmin(fabs(firstTickError + tickCount*intervalError), 0.5); - return fmax(fabs(firstTickError),lastTickError)*tickResolution; + lastTickError = MIN(fabs(firstTickError + tickCount*intervalError), 0.5); + return MAX(fabs(firstTickError),lastTickError)*tickResolution; } -- cgit v0.12 From 4f7024c77a1a45eff1194e3148e9cbc26146be16 Mon Sep 17 00:00:00 2001 From: jaspert Date: Fri, 8 Mar 2019 14:58:54 +0000 Subject: Changed scale man page to reflect how tick values are now displayed --- doc/scale.n | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/scale.n b/doc/scale.n index 6b960ce..b1f1981 100644 --- a/doc/scale.n +++ b/doc/scale.n @@ -55,7 +55,7 @@ it is the scale's width. .OP \-resolution resolution Resolution A real value specifying the resolution for the scale. If this value is greater than zero then the scale's value will always be -rounded to an even multiple of this value, as will tick marks and +rounded to an even multiple of this value, as will the endpoints of the scale. If the value is less than zero then no rounding occurs. Defaults to 1 (i.e., the value will be integral). .OP \-showvalue showValue ShowValue @@ -78,7 +78,7 @@ specified by the \fB\-activebackground\fR option. .OP \-tickinterval tickInterval TickInterval Must be a real value. Determines the spacing between numerical -tick marks displayed below or to the left of the slider. +tick marks displayed below or to the left of the slider. The values will all be displayed with the same number of decimal places, which will be enough to ensure they are all accurate to within 20% of a tick interval. If 0, no tick marks will be displayed. .OP \-to to To Specifies a real value corresponding -- cgit v0.12 From 5d23499fac449980d06f4ca3535f02a27a4052a1 Mon Sep 17 00:00:00 2001 From: dkf Date: Sun, 24 Mar 2019 12:31:12 +0000 Subject: Minor tinkering (style, efficiency, etc.) --- generic/tkScale.c | 116 ++++++++++++++++++++++++++++++++++++++--------------- unix/tkUnixScale.c | 67 ++++++++++++++++--------------- 2 files changed, 118 insertions(+), 65 deletions(-) diff --git a/generic/tkScale.c b/generic/tkScale.c index 0d9bc10..8118436 100644 --- a/generic/tkScale.c +++ b/generic/tkScale.c @@ -162,6 +162,8 @@ static void ComputeScaleGeometry(TkScale *scalePtr); static int ConfigureScale(Tcl_Interp *interp, TkScale *scalePtr, int objc, Tcl_Obj *const objv[]); static void DestroyScale(char *memPtr); +static double MaxTickRoundingError(TkScale *scalePtr, + double tickResolution); static void ScaleCmdDeletedProc(ClientData clientData); static void ScaleEventProc(ClientData clientData, XEvent *eventPtr); @@ -182,13 +184,50 @@ static void ScaleSetVariable(TkScale *scalePtr); static const Tk_ClassProcs scaleClass = { sizeof(Tk_ClassProcs), /* size */ ScaleWorldChanged, /* worldChangedProc */ - NULL, /* createProc */ - NULL /* modalProc */ + NULL, /* createProc */ + NULL /* modalProc */ }; + +/* + *-------------------------------------------------------------- + * + * ScaleDigit, ScaleMax, ScaleMin, ScaleRound -- + * + * Simple math helper functions, designed to be automatically inlined by + * the compiler most of the time. + * + *-------------------------------------------------------------- + */ -#define ROUND(d) ((int) floor((d) + 0.5)) -#define MIN(a, b) ((a) < (b)? (a): (b)) -#define MAX(a, b) ((a) > (b)? (a): (b)) +static inline int +ScaleDigit( + double value) +{ + return (int) floor(log10(fabs(value))); +} + +static inline double +ScaleMax( + double a, + double b) +{ + return (a > b) ? a : b; +} + +static inline double +ScaleMin( + double a, + double b) +{ + return (a < b) ? a : b; +} + +static inline int +ScaleRound( + double value) +{ + return (int) floor(value + 0.5); +} /* *-------------------------------------------------------------- @@ -763,7 +802,6 @@ ScaleWorldChanged( TkEventuallyRedrawScale(scalePtr, REDRAW_ALL); } - /* *---------------------------------------------------------------------- @@ -784,8 +822,8 @@ ScaleWorldChanged( * *---------------------------------------------------------------------- */ - -static double + +static double MaxTickRoundingError( TkScale *scalePtr, /* Information about scale widget. */ double tickResolution) /* Separation between displayable values. */ @@ -793,16 +831,27 @@ MaxTickRoundingError( double tickPosn, firstTickError, lastTickError, intervalError; int tickCount; - tickPosn = scalePtr->fromValue/tickResolution; - firstTickError = tickPosn - ROUND(tickPosn); - tickPosn = scalePtr->tickInterval/tickResolution; - intervalError = tickPosn - ROUND(tickPosn); - tickCount = (int)((scalePtr->toValue-scalePtr->fromValue) / - scalePtr->tickInterval); // not including first - lastTickError = MIN(fabs(firstTickError + tickCount*intervalError), 0.5); - return MAX(fabs(firstTickError),lastTickError)*tickResolution; -} + /* + * Compute the error for each tick-related measure. + */ + tickPosn = scalePtr->fromValue / tickResolution; + firstTickError = tickPosn - ScaleRound(tickPosn); + + tickPosn = scalePtr->tickInterval / tickResolution; + intervalError = tickPosn - ScaleRound(tickPosn); + + tickCount = (int) ((scalePtr->toValue - scalePtr->fromValue) / + scalePtr->tickInterval); /* not including first */ + lastTickError = ScaleMin(0.5, + fabs(firstTickError + tickCount * intervalError)); + + /* + * Compute the maximum cumulative rounding error. + */ + + return ScaleMax(fabs(firstTickError), lastTickError) * tickResolution; +} /* *---------------------------------------------------------------------- @@ -843,23 +892,25 @@ ComputeFormat( if (maxValue == 0) { maxValue = 1; } - mostSigDigit = (int) floor(log10(maxValue)); + mostSigDigit = ScaleDigit(maxValue); if (forTicks) { /* * Display only enough digits to ensure adjacent ticks have different - * values + * values. */ if (scalePtr->tickInterval != 0) { - leastSigDigit = (int) floor(log10(fabs(scalePtr->tickInterval))); + leastSigDigit = ScaleDigit(scalePtr->tickInterval); + /* * Now add more digits until max error is less than 0.2 intervals */ - while (MaxTickRoundingError(scalePtr, pow(10,leastSigDigit)) > - fabs(0.2*scalePtr->tickInterval)) - --leastSigDigit; + while (MaxTickRoundingError(scalePtr, pow(10, leastSigDigit)) + > fabs(0.2 * scalePtr->tickInterval)) { + --leastSigDigit; + } numDigits = 1 + mostSigDigit - leastSigDigit; } else { numDigits = 1; @@ -884,21 +935,21 @@ ComputeFormat( /* * A resolution was specified for the scale, so just use it. */ - - leastSigDigit = (int) floor(log10(scalePtr->resolution)); + + leastSigDigit = ScaleDigit(scalePtr->resolution); } else { /* * No resolution was specified, so compute the difference in * value between adjacent pixels and use it for the least * significant digit. */ - + x = fabs(scalePtr->fromValue - scalePtr->toValue); if (scalePtr->length > 0) { x /= scalePtr->length; } - if (x > 0){ - leastSigDigit = (int) floor(log10(x)); + if (x > 0) { + leastSigDigit = ScaleDigit(x); } else { leastSigDigit = 0; } @@ -930,17 +981,18 @@ ComputeFormat( if (mostSigDigit < 0) { fDigits++; /* Zero to left of decimal point. */ } + if (forTicks) { if (fDigits <= eDigits) { sprintf(scalePtr->tickFormat, "%%.%df", afterDecimal); } else { - sprintf(scalePtr->tickFormat, "%%.%de", numDigits-1); + sprintf(scalePtr->tickFormat, "%%.%de", numDigits - 1); } } else { if (fDigits <= eDigits) { sprintf(scalePtr->valueFormat, "%%.%df", afterDecimal); } else { - sprintf(scalePtr->valueFormat, "%%.%de", numDigits-1); + sprintf(scalePtr->valueFormat, "%%.%de", numDigits - 1); } } } @@ -1546,8 +1598,8 @@ TkScaleValueToPixel( if (valueRange == 0) { y = 0; } else { - y = (int) ((value - scalePtr->fromValue) * pixelRange - / valueRange + 0.5); + y = ScaleRound((value - scalePtr->fromValue) * pixelRange + / valueRange); if (y < 0) { y = 0; } else if (y > pixelRange) { diff --git a/unix/tkUnixScale.c b/unix/tkUnixScale.c index f5e3fc2..778c010 100644 --- a/unix/tkUnixScale.c +++ b/unix/tkUnixScale.c @@ -24,13 +24,13 @@ static void DisplayHorizontalScale(TkScale *scalePtr, Drawable drawable, XRectangle *drawnAreaPtr); static void DisplayHorizontalValue(TkScale *scalePtr, - Drawable drawable, double value, - int top, char* format); + Drawable drawable, double value, int top, + const char *format); static void DisplayVerticalScale(TkScale *scalePtr, Drawable drawable, XRectangle *drawnAreaPtr); static void DisplayVerticalValue(TkScale *scalePtr, - Drawable drawable, double value, - int rightEdge, char* format); + Drawable drawable, double value, int rightEdge, + const char *format); /* *---------------------------------------------------------------------- @@ -178,7 +178,7 @@ DisplayVerticalScale( if (scalePtr->showValue) { DisplayVerticalValue(scalePtr, drawable, scalePtr->value, - scalePtr->vertValueRightX, scalePtr->valueFormat); + scalePtr->vertValueRightX, scalePtr->valueFormat); } /* @@ -230,8 +230,8 @@ DisplayVerticalScale( Tk_GetFontMetrics(scalePtr->tkfont, &fm); Tk_DrawChars(scalePtr->display, drawable, scalePtr->textGC, scalePtr->tkfont, scalePtr->label, - scalePtr->labelLength, scalePtr->vertLabelX, - scalePtr->inset + (3*fm.ascent)/2); + scalePtr->labelLength, scalePtr->vertLabelX, + scalePtr->inset + (3 * fm.ascent) / 2); } } @@ -265,7 +265,7 @@ DisplayVerticalValue( * pixels (we'll compute pixels). */ int rightEdge, /* X-coordinate of right edge of text, * specified in pixels. */ - char* format) /* Format string to use for the value */ + const char *format) /* Format string to use for the value */ { register Tk_Window tkwin = scalePtr->tkwin; int y, width, length; @@ -275,7 +275,7 @@ DisplayVerticalValue( Tk_GetFontMetrics(scalePtr->tkfont, &fm); y = TkScaleValueToPixel(scalePtr, value) + fm.ascent/2; if (snprintf(valueString, TCL_DOUBLE_SPACE, format, value) < 0) { - valueString[TCL_DOUBLE_SPACE - 1] = '\0'; + valueString[TCL_DOUBLE_SPACE - 1] = '\0'; } length = (int) strlen(valueString); width = Tk_TextWidth(scalePtr->tkfont, valueString, length); @@ -285,10 +285,10 @@ DisplayVerticalValue( * the window. */ - if ((y - fm.ascent) < (scalePtr->inset + SPACING)) { + if (y - fm.ascent < scalePtr->inset + SPACING) { y = scalePtr->inset + SPACING + fm.ascent; } - if ((y + fm.descent) > (Tk_Height(tkwin) - scalePtr->inset - SPACING)) { + if (y + fm.descent > Tk_Height(tkwin) - scalePtr->inset - SPACING) { y = Tk_Height(tkwin) - scalePtr->inset - SPACING - fm.descent; } Tk_DrawChars(scalePtr->display, drawable, scalePtr->textGC, @@ -327,7 +327,7 @@ DisplayHorizontalScale( { register Tk_Window tkwin = scalePtr->tkwin; int x, y, width, height, shadowWidth; - double tickValue, tickInterval = scalePtr->tickInterval; + double tickInterval = scalePtr->tickInterval; Tk_3DBorder sliderBorder; /* @@ -351,7 +351,7 @@ DisplayHorizontalScale( if (tickInterval != 0) { char valueString[TCL_DOUBLE_SPACE]; - double ticks, maxTicks; + double ticks, maxTicks, tickValue; /* * Ensure that we will only draw enough of the tick values such @@ -361,17 +361,17 @@ DisplayHorizontalScale( ticks = fabs((scalePtr->toValue - scalePtr->fromValue) / tickInterval); - if (snprintf(valueString, TCL_DOUBLE_SPACE, scalePtr->tickFormat, - scalePtr->fromValue) < 0) { - valueString[TCL_DOUBLE_SPACE - 1] = '\0'; - } + if (snprintf(valueString, TCL_DOUBLE_SPACE, scalePtr->tickFormat, + scalePtr->fromValue) < 0) { + valueString[TCL_DOUBLE_SPACE - 1] = '\0'; + } maxTicks = (double) Tk_Width(tkwin) / (double) Tk_TextWidth(scalePtr->tkfont, valueString, -1); if (ticks > maxTicks) { - tickInterval *= (ticks / maxTicks); + tickInterval *= ticks / maxTicks; } - for (tickValue = scalePtr->fromValue; ; - tickValue += tickInterval) { + tickValue = scalePtr->fromValue; + while (1) { /* * The TkRoundValueToResolution call gets rid of accumulated * round-off errors, if any. @@ -389,6 +389,7 @@ DisplayHorizontalScale( } DisplayHorizontalValue(scalePtr, drawable, tickValue, scalePtr->horizTickY, scalePtr->tickFormat); + tickValue += tickInterval; } } } @@ -452,8 +453,8 @@ DisplayHorizontalScale( Tk_GetFontMetrics(scalePtr->tkfont, &fm); Tk_DrawChars(scalePtr->display, drawable, scalePtr->textGC, scalePtr->tkfont, scalePtr->label, - scalePtr->labelLength, scalePtr->inset + fm.ascent/2, - scalePtr->horizLabelY + fm.ascent); + scalePtr->labelLength, scalePtr->inset + fm.ascent/2, + scalePtr->horizLabelY + fm.ascent); } } @@ -487,7 +488,7 @@ DisplayHorizontalValue( * pixels (we'll compute pixels). */ int top, /* Y-coordinate of top edge of text, specified * in pixels. */ - char* format) /* Format string to use for the value */ + const char *format) /* Format string to use for the value */ { register Tk_Window tkwin = scalePtr->tkwin; int x, y, length, width; @@ -498,7 +499,7 @@ DisplayHorizontalValue( Tk_GetFontMetrics(scalePtr->tkfont, &fm); y = top + fm.ascent; if (snprintf(valueString, TCL_DOUBLE_SPACE, format, value) < 0) { - valueString[TCL_DOUBLE_SPACE - 1] = '\0'; + valueString[TCL_DOUBLE_SPACE - 1] = '\0'; } length = (int) strlen(valueString); width = Tk_TextWidth(scalePtr->tkfont, valueString, length); @@ -508,8 +509,8 @@ DisplayHorizontalValue( * the window. */ - x -= (width)/2; - if (x < (scalePtr->inset + SPACING)) { + x -= width / 2; + if (x < scalePtr->inset + SPACING) { x = scalePtr->inset + SPACING; } @@ -566,10 +567,10 @@ TkpDisplayScale( Tcl_Preserve(scalePtr); if ((scalePtr->flags & INVOKE_COMMAND) && (scalePtr->command != NULL)) { Tcl_Preserve(interp); - if (snprintf(string, TCL_DOUBLE_SPACE, scalePtr->valueFormat, - scalePtr->value) < 0) { - string[TCL_DOUBLE_SPACE - 1] = '\0'; - } + if (snprintf(string, TCL_DOUBLE_SPACE, scalePtr->valueFormat, + scalePtr->value) < 0) { + string[TCL_DOUBLE_SPACE - 1] = '\0'; + } Tcl_DStringInit(&buf); Tcl_DStringAppend(&buf, scalePtr->command, -1); Tcl_DStringAppend(&buf, " ", -1); @@ -638,7 +639,7 @@ TkpDisplayScale( gc = Tk_GCForColor(scalePtr->highlightColorPtr, pixmap); } else { gc = Tk_GCForColor( - Tk_3DBorderColor(scalePtr->highlightBorder), pixmap); + Tk_3DBorderColor(scalePtr->highlightBorder), pixmap); } Tk_DrawFocusHighlight(tkwin, gc, scalePtr->highlightWidth, pixmap); } @@ -700,7 +701,7 @@ TkpScaleElement( if (y < sliderFirst) { return TROUGH1; } - if (y < (sliderFirst+scalePtr->sliderLength)) { + if (y < sliderFirst + scalePtr->sliderLength) { return SLIDER; } return TROUGH2; @@ -720,7 +721,7 @@ TkpScaleElement( if (x < sliderFirst) { return TROUGH1; } - if (x < (sliderFirst+scalePtr->sliderLength)) { + if (x < sliderFirst + scalePtr->sliderLength) { return SLIDER; } return TROUGH2; -- cgit v0.12 From bd919c12a0d87ab9bf1bd86a114ed66898bbfcd1 Mon Sep 17 00:00:00 2001 From: fvogel Date: Sun, 24 Mar 2019 20:20:39 +0000 Subject: Name the TICK_VALUES_DISPLAY_ACCURACY constant --- generic/tkScale.c | 5 +++-- generic/tkScale.h | 8 ++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/generic/tkScale.c b/generic/tkScale.c index 8118436..c5cdaff 100644 --- a/generic/tkScale.c +++ b/generic/tkScale.c @@ -904,11 +904,12 @@ ComputeFormat( leastSigDigit = ScaleDigit(scalePtr->tickInterval); /* - * Now add more digits until max error is less than 0.2 intervals + * Now add more digits until max error is less than + * TICK_VALUES_DISPLAY_ACCURACY intervals */ while (MaxTickRoundingError(scalePtr, pow(10, leastSigDigit)) - > fabs(0.2 * scalePtr->tickInterval)) { + > fabs(TICK_VALUES_DISPLAY_ACCURACY * scalePtr->tickInterval)) { --leastSigDigit; } numDigits = 1 + mostSigDigit - leastSigDigit; diff --git a/generic/tkScale.h b/generic/tkScale.h index 4250b06..e68b786 100644 --- a/generic/tkScale.h +++ b/generic/tkScale.h @@ -217,6 +217,14 @@ typedef struct TkScale { #define SPACING 2 /* + * The tick values are all displayed with the same number of decimal places. + * This number of decimal places is such that the displayed values are all + * accurate to within the following proportion of a tick interval. + */ + +#define TICK_VALUES_DISPLAY_ACCURACY 0.2 + +/* * Declaration of procedures used in the implementation of the scale widget. */ -- cgit v0.12 From 155b295553449458a4eb8bd2a3ab1f12df26cf3b Mon Sep 17 00:00:00 2001 From: fvogel Date: Tue, 26 Mar 2019 07:14:28 +0000 Subject: Change TICK_VALUES_DISPLAY_ACCURACY for the more correct word TICK_VALUES_DISPLAY_PRECISION --- doc/scale.n | 4 +++- generic/tkScale.c | 4 ++-- generic/tkScale.h | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/doc/scale.n b/doc/scale.n index b1f1981..c5f22b4 100644 --- a/doc/scale.n +++ b/doc/scale.n @@ -78,7 +78,9 @@ specified by the \fB\-activebackground\fR option. .OP \-tickinterval tickInterval TickInterval Must be a real value. Determines the spacing between numerical -tick marks displayed below or to the left of the slider. The values will all be displayed with the same number of decimal places, which will be enough to ensure they are all accurate to within 20% of a tick interval. +tick marks displayed below or to the left of the slider. The values will all +be displayed with the same number of decimal places, which will be enough to +ensure they are all precise to within 20% of a tick interval. If 0, no tick marks will be displayed. .OP \-to to To Specifies a real value corresponding diff --git a/generic/tkScale.c b/generic/tkScale.c index c5cdaff..b28b08d 100644 --- a/generic/tkScale.c +++ b/generic/tkScale.c @@ -905,11 +905,11 @@ ComputeFormat( /* * Now add more digits until max error is less than - * TICK_VALUES_DISPLAY_ACCURACY intervals + * TICK_VALUES_DISPLAY_PRECISION intervals */ while (MaxTickRoundingError(scalePtr, pow(10, leastSigDigit)) - > fabs(TICK_VALUES_DISPLAY_ACCURACY * scalePtr->tickInterval)) { + > fabs(TICK_VALUES_DISPLAY_PRECISION * scalePtr->tickInterval)) { --leastSigDigit; } numDigits = 1 + mostSigDigit - leastSigDigit; diff --git a/generic/tkScale.h b/generic/tkScale.h index e68b786..b307aa2 100644 --- a/generic/tkScale.h +++ b/generic/tkScale.h @@ -222,7 +222,7 @@ typedef struct TkScale { * accurate to within the following proportion of a tick interval. */ -#define TICK_VALUES_DISPLAY_ACCURACY 0.2 +#define TICK_VALUES_DISPLAY_PRECISION 0.2 /* * Declaration of procedures used in the implementation of the scale widget. -- cgit v0.12 From dc37e2284b4baccb9a8e7220604bc667d7050129 Mon Sep 17 00:00:00 2001 From: fvogel Date: Thu, 28 Mar 2019 19:34:20 +0000 Subject: Revert previous commit following discussion on tcl-core list. --- doc/scale.n | 4 +--- generic/tkScale.c | 4 ++-- generic/tkScale.h | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/doc/scale.n b/doc/scale.n index c5f22b4..b1f1981 100644 --- a/doc/scale.n +++ b/doc/scale.n @@ -78,9 +78,7 @@ specified by the \fB\-activebackground\fR option. .OP \-tickinterval tickInterval TickInterval Must be a real value. Determines the spacing between numerical -tick marks displayed below or to the left of the slider. The values will all -be displayed with the same number of decimal places, which will be enough to -ensure they are all precise to within 20% of a tick interval. +tick marks displayed below or to the left of the slider. The values will all be displayed with the same number of decimal places, which will be enough to ensure they are all accurate to within 20% of a tick interval. If 0, no tick marks will be displayed. .OP \-to to To Specifies a real value corresponding diff --git a/generic/tkScale.c b/generic/tkScale.c index b28b08d..c5cdaff 100644 --- a/generic/tkScale.c +++ b/generic/tkScale.c @@ -905,11 +905,11 @@ ComputeFormat( /* * Now add more digits until max error is less than - * TICK_VALUES_DISPLAY_PRECISION intervals + * TICK_VALUES_DISPLAY_ACCURACY intervals */ while (MaxTickRoundingError(scalePtr, pow(10, leastSigDigit)) - > fabs(TICK_VALUES_DISPLAY_PRECISION * scalePtr->tickInterval)) { + > fabs(TICK_VALUES_DISPLAY_ACCURACY * scalePtr->tickInterval)) { --leastSigDigit; } numDigits = 1 + mostSigDigit - leastSigDigit; diff --git a/generic/tkScale.h b/generic/tkScale.h index b307aa2..e68b786 100644 --- a/generic/tkScale.h +++ b/generic/tkScale.h @@ -222,7 +222,7 @@ typedef struct TkScale { * accurate to within the following proportion of a tick interval. */ -#define TICK_VALUES_DISPLAY_PRECISION 0.2 +#define TICK_VALUES_DISPLAY_ACCURACY 0.2 /* * Declaration of procedures used in the implementation of the scale widget. -- cgit v0.12