blob: 808de676e758120ca6531f9d1328871ef4d9b402 [file] [log] [blame]
Stephen M. Cameronaf58ef32012-03-07 07:56:16 +01001#include <stdio.h>
2#include <math.h>
3#include <malloc.h>
Stephen M. Cameron17ba3e42012-03-07 19:36:18 +01004#include <string.h>
Stephen M. Cameronaf58ef32012-03-07 07:56:16 +01005
Jens Axboe3c3ed072012-03-27 09:12:39 +02006/*
Stephen M. Cameronaf58ef32012-03-07 07:56:16 +01007 * 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
17static 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
Stephen M. Cameron7175d912012-03-11 11:35:10 +010042static void shorten(struct tickmark *tm, int nticks, int *power_of_ten,
Jens Axboed8fbeef2012-03-14 10:25:44 +010043 int use_KMG_symbols, int base_offset)
Stephen M. Cameron17ba3e42012-03-07 19:36:18 +010044{
Jens Axboed8fbeef2012-03-14 10:25:44 +010045 const char shorten_chr[] = { 0, 'K', 'M', 'G', 'P', 'E', 0 };
46 int i, l, minshorten, shorten_idx = 0;
Stephen M. Cameron1e1ffcc2012-03-11 11:33:46 +010047 char *str;
Stephen M. Cameron17ba3e42012-03-07 19:36:18 +010048
Stephen M. Cameron1e1ffcc2012-03-11 11:33:46 +010049 minshorten = 100;
50 for (i = 0; i < nticks; i++) {
51 str = tm[i].string;
52 l = strlen(str);
53
Stephen M. Cameronc9e8d6e2012-03-12 07:32:30 +010054 if (strcmp(str, "0") == 0)
55 continue;
Stephen M. Cameron1e1ffcc2012-03-11 11:33:46 +010056 if (l > 9 && strcmp(&str[l - 9], "000000000") == 0) {
Stephen M. Cameron7175d912012-03-11 11:35:10 +010057 *power_of_ten = 9;
Jens Axboed8fbeef2012-03-14 10:25:44 +010058 shorten_idx = 3;
Stephen M. Cameron1e1ffcc2012-03-11 11:33:46 +010059 } else if (6 < minshorten && l > 6 &&
60 strcmp(&str[l - 6], "000000") == 0) {
Stephen M. Cameron7175d912012-03-11 11:35:10 +010061 *power_of_ten = 6;
Jens Axboed8fbeef2012-03-14 10:25:44 +010062 shorten_idx = 2;
Stephen M. Cameron1e1ffcc2012-03-11 11:33:46 +010063 } else if (l > 3 && strcmp(&str[l - 3], "000") == 0) {
Stephen M. Cameron7175d912012-03-11 11:35:10 +010064 *power_of_ten = 3;
Jens Axboed8fbeef2012-03-14 10:25:44 +010065 shorten_idx = 1;
Stephen M. Cameron1e1ffcc2012-03-11 11:33:46 +010066 } else {
Stephen M. Cameron7175d912012-03-11 11:35:10 +010067 *power_of_ten = 0;
Stephen M. Cameron1e1ffcc2012-03-11 11:33:46 +010068 }
69
Stephen M. Cameron7175d912012-03-11 11:35:10 +010070 if (*power_of_ten < minshorten)
71 minshorten = *power_of_ten;
Stephen M. Cameron1e1ffcc2012-03-11 11:33:46 +010072 }
73
74 if (minshorten == 0)
75 return;
Jens Axboed8fbeef2012-03-14 10:25:44 +010076 if (!use_KMG_symbols)
77 shorten_idx = 0;
78 else if (base_offset)
79 shorten_idx += base_offset;
Stephen M. Cameron1e1ffcc2012-03-11 11:33:46 +010080
81 for (i = 0; i < nticks; i++) {
82 str = tm[i].string;
83 l = strlen(str);
Jens Axboed8fbeef2012-03-14 10:25:44 +010084 str[l - minshorten] = shorten_chr[shorten_idx];
85 if (shorten_idx)
86 str[l - minshorten + 1] = '\0';
Stephen M. Cameron17ba3e42012-03-07 19:36:18 +010087 }
88}
89
Stephen M. Cameron7175d912012-03-11 11:35:10 +010090int calc_tickmarks(double min, double max, int nticks, struct tickmark **tm,
Jens Axboed8fbeef2012-03-14 10:25:44 +010091 int *power_of_ten, int use_KMG_symbols, int base_offset)
Stephen M. Cameronaf58ef32012-03-07 07:56:16 +010092{
93 char str[100];
94 int nfrac;
95 double d; /* tick mark spacing */
96 double graphmin, graphmax; /* graph range min and max */
97 double range, x;
98 int count, i;
99
100 /* we expect min != max */
101 range = nicenum(max - min, 0);
102 d = nicenum(range / (nticks - 1), 1);
103 graphmin = floor(min / d) * d;
104 graphmax = ceil(max / d) * d;
105 nfrac = MAX(-floor(log10(d)), 0);
106 snprintf(str, sizeof(str)-1, "%%.%df", nfrac);
107
108 count = ((graphmax + 0.5 * d) - graphmin) / d + 1;
109 *tm = malloc(sizeof(**tm) * count);
110
111 i = 0;
112 for (x = graphmin; x < graphmax + 0.5 * d; x += d) {
113 (*tm)[i].value = x;
114 sprintf((*tm)[i].string, str, x);
Stephen M. Cameronaf58ef32012-03-07 07:56:16 +0100115 i++;
116 }
Jens Axboed8fbeef2012-03-14 10:25:44 +0100117 shorten(*tm, i, power_of_ten, use_KMG_symbols, base_offset);
Stephen M. Cameronaf58ef32012-03-07 07:56:16 +0100118 return i;
119}
120
121#if 0
122
123static void test_range(double x, double y)
124{
125 int nticks, i;
126
127 struct tickmark *tm = NULL;
128 printf("Testing range %g - %g\n", x, y);
129 nticks = calc_tickmarks(x, y, 10, &tm);
130
Jens Axboe3c3ed072012-03-27 09:12:39 +0200131 for (i = 0; i < nticks; i++)
Stephen M. Cameronaf58ef32012-03-07 07:56:16 +0100132 printf(" (%s) %g\n", tm[i].string, tm[i].value);
Jens Axboe3c3ed072012-03-27 09:12:39 +0200133
Stephen M. Cameronaf58ef32012-03-07 07:56:16 +0100134 printf("\n\n");
135 free(tm);
136}
137
138int main(int argc, char *argv[])
139{
Jens Axboe3c3ed072012-03-27 09:12:39 +0200140 test_range(0.0005, 0.008);
141 test_range(0.5, 0.8);
142 test_range(5.5, 8.8);
143 test_range(50.5, 80.8);
144 test_range(-20, 20.8);
145 test_range(-30, 700.8);
Stephen M. Cameronaf58ef32012-03-07 07:56:16 +0100146}
147#endif