vmrad@deneb.ucdavis.edu (0048;0000008890;500;737;56;) (12/03/88)
I am currently creating a graph drawing package for our Sophy nuclear medicine computer. Among the items I wish this package to do is label the axis with pleasing numbers, and place tick marks at pleasing intervals. I found that writing a function which produces such pleasing numbers given the min and max as input is decidedly non-trivial. Rather than re-invent a wheel, I thought I would try and tap the collective expertise of comp.graphics. What I am looking for in a nutshell: axisMin axisMax =somefunction=> graphMin graphMax majorTick minorTick Can anyone point me toward some code that generates such axis values? A reference that discusses such code would be very nice, too. We have almost 300 books on graphics here at UCD, and I picked the ten most likely to have such a discussion and found nothing. I do not savor the idea of perusing the remainder of the books for such a function.
vmrad@deneb.ucdavis.edu (0048;0000008890;500;737;56;) (12/03/88)
OK. I figured out when to hit CR and when not to. Here is my name in the header and my .signature for my previous message. Sign, these things are so complex. Bernard Littau VM Radiological Sciences Telephone: (916) 752-0184 School of Veterinary Medicine Internet: vmrad@ucdavis.edu University of California BITNET: vmrad@ucdavis Davis, CA 95616 UUCP: {ucbvax,lll-crg,sdcsvax}!ucdavis!vmrad
ph@miro.Berkeley.EDU (Paul Heckbert) (12/03/88)
In article <3343@ucdavis.ucdavis.edu> vmrad@deneb.ucdavis.edu asked for code to label axes with pleasing numbers and place tick marks at pleasing intervals. Here is some code I've used. The heart of it is a simple little routine called "nicenum" that picks "nice-looking" numbers. The below is geared toward y axis labeling but of course it could work equally well for the x axis. Paul Heckbert, CS grad student 508-7 Evans Hall, UC Berkeley UUCP: ucbvax!miro.berkeley.edu!ph Berkeley, CA 94720 ARPA: ph@miro.berkeley.edu /* * label: test program to demonstrate nice graph axis labeling * * Paul Heckbert, 2 Dec 88 */ #include <stdio.h> #include <math.h> double expt(), tick(), nicenum(); #define NTICK 5 /* desired number of tick marks */ main(ac, av) int ac; char **av; { double ymin, ymax; if (ac!=3) { fprintf(stderr, "Usage: label <ymin> <ymax>\n"); exit(1); } ymin = atof(av[1]); ymax = atof(av[2]); ylabel(ymin, ymax); } ylabel(ymin, ymax) double ymin, ymax; { char str[6], temp[20]; int exp; double graphymin, graphymax, range, d, y; /* we expect ymin!=ymax */ range = nicenum(ymax-ymin, 0); d = nicenum(range/(NTICK-1), 1); /* tick mark spacing */ graphymin = floor(ymin/d)*d; graphymax = ceil(ymax/d)*d; exp = floor(log10(d)); sprintf(str, "%%.%df", exp<0 ? -exp : 0); /* simplest axis labels */ printf("graphymin=%g graphymax=%g increment=%g\n", graphymin, graphymax, d); for (y=graphymin; y<graphymax+.5*d; y+=d) { sprintf(temp, str, y); printf("(%s)\n", temp); } } /* * nicenum: find a "nice" number approximately equal to x * round if round=1, ceil if round=0 */ static double nicenum(x, round) double x; int round; { int exp; double f, y; exp = floor(log10(x)); f = x/expt(10., exp); /* fraction between 1 and 10 */ if (round) if (f<1.5) y = 1.; else if (f<3.) y = 2.; else if (f<7.) y = 5.; else y = 10.; else if (f<=1.) y = 1.; else if (f<=2.) y = 2.; else if (f<=5.) y = 5.; else y = 10.; return y*expt(10., exp); } /* * expt(a,n)=a^n for integer n * roundoff errors in pow were causing problems, so I wrote my own */ double expt(a, n) double a; register int n; { double x; x = 1.; if (n>0) for (; n>0; n--) x *= a; else for (; n<0; n++) x /= a; return x; }
kent@decwrl.dec.com (Christopher A. Kent) (12/05/88)
The ACM "Collected Algorithms" has routines (in FORTRAN) that do a very nice job of this. See Algorithm 463. (I believe the 464 does the same thing for a log scale.) -- Chris Kent Western Research Laboratory Digital Equipment Corporation kent@decwrl.dec.com decwrl!kent (415) 853-6639
wetter@cit-vax.Caltech.Edu (Pierce T. Wetter) (12/06/88)
in article <3343@ucdavis.ucdavis.edu>, vmrad@deneb.ucdavis.edu (0048;0000008890;500;737;56;) says: > > I am currently creating a graph drawing package for our Sophy nuclear > medicine computer. Among the items I wish this package to do is label > the axis with pleasing numbers, and place tick marks at pleasing > intervals. I found that writing a function which produces such > pleasing numbers given the min and max as input is decidedly > non-trivial. Rather than re-invent a wheel, I thought I would try and > tap the collective expertise of comp.graphics. > Here are the two functions I use in a package I wrote about 3 yrs ago. Getdiv returns the spacing between ticks given their minimum and maximum values find start finds a good starting point to start placing ticks at (i.e. if your range is from -27 to 0 use -25. You can also use the value of getdiv to round your numbers. Remember .1 + .1 + .1 + .1 +.1 +.1+ .1 +.1 +.1 +.1 is not equal to 1. This isn't a problem generally, but it is at zero because you have to make sure you label that point 0 not 2e-26. double getdiv(xmn, xmx) double xmn, xmx; { double diff, lgdiff, div, temp; diff = xmx - xmn; diff = sqrt(diff*diff); temp = log10(diff); lgdiff = (double) ((int) (temp + 0.5)); lgdiff -= 1.0; div = 10.0 / wglobals.minorcount * pow(10.0, lgdiff); if ((diff/div) < (2.1*wglobals.minorcount)) div /= 10.0; return (div); } double findstart(xmn, xmx) double xmn; double xmx; { /* find starting point given min max majorspacing*/ double x, dv; int xi, between(); dv = wglobals.minorcount * getdiv(xmn, xmx); /*minorcount is the number of minor ticks per major tick*/ xi = (int) xmn / (dv); x = (xi + 1) * dv; if (between(x, xmn, xmx)) /* xmn<= x <=xmx */ return x; while (!between(x, xmn, xmx) && (x < xmx)) { x += dv; } if (between(x, xmn, xmx)) return x; else return xmn; } Pierce -- ____________________________________________________________________________ You can flame or laud me at: wetter@tybalt.caltech.edu or wetter@csvax.caltech.edu or pwetter@caltech.bitnet (There would be a witty saying here, but my signature has to be < 4lines)