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, int base_offset) 44 { 45 const char shorten_chr[] = { 0, 'K', 'M', 'G', 'P', 'E', 0 }; 46 int i, l, minshorten, shorten_idx = 0; 47 char *str; 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_idx = 3; 59 } else if (6 < minshorten && l > 6 && 60 strcmp(&str[l - 6], "000000") == 0) { 61 *power_of_ten = 6; 62 shorten_idx = 2; 63 } else if (l > 3 && strcmp(&str[l - 3], "000") == 0) { 64 *power_of_ten = 3; 65 shorten_idx = 1; 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 if (!use_KMG_symbols) 77 shorten_idx = 0; 78 else if (base_offset) 79 shorten_idx += base_offset; 80 81 for (i = 0; i < nticks; i++) { 82 str = tm[i].string; 83 l = strlen(str); 84 str[l - minshorten] = shorten_chr[shorten_idx]; 85 if (shorten_idx) 86 str[l - minshorten + 1] = '\0'; 87 } 88 } 89 90 int calc_tickmarks(double min, double max, int nticks, struct tickmark **tm, 91 int *power_of_ten, int use_KMG_symbols, int base_offset) 92 { 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); 115 i++; 116 } 117 shorten(*tm, i, power_of_ten, use_KMG_symbols, base_offset); 118 return i; 119 } 120 121 #if 0 122 123 static 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 131 for (i = 0; i < nticks; i++) 132 printf(" (%s) %g\n", tm[i].string, tm[i].value); 133 134 printf("\n\n"); 135 free(tm); 136 } 137 138 int main(int argc, char *argv[]) 139 { 140 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); 146 } 147 #endif 148