You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
147 lines
3.2 KiB
147 lines
3.2 KiB
#include <stdio.h> |
|
#include <math.h> |
|
#include <malloc.h> |
|
#include <string.h> |
|
|
|
/* |
|
* adapted from Paul Heckbert's algorithm on p 657-659 of |
|
* Andrew S. Glassner's book, "Graphics Gems" |
|
* ISBN 0-12-286166-3 |
|
* |
|
*/ |
|
|
|
#include "tickmarks.h" |
|
|
|
#define MAX(a, b) (((a) < (b)) ? (b) : (a)) |
|
|
|
static double nicenum(double x, int round) |
|
{ |
|
int exp; /* exponent of x */ |
|
double f; /* fractional part of x */ |
|
|
|
exp = floor(log10(x)); |
|
f = x / pow(10.0, exp); |
|
if (round) { |
|
if (f < 1.5) |
|
return 1.0 * pow(10.0, exp); |
|
if (f < 3.0) |
|
return 2.0 * pow(10.0, exp); |
|
if (f < 7.0) |
|
return 5.0 * pow(10.0, exp); |
|
return 10.0 * pow(10.0, exp); |
|
} |
|
if (f <= 1.0) |
|
return 1.0 * pow(10.0, exp); |
|
if (f <= 2.0) |
|
return 2.0 * pow(10.0, exp); |
|
if (f <= 5.0) |
|
return 5.0 * pow(10.0, exp); |
|
return 10.0 * pow(10.0, exp); |
|
} |
|
|
|
static void shorten(struct tickmark *tm, int nticks, int *power_of_ten, |
|
int use_KMG_symbols, int base_offset) |
|
{ |
|
const char shorten_chr[] = { 0, 'K', 'M', 'G', 'P', 'E', 0 }; |
|
int i, l, minshorten, shorten_idx = 0; |
|
char *str; |
|
|
|
minshorten = 100; |
|
for (i = 0; i < nticks; i++) { |
|
str = tm[i].string; |
|
l = strlen(str); |
|
|
|
if (strcmp(str, "0") == 0) |
|
continue; |
|
if (l > 9 && strcmp(&str[l - 9], "000000000") == 0) { |
|
*power_of_ten = 9; |
|
shorten_idx = 3; |
|
} else if (6 < minshorten && l > 6 && |
|
strcmp(&str[l - 6], "000000") == 0) { |
|
*power_of_ten = 6; |
|
shorten_idx = 2; |
|
} else if (l > 3 && strcmp(&str[l - 3], "000") == 0) { |
|
*power_of_ten = 3; |
|
shorten_idx = 1; |
|
} else { |
|
*power_of_ten = 0; |
|
} |
|
|
|
if (*power_of_ten < minshorten) |
|
minshorten = *power_of_ten; |
|
} |
|
|
|
if (minshorten == 0) |
|
return; |
|
if (!use_KMG_symbols) |
|
shorten_idx = 0; |
|
else if (base_offset) |
|
shorten_idx += base_offset; |
|
|
|
for (i = 0; i < nticks; i++) { |
|
str = tm[i].string; |
|
l = strlen(str); |
|
str[l - minshorten] = shorten_chr[shorten_idx]; |
|
if (shorten_idx) |
|
str[l - minshorten + 1] = '\0'; |
|
} |
|
} |
|
|
|
int calc_tickmarks(double min, double max, int nticks, struct tickmark **tm, |
|
int *power_of_ten, int use_KMG_symbols, int base_offset) |
|
{ |
|
char str[100]; |
|
int nfrac; |
|
double d; /* tick mark spacing */ |
|
double graphmin, graphmax; /* graph range min and max */ |
|
double range, x; |
|
int count, i; |
|
|
|
/* we expect min != max */ |
|
range = nicenum(max - min, 0); |
|
d = nicenum(range / (nticks - 1), 1); |
|
graphmin = floor(min / d) * d; |
|
graphmax = ceil(max / d) * d; |
|
nfrac = MAX(-floor(log10(d)), 0); |
|
snprintf(str, sizeof(str)-1, "%%.%df", nfrac); |
|
|
|
count = ((graphmax + 0.5 * d) - graphmin) / d + 1; |
|
*tm = malloc(sizeof(**tm) * count); |
|
|
|
i = 0; |
|
for (x = graphmin; x < graphmax + 0.5 * d; x += d) { |
|
(*tm)[i].value = x; |
|
sprintf((*tm)[i].string, str, x); |
|
i++; |
|
} |
|
shorten(*tm, i, power_of_ten, use_KMG_symbols, base_offset); |
|
return i; |
|
} |
|
|
|
#if 0 |
|
|
|
static void test_range(double x, double y) |
|
{ |
|
int nticks, i; |
|
|
|
struct tickmark *tm = NULL; |
|
printf("Testing range %g - %g\n", x, y); |
|
nticks = calc_tickmarks(x, y, 10, &tm); |
|
|
|
for (i = 0; i < nticks; i++) |
|
printf(" (%s) %g\n", tm[i].string, tm[i].value); |
|
|
|
printf("\n\n"); |
|
free(tm); |
|
} |
|
|
|
int main(int argc, char *argv[]) |
|
{ |
|
test_range(0.0005, 0.008); |
|
test_range(0.5, 0.8); |
|
test_range(5.5, 8.8); |
|
test_range(50.5, 80.8); |
|
test_range(-20, 20.8); |
|
test_range(-30, 700.8); |
|
} |
|
#endif
|
|
|