0

I'm using the Nice Numbers for Graph Labels algorithm (by Andrew S. Glassner), but on some numbers, e.g.: (min: -206.13 max: 729.02), it returns more Ticks than MAX_TICKS:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>

#define MARGIN "          "
#define MARGIN_LEN 10
#define MAX_TICKS 10
#define NVALUES 10
#define WIDTH 70

static double nice(double range, int round)
{
    double exponent;
    double fraction;
    double nicefrac;

    exponent = floor(log10(range));
    fraction = range / pow(10, exponent);
    if (round) {
        if (fraction < 1.5)
            nicefrac = 1.0;
        else
        if (fraction < 3.0)
            nicefrac = 2.0;
        else
        if (fraction < 7.0)
            nicefrac = 5.0;
        else
            nicefrac = 10.0;
    } else {
        if (fraction <= 1.0)
            nicefrac = 1.0;
        else
        if (fraction <= 2.0)
            nicefrac = 2.0;
        else
        if (fraction <= 5.0)
            nicefrac = 5.0;
        else
            nicefrac = 10.0;
    }
    return nicefrac * pow(10, exponent);
}

static double calc(double range, int ticks)
{
    double tick;

    range = nice(fabs(range), 0);
    tick = nice(range / (ticks - 1), 1);
    return tick;
}

int main(void)
{
    double value[NVALUES], min, max, tick, sum;
    int ticks, width, wtick, wzero, wcurr;
    int i, j;

    srand(time(NULL));
    for (i = 0; i < NVALUES; i++) {
        value[i] = (rand() % 100000) / 100.0 - 250.0;
    }
    min = max = value[0];
    for (i = 0; i < NVALUES; i++) {
        if (value[i] < min) min = value[i];
        if (value[i] > max) max = value[i];
    }
    printf("MIN = %.2f | MAX = %.2f\n", min, max);
    if (min > 0.0) min = 0.0;
    if (max < 0.0) max = 0.0;
    tick = calc(max - min, MAX_TICKS);
    min = floor(min / tick) * tick;
    max = ceil(max / tick) * tick;
    ticks = (int)((max - min) / tick);
    width = (int)(floor(WIDTH / ticks) * ticks);
    wtick = (int)(width / ticks);
    wzero = (int)ceil(((0.0 - min) / (max - min)) * width);
    printf("MIN = %.2f | MAX = %.2f | TICK = %.2f | TICKS = %d\n", min, max, tick, ticks);
    printf("\n" MARGIN);
    sum = min;
    for (i = 0; i <= ticks; i++) {
        printf("%-*.*f", wtick, tick == (int)tick ? 0 : 2, sum);
        sum += tick;
    }
    printf("\n" MARGIN);
    for (i = 0; i <= ticks; i++) {
        printf("%-*s", wtick, "|");
    }
    printf("\n");
    for (i = 0; i < NVALUES; i++) {
        printf("%*.2f ", MARGIN_LEN - 1, value[i]);
        wcurr = (int)round(((value[i] - min) / (max - min)) * width);
        if (value[i] < 0.0) {
            for (j = 0; j < wzero; j++)
                printf("%c", j < wcurr ? ' ' : '*');
        } else {
            for (j = 0; j < wcurr; j++)
                printf("%c", j < wzero ? ' ' : '*');
        }   
        printf("\n");
    }
    return 0;
}

In this example MAX_TICKS = 10 but I get 11 ticks:

MIN = -206.13 | MAX = 729.02
MIN = -300.00 | MAX = 800.00 | TICK = 100.00 | TICKS = 11

          -300  -200  -100  0     100   200   300   400   500   600   700   800   
          |     |     |     |     |     |     |     |     |     |     |     |     
   729.02                   ********************************************
   701.07                   ******************************************
   -84.62              *****
   462.44                   ****************************
   387.91                   ***********************
   683.73                   *****************************************
   631.30                   **************************************
   146.09                   *********
   663.66                   ****************************************
  -206.13       ************

The desired output when MAX_TICKS = 10 (7 ticks):

enter image description here

How to adjust the algorithm in order to get <= MAX_TICKS?

Community
  • 1
  • 1
David Ranieri
  • 39,972
  • 7
  • 52
  • 94

1 Answers1

1

For min and max specific values -206.13 and 729.02 range is calculated to be 1000.00 and tick to be 100.00. But after that min and max are set to -300 and 800 which expands range to 1100.00. And you got 11 insted of 10 ticks. Solution will be to calculate tick again when min and max are set:

...
if (min > 0.0) min = 0.0;
if (max < 0.0) max = 0.0;
tick = calc(max - min, MAX_TICKS);

min = floor(min / tick) * tick;
max = ceil(max / tick) * tick;

if ( ((max - min) / tick) > MAX_TICKS) {
    tick = calc(max - min, MAX_TICKS);
    min = floor(min / tick) * tick;
    max = ceil(max / tick) * tick;
}

ticks = (int)((max - min) / tick);
...
Anto Jurković
  • 11,188
  • 2
  • 29
  • 42
  • Thank you Anto, that was my first try, it works as expected but the scale was something like `-300 -100 100 300 500 700 `, as you can see, `0` is not in the scale, for now, I'm using [this](https://ideone.com/MtYmCC) as solution (a recursive call while `> MAX_TICKS`. Thank you again ;) – David Ranieri Feb 10 '15 at 14:04
  • I updated the solution because `min` and `max` has to be recalculated too in those special cases and ticks will be `-400, -200, 0, ..., 800` – Anto Jurković Feb 10 '15 at 14:06