Friday, March 4, 2011

Ticking function grapher

Hello everyone,

I am trying to figure out the following problem. I am building Yet another math function grapher, The function is drawn on its predefined x,y range, that's all good.

Now I am working on the background and the ticking of X, Y axes (if any axes are shown).

I worked out the following. I have a fixed width of 250 p The tick gap should be between 12.5 and 50p.

The ticks should indicate either unit or half unit range, by that i mean the following.

x range (-5, 5): one tick = 1

x range (-1, 1): one tick = 0.5 or 0.1 depending on the gap that each of this option would generate.

x range (0.1, 0.3): 0.05

Given a Xrange How would you get the number of ticks between either full or half unit range ?

Or maybe there are other way to approach this type of problems.

From stackoverflow
  • Using deltaX

    if deltax between 2 and 10 half increment if deltax between 10 and 20 unit increment if smaller than 2 we multiply by 10 and test again if larger than 20 we divide Then we get the position of the first unit or half increment on the width using xmin.

    I still need to test this solution.

  • One way to do this would be to "normalise" the difference between the minimum and maximum and do a case distinction on that value. In python:

    delta = maximum - minimum
    factor = 10**math.ceil(math.log(delta,10))  # smallest power of 10 greater than delta
    normalised_delta = delta / factor           # 0.1 <= normalised_delta < 1
    if normalised_delta/5 >= 0.1:
      step_size = 0.1
    elif normalised_delta/5 >= 0.05:
      step_size = 0.05
    elif normalised_delta/20 <= 0.01:
      step_size = 0.01
    step_size = step_size * factor
    

    The above code assumes you want the biggest possible gap. For the smallest you would use the following if:

    if normalised_delta/20 == 0.005:
      step_size = 0.005
    elif normalised_delta/20 <= 0.01:
      step_size = 0.01
    elif normalised_delta/5 >= 0.05:
      step_size = 0.05
    

    Besides the possibility that there are more than one suitable values, there is also the somewhat worrisome possibility that there are none. Take for example the range [0,24] where a gap of 12.5p would give a step size of 1.2 and a gap of 50p would give step size 4.8. There is no "unit" or "half unit" in between. The problem is that the difference between a gap of 12.5p and one of 50p is a factor 4 while the difference between 0.01 and 0.05 is a factor 5. So you will have to widen the range of allowable gaps a bit and adjust the code accordingly.

    Clarification of some of the magic numbers: divisions by 20 and 5 correspond to the number of segments with the minimal and maximal gap size, respectively (ie. 250/12.5 and 250/50). As the normalised delta is in the range [0.1,1), you get that dividing it by 20 and 5 gives you [0.005,0.05) and [0.02,0.2), respectively. These ranges result in the possible (normalised) step sizes of 0.005 and 0.01 for the first range and 0.05 and 0.1 for the second.

    coulix : Thanks ! the factor = 10**math.ceil(math.log(delta,10)) did the tric !
  • You might want to take a look at Jgraph, which solves a complementary problem: it is a data grapher rather than a function grapher. But there are a lot of things in common such as dealing with major and minor tick marks, axis labels, and so on and so forth. I find the input language a little verbose for my taste, but Jgraph produces really nice technical graphs. There are a lot of examples on the web site and probably some good ideas you could steal.

    And you know what they say: talent imitates, but genius steals :-)

  • This seems to do what i was expecting.

    import math

    def main(): getTickGap(-1,1.5)

    def next_multiple(x, y): return math.ceil(x/y)*y

    def getTickGap(xmin, xmax): xdelta = xmax -xmin width = 250 # smallest power of 10 greater than delta factor = 10**math.ceil(math.log(xdelta,10)) # 0.1 <= normalised_delta < 1 normalised_delta = xdelta / factor print("normalised_delta", normalised_delta)

    # we want largest gap
    if normalised_delta/4 >= 0.1:
      step_size = 0.1
    elif normalised_delta/4 >= 0.05:
      step_size = 0.05
    elif normalised_delta/20 <= 0.01:
      step_size = 0.01
    step_size = step_size * factor
    
    
    ##    if normalised_delta/20 == 0.005:
    ##      step_size = 0.005
    ##    elif normalised_delta/20 <= 0.01:
    ##      step_size = 0.01
    ##    elif normalised_delta/4 >= 0.05:
    ##      step_size = 0.05
    ##    step_size = step_size * factor
    print("step_size", step_size)
    totalsteps = xdelta/step_size
    print("Total steps", totalsteps)
    print("Range [", xmin, ",", xmax, "]")
    
    firstInc = next_multiple(xmin, step_size)
    count = (250/xdelta)*(firstInc - xmin)
    print("firstInc ", firstInc, 'tick at ', count)
    print("start at ", firstInc - xmin, (width/totalsteps)*(firstInc - xmin))
    inc = firstInc
    
    while (inc <xmax):
        inc += step_size
        count += (width/totalsteps)
        print(" inc", inc, "tick at ", count)
    

    if name == "main": main()

  • On range -1, 0

    i get

    normalised_delta 1.0
    step_size 0.1
    Total steps 10.0
    Range [ -1 , 0 ]
    firstInc  -1.0 tick at  0.0
    start at  0.0 0.0
     inc -0.9 tick at  25.0
     inc -0.8 tick at  50.0
     inc -0.7 tick at  75.0
     inc -0.6 tick at  100.0
     inc -0.5 tick at  125.0
     inc -0.4 tick at  150.0
     inc -0.3 tick at  175.0
     inc -0.2 tick at  200.0
     inc -0.1 tick at  225.0
     inc -1.38777878078e-16 tick at  250.0
     inc 0.1 tick at  275.0
    

    How come the second line from bottom get this number ????

    mweerden : This is due to the inaccuracies of floating-point numbers and operations on computers. Specifically, 0.1 does not have a precise representation and with + you keep adding the error. If you use -1.0+9*0.1 the error is much smaller. (See http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems)

0 comments:

Post a Comment