views:

57

answers:

1

I'm working on a charting algorithm that will give me a set n array of y axis values I would use on my graph.

The main problem is that I also want to calculate the number of number of steps to use and also use nice numbers for them. It must be able to take integers and doubles and be able to handle small ranges (under 1) and large ranges (over 10000 etc).

For example, if I was given a range of 0.1 - 0.9, ideally i would have values of 0, 0.2, 0.4, 0.6, 0.8, 1 but if I were given 0.3 to 0.7 I might use 0.3, 0.4, 0.5, 0.6, 0.7

This is what I have so far, it works well with small ranges, but terribly in large ranges, and doesn't give me nice numbers

-(double*)yAxisValues:(double)min (double):max {

    double diff = max - min;
    double divisor = 1.0;

    if (diff > 1) {
        while (diff > 1) {
            diff /= 10;
            divisor *= 10;
        }
    } else {
        while (diff < 1) {
            diff *= 10;
            divisor *= 10;
        }
    }

    double newMin = round(min * divisor) / divisor;
    double newMax = round(max * divisor) / divisor;

    if (newMin > min) {
        newMin -= 1.0/divisor;
    }
    if (newMax < max) {
        newMax += 1.0/divisor;
    }

    int test2 = round((newMax - newMin) * divisor); 
    if (test2 >= 7) {
        while (test2 % 6 != 0 && test2 % 5 != 0 && test2 % 4 != 0 && test2 % 3 != 0) {
            test2++;
            newMax += 1.0/divisor;
        }
    }

    if (test2 % 6 == 0) {
        test2 = 6;
    } else if (test2 % 5 == 0) {
        test2 = 5;
    } else if (test2 % 4 == 0 || test2 == 2) {
        test2 = 4;
    } else if (test2 % 3 == 0) {
        test2 = 3;
    }

    double *values = malloc(sizeof(double) * (test2 + 1));
    for (int i = 0; i < test2 + 1; i++) {
        values[i] = newMin + (newMax - newMin) * i / test2;
    }
    return values;
}

Any suggestions?

A: 

Here's a snippet of code that does something similar, though has a slightly different approach. The "units" refer to what your are plotting on the graph. So if your scale is so that one unit on your graph should be 20 pixels on screen, this function would return how many units each step should be. With that information you can then easily calculate what the axis values are and where to draw them.

- (float)unitsPerMajorGridLine:(float)pixelsPerUnit {
    float amountAtMinimum, orderOfMagnitude, fraction;

    amountAtMinimum = [[self minimumPixelsPerMajorGridLine] floatValue]/pixelsPerUnit;  
    orderOfMagnitude = floor(log10(amountAtMinimum));
    fraction = amountAtMinimum / pow(10.0, orderOfMagnitude);

    if (fraction <= 2) {
        return 2 * pow(10.0, orderOfMagnitude);
    } else if (fraction <= 5) {
        return 5 * pow(10.0, orderOfMagnitude);
    } else {
        return 10 * pow(10.0, orderOfMagnitude);
    }
}
Johan Kool
The thing is that I do not know how many pixels there would be per unit, because I want the axis line to change based on what I am given. For certain sets of data maybe 20 pixels per unit would be good, but for other sets I would need 50 or 0.002 etc.
James
That is not any different from how I use this code. You know the range of the data you are plotting, as well as the size of your graph. To fit the data on you graph, divide the size by the range to get the `pixelsPerUnit` value that you can pass to this method.
Johan Kool
Alright, after looking through it more carefully, reading your instructions and modifying it a little, I've got it working, thanks!
James