2605f721f0089de5567f0a1195d6d27e0cc71095
[fio.git] / tickmarks.c
1 #include <stdio.h>
2 #include <math.h>
3 #include <malloc.h>
4 #include <string.h>
5
6 /* 
7  * adapted from Paul Heckbert's algorithm on p 657-659 of
8  * Andrew S. Glassner's book, "Graphics Gems"
9  * ISBN 0-12-286166-3
10  *
11  */
12
13 #include "tickmarks.h"
14
15 #define MAX(a, b) (((a) < (b)) ? (b) : (a))
16
17 static double nicenum(double x, int round)
18 {
19         int exp;        /* exponent of x */
20         double f;       /* fractional part of x */
21
22         exp = floor(log10(x));
23         f = x / pow(10.0, exp);
24         if (round) {
25                 if (f < 1.5)
26                         return 1.0 * pow(10.0, exp);
27                 if (f < 3.0)
28                         return 2.0 * pow(10.0, exp);
29                 if (f < 7.0)
30                         return 5.0 * pow(10.0, exp);
31                 return 10.0 * pow(10.0, exp);
32         }
33         if (f <= 1.0)
34                 return 1.0 * pow(10.0, exp);
35         if (f <= 2.0)
36                 return 2.0 * pow(10.0, exp);
37         if (f <= 5.0)
38                 return 5.0 * pow(10.0, exp);
39         return 10.0 * pow(10.0, exp);
40 }
41
42 static void shorten(struct tickmark *tm, int nticks, int *power_of_ten,
43                         int use_KMG_symbols)
44 {
45         int i, l, minshorten;
46         char *str;
47         char shorten_char = '?';
48
49         minshorten = 100;
50         for (i = 0; i < nticks; i++) {
51                 str = tm[i].string;
52                 l = strlen(str);
53
54                 if (strcmp(str, "0") == 0)
55                         continue;
56                 if (l > 9 && strcmp(&str[l - 9], "000000000") == 0) {
57                         *power_of_ten = 9;
58                         shorten_char = use_KMG_symbols ? 'G' : '\0';
59                 } else if (6 < minshorten && l > 6 &&
60                                 strcmp(&str[l - 6], "000000") == 0) {
61                         *power_of_ten = 6;
62                         shorten_char = use_KMG_symbols ? 'M' : '\0';
63                 } else if (l > 3 && strcmp(&str[l - 3], "000") == 0) {
64                         *power_of_ten = 3;
65                         shorten_char = use_KMG_symbols ? 'K': '\0';
66                 } else {
67                         *power_of_ten = 0;
68                 }
69
70                 if (*power_of_ten < minshorten)
71                         minshorten = *power_of_ten;
72         }
73
74         if (minshorten == 0)
75                 return;
76
77         for (i = 0; i < nticks; i++) {
78                 str = tm[i].string;
79                 l = strlen(str);
80                 str[l - minshorten] = shorten_char;
81                 str[l - minshorten + 1] = '\0';
82         }
83 }
84
85 int calc_tickmarks(double min, double max, int nticks, struct tickmark **tm,
86                 int *power_of_ten, int use_KMG_symbols)
87 {
88         char str[100];
89         int nfrac;
90         double d;       /* tick mark spacing */
91         double graphmin, graphmax;      /* graph range min and max */
92         double range, x;
93         int count, i;
94
95         /* we expect min != max */
96         range = nicenum(max - min, 0);
97         d = nicenum(range / (nticks - 1), 1);
98         graphmin = floor(min / d) * d;
99         graphmax = ceil(max / d) * d;
100         nfrac = MAX(-floor(log10(d)), 0);
101         snprintf(str, sizeof(str)-1, "%%.%df", nfrac);
102
103         count = ((graphmax + 0.5 * d) - graphmin) / d + 1;
104         *tm = malloc(sizeof(**tm) * count);
105
106         i = 0;
107         for (x = graphmin; x < graphmax + 0.5 * d; x += d) {
108                 (*tm)[i].value = x;
109                 sprintf((*tm)[i].string, str, x);
110                 i++;
111         }
112         shorten(*tm, i, power_of_ten, use_KMG_symbols);
113         return i;
114 }
115
116 #if 0
117
118 static void test_range(double x, double y)
119 {
120         int nticks, i;
121
122         struct tickmark *tm = NULL;
123         printf("Testing range %g - %g\n", x, y);
124         nticks = calc_tickmarks(x, y, 10, &tm);
125
126         for (i = 0; i < nticks; i++) {
127                 printf("   (%s) %g\n", tm[i].string, tm[i].value);
128         }
129         printf("\n\n");
130         free(tm);
131 }
132
133 int main(int argc, char *argv[])
134 {
135         test_range(0.0005, 0.008);      
136         test_range(0.5, 0.8);   
137         test_range(5.5, 8.8);   
138         test_range(50.5, 80.8); 
139         test_range(-20, 20.8);  
140         test_range(-30, 700.8); 
141 }
142 #endif