diff --git a/src/rrd_graph.c b/src/rrd_graph.c
index e922814ee5e3ea146ef8d208716678cb97c07cce..938132ac442fbb48e720a76d22d3ca97ac5a6b0e 100644 (file)
--- a/src/rrd_graph.c
+++ b/src/rrd_graph.c
/****************************************************************************
- * RRDtool 1.2.12 Copyright by Tobi Oetiker, 1997-2005
+ * RRDtool 1.2.13 Copyright by Tobi Oetiker, 1997-2006
****************************************************************************
* rrd__graph.c produce graphs from data in rrdfiles
****************************************************************************/
{-1,0,TMT_MONTH,0,TMT_MONTH,0,TMT_MONTH,0,0,""}
};
-/* sensible logarithmic y label intervals ...
- the first element of each row defines the possible starting points on the
- y axis ... the other specify the */
-
-double yloglab[][12]= {{ 1e9, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
- { 1e3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
- { 1e1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
- /* { 1e1, 1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, */
- { 1e1, 1, 2.5, 5, 7.5, 0, 0, 0, 0, 0, 0, 0 },
- { 1e1, 1, 2, 4, 6, 8, 0, 0, 0, 0, 0, 0 },
- { 1e1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0 },
- { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }};
-
/* sensible y label intervals ...*/
ylab_t ylab[]= {
}
-/* find SI magnitude symbol for the numbers on the y-axis*/
-void
-si_unit(
- image_desc_t *im /* image description */
-)
-{
-
- char symbol[] = {'a', /* 10e-18 Atto */
+static char si_symbol[] = {
+ 'a', /* 10e-18 Atto */
'f', /* 10e-15 Femto */
'p', /* 10e-12 Pico */
'n', /* 10e-9 Nano */
'G', /* 10e9 Giga */
'T', /* 10e12 Tera */
'P', /* 10e15 Peta */
- 'E'};/* 10e18 Exa */
+ 'E', /* 10e18 Exa */
+};
+static const int si_symbcenter = 6;
+
+/* find SI magnitude symbol for the numbers on the y-axis*/
+void
+si_unit(
+ image_desc_t *im /* image description */
+)
+{
- int symbcenter = 6;
double digits,viewdigits=0;
digits = floor( log( max( fabs(im->minval),fabs(im->maxval)))/log((double)im->base));
im->viewfactor = im->magfact / pow((double)im->base , viewdigits);
- if ( ((viewdigits+symbcenter) < sizeof(symbol)) &&
- ((viewdigits+symbcenter) >= 0) )
- im->symbol = symbol[(int)viewdigits+symbcenter];
+ if ( ((viewdigits+si_symbcenter) < sizeof(si_symbol)) &&
+ ((viewdigits+si_symbcenter) >= 0) )
+ im->symbol = si_symbol[(int)viewdigits+si_symbcenter];
else
im->symbol = '?';
}
there was no data in the graph ... this is not good ...
lets set these to dummy values then ... */
- if (isnan(minval)) minval = 0.0;
- if (isnan(maxval)) maxval = 1.0;
+ if (im->logarithmic) {
+ if (isnan(minval)) minval = 0.2;
+ if (isnan(maxval)) maxval = 5.1;
+ }
+ else {
+ if (isnan(minval)) minval = 0.0;
+ if (isnan(maxval)) maxval = 1.0;
+ }
/* adjust min and max values */
if (isnan(im->minval)
- /* don't adjust low-end with log scale */
- || ((!im->logarithmic && !im->rigid) && im->minval > minval)
- )
- im->minval = minval;
+ /* don't adjust low-end with log scale */ /* why not? */
+ || ((!im->rigid) && im->minval > minval)
+ ) {
+ if (im->logarithmic)
+ im->minval = minval * 0.5;
+ else
+ im->minval = minval;
+ }
if (isnan(im->maxval)
|| (!im->rigid && im->maxval < maxval)
) {
if (im->logarithmic)
- im->maxval = maxval * 1.1;
+ im->maxval = maxval * 2.0;
else
im->maxval = maxval;
}
int fill=0, fill_last;
int leg_c = 0;
int leg_x = border, leg_y = im->yimg;
+ int leg_y_prev = im->yimg;
int leg_cc;
int glue = 0;
int i,ii, mark = 0;
+ legspace[ii]
+ glue;
}
- leg_y += im->text_prop[TEXT_PROP_LEGEND].size*1.8;
- if (prt_fctn == 's') leg_y -= im->text_prop[TEXT_PROP_LEGEND].size;
+ leg_y_prev = leg_y;
+ /* only add y space if there was text on the line */
+ if (leg_x > border || prt_fctn == 's')
+ leg_y += im->text_prop[TEXT_PROP_LEGEND].size*1.8;
+ if (prt_fctn == 's')
+ leg_y -= im->text_prop[TEXT_PROP_LEGEND].size;
fill = 0;
leg_c = 0;
mark = ii;
}
}
- im->yimg = leg_y;
+ im->yimg = leg_y_prev;
+ /* if we did place some legends we have to add vertical space */
+ if (leg_y != im->yimg){
+ im->yimg += im->text_prop[TEXT_PROP_LEGEND].size*1.8;
+ }
free(legspace);
}
return 0;
return 1;
}
+/* this is frexp for base 10 */
+double frexp10(double, double *);
+double frexp10(double x, double *e) {
+ double mnt;
+ int iexp;
+
+ iexp = floor(log(fabs(x)) / log(10));
+ mnt = x / pow(10.0, iexp);
+ if(mnt >= 10.0) {
+ iexp++;
+ mnt = x / pow(10.0, iexp);
+ }
+ *e = iexp;
+ return mnt;
+}
+
/* logaritmic horizontal grid */
int
horizontal_log_grid(image_desc_t *im)
{
- double pixpex;
- int ii,i;
- int minoridx=0, majoridx=0;
- char graph_label[100];
- double X0,X1,Y0;
- double value, pixperstep, minstep;
+ double yloglab[][10] = {
+ {1.0, 10., 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
+ {1.0, 5.0, 10., 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
+ {1.0, 2.0, 5.0, 7.0, 10., 0.0, 0.0, 0.0, 0.0, 0.0},
+ {1.0, 2.0, 4.0, 6.0, 8.0, 10., 0.0, 0.0, 0.0, 0.0},
+ {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.}};
+
+ int i, j, val_exp, min_exp;
+ double nex; /* number of decades in data */
+ double logscale; /* scale in logarithmic space */
+ int exfrac = 1; /* decade spacing */
+ int mid = -1; /* row in yloglab for major grid */
+ double mspac; /* smallest major grid spacing (pixels) */
+ int flab; /* first value in yloglab to use */
+ double value, tmp;
+ double X0,X1,Y0;
+ char graph_label[100];
- /* find grid spaceing */
- pixpex= (double)im->ysize / (log10(im->maxval) - log10(im->minval));
+ nex = log10(im->maxval / im->minval);
+ logscale = im->ysize / nex;
- if (isnan(pixpex)) {
- return 0;
- }
+ /* major spacing for data with high dynamic range */
+ while(logscale * exfrac < 3 * im->text_prop[TEXT_PROP_LEGEND].size) {
+ if(exfrac == 1) exfrac = 3;
+ else exfrac += 3;
+ }
- for(i=0;yloglab[i][0] > 0;i++){
- minstep = log10(yloglab[i][0]);
- for(ii=1;yloglab[i][ii+1] > 0;ii++){
- if(yloglab[i][ii+2]==0){
- minstep = log10(yloglab[i][ii+1])-log10(yloglab[i][ii]);
- break;
+ /* major spacing for less dynamic data */
+ do {
+ /* search best row in yloglab */
+ mid++;
+ for(i = 0; yloglab[mid][i + 1] < 10.0; i++);
+ mspac = logscale * log10(10.0 / yloglab[mid][i]);
+ } while(mspac > 2 * im->text_prop[TEXT_PROP_LEGEND].size && mid < 5);
+ if(mid) mid--;
+
+ /* find first value in yloglab */
+ for(flab = 0; frexp10(im->minval, &tmp) > yloglab[mid][flab]; flab++);
+ if(yloglab[mid][flab] == 10.0) {
+ tmp += 1.0;
+ flab = 0;
+ }
+ val_exp = tmp;
+ if(val_exp % exfrac) val_exp += abs(-val_exp % exfrac);
+
+ X0=im->xorigin;
+ X1=im->xorigin+im->xsize;
+
+ /* draw grid */
+ while(1) {
+ value = yloglab[mid][flab] * pow(10.0, val_exp);
+
+ Y0 = ytr(im, value);
+ if(Y0 <= im->yorigin - im->ysize) break;
+
+ /* major grid line */
+ gfx_new_dashed_line ( im->canvas,
+ X0-2,Y0,
+ X1+2,Y0,
+ MGRIDWIDTH, im->graph_col[GRC_MGRID],
+ im->grid_dash_on, im->grid_dash_off);
+
+ /* label */
+ if (im->extra_flags & FORCE_UNITS_SI) {
+ int scale;
+ double pvalue;
+ char symbol;
+
+ scale = floor(val_exp / 3.0);
+ if( value >= 1.0 ) pvalue = pow(10.0, val_exp % 3);
+ else pvalue = pow(10.0, ((val_exp + 1) % 3) + 2);
+ pvalue *= yloglab[mid][flab];
+
+ if ( ((scale+si_symbcenter) < (int)sizeof(si_symbol)) &&
+ ((scale+si_symbcenter) >= 0) )
+ symbol = si_symbol[scale+si_symbcenter];
+ else
+ symbol = '?';
+
+ sprintf(graph_label,"%3.0f %c", pvalue, symbol);
+ } else
+ sprintf(graph_label,"%3.0e", value);
+ gfx_new_text ( im->canvas,
+ X0-im->text_prop[TEXT_PROP_AXIS].size, Y0,
+ im->graph_col[GRC_FONT],
+ im->text_prop[TEXT_PROP_AXIS].font,
+ im->text_prop[TEXT_PROP_AXIS].size,
+ im->tabwidth,0.0, GFX_H_RIGHT, GFX_V_CENTER,
+ graph_label );
+
+ /* minor grid */
+ if(mid < 4 && exfrac == 1) {
+ /* find first and last minor line behind current major line
+ * i is the first line and j tha last */
+ if(flab == 0) {
+ min_exp = val_exp - 1;
+ for(i = 1; yloglab[mid][i] < 10.0; i++);
+ i = yloglab[mid][i - 1] + 1;
+ j = 10;
+ }
+ else {
+ min_exp = val_exp;
+ i = yloglab[mid][flab - 1] + 1;
+ j = yloglab[mid][flab];
}
+
+ /* draw minor lines below current major line */
+ for(; i < j; i++) {
+
+ value = i * pow(10.0, min_exp);
+ if(value < im->minval) continue;
+
+ Y0 = ytr(im, value);
+ if(Y0 <= im->yorigin - im->ysize) break;
+
+ /* draw lines */
+ gfx_new_dashed_line ( im->canvas,
+ X0-1,Y0,
+ X1+1,Y0,
+ GRIDWIDTH, im->graph_col[GRC_GRID],
+ im->grid_dash_on, im->grid_dash_off);
+ }
+ }
+ else if(exfrac > 1) {
+ for(i = val_exp - exfrac / 3 * 2; i < val_exp; i += exfrac / 3) {
+ value = pow(10.0, i);
+ if(value < im->minval) continue;
+
+ Y0 = ytr(im, value);
+ if(Y0 <= im->yorigin - im->ysize) break;
+
+ /* draw lines */
+ gfx_new_dashed_line ( im->canvas,
+ X0-1,Y0,
+ X1+1,Y0,
+ GRIDWIDTH, im->graph_col[GRC_GRID],
+ im->grid_dash_on, im->grid_dash_off);
+ }
+ }
+
+ /* next decade */
+ if(yloglab[mid][++flab] == 10.0) {
+ flab = 0;
+ val_exp += exfrac;
}
- pixperstep = pixpex * minstep;
- if(pixperstep > 5){minoridx = i;}
- if(pixperstep > 2 * im->text_prop[TEXT_PROP_LEGEND].size){majoridx = i;}
}
-
- X0=im->xorigin;
- X1=im->xorigin+im->xsize;
- /* paint minor grid */
- for (value = pow((double)10, log10(im->minval)
- - fmod(log10(im->minval),log10(yloglab[minoridx][0])));
- value <= im->maxval;
- value *= yloglab[minoridx][0]){
- if (value < im->minval) continue;
- i=0;
- while(yloglab[minoridx][++i] > 0){
- Y0 = ytr(im,value * yloglab[minoridx][i]);
- if (Y0 <= im->yorigin - im->ysize) break;
- gfx_new_dashed_line ( im->canvas,
- X0-1,Y0,
- X1+1,Y0,
- GRIDWIDTH, im->graph_col[GRC_GRID],
- im->grid_dash_on, im->grid_dash_off);
+
+ /* draw minor lines after highest major line */
+ if(mid < 4 && exfrac == 1) {
+ /* find first and last minor line below current major line
+ * i is the first line and j tha last */
+ if(flab == 0) {
+ min_exp = val_exp - 1;
+ for(i = 1; yloglab[mid][i] < 10.0; i++);
+ i = yloglab[mid][i - 1] + 1;
+ j = 10;
+ }
+ else {
+ min_exp = val_exp;
+ i = yloglab[mid][flab - 1] + 1;
+ j = yloglab[mid][flab];
+ }
+
+ /* draw minor lines below current major line */
+ for(; i < j; i++) {
+
+ value = i * pow(10.0, min_exp);
+ if(value < im->minval) continue;
+
+ Y0 = ytr(im, value);
+ if(Y0 <= im->yorigin - im->ysize) break;
+
+ /* draw lines */
+ gfx_new_dashed_line ( im->canvas,
+ X0-1,Y0,
+ X1+1,Y0,
+ GRIDWIDTH, im->graph_col[GRC_GRID],
+ im->grid_dash_on, im->grid_dash_off);
+ }
+ }
+ /* fancy minor gridlines */
+ else if(exfrac > 1) {
+ for(i = val_exp - exfrac / 3 * 2; i < val_exp; i += exfrac / 3) {
+ value = pow(10.0, i);
+ if(value < im->minval) continue;
+
+ Y0 = ytr(im, value);
+ if(Y0 <= im->yorigin - im->ysize) break;
+
+ /* draw lines */
+ gfx_new_dashed_line ( im->canvas,
+ X0-1,Y0,
+ X1+1,Y0,
+ GRIDWIDTH, im->graph_col[GRC_GRID],
+ im->grid_dash_on, im->grid_dash_off);
}
}
- /* paint major grid and labels*/
- for (value = pow((double)10, log10(im->minval)
- - fmod(log10(im->minval),log10(yloglab[majoridx][0])));
- value <= im->maxval;
- value *= yloglab[majoridx][0]){
- if (value < im->minval) continue;
- i=0;
- while(yloglab[majoridx][++i] > 0){
- Y0 = ytr(im,value * yloglab[majoridx][i]);
- if (Y0 <= im->yorigin - im->ysize) break;
- gfx_new_dashed_line ( im->canvas,
- X0-2,Y0,
- X1+2,Y0,
- MGRIDWIDTH, im->graph_col[GRC_MGRID],
- im->grid_dash_on, im->grid_dash_off);
-
- sprintf(graph_label,"%3.0e",value * yloglab[majoridx][i]);
- gfx_new_text ( im->canvas,
- X0-im->text_prop[TEXT_PROP_AXIS].size, Y0,
- im->graph_col[GRC_FONT],
- im->text_prop[TEXT_PROP_AXIS].font,
- im->text_prop[TEXT_PROP_AXIS].size,
- im->tabwidth,0.0, GFX_H_RIGHT, GFX_V_CENTER,
- graph_label );
- }
- }
- return 1;
+ return 1;
}
parsetime("end-24h", &start_tv);
parsetime("now", &end_tv);
+ /* defines for long options without a short equivalent. should be bytes,
+ and may not collide with (the ASCII value of) short options */
+ #define LONGOPT_UNITS_SI 255
+
while (1){
static struct option long_options[] =
{
{"no-gridfit", no_argument, 0, 'N'},
{"units-exponent",required_argument, 0, 'X'},
{"units-length",required_argument, 0, 'L'},
+ {"units", required_argument, 0, LONGOPT_UNITS_SI },
{"step", required_argument, 0, 'S'},
{"tabwidth", required_argument, 0, 'T'},
{"font-render-mode", required_argument, 0, 'R'},
case 'F':
im->extra_flags |= FORCE_RULES_LEGEND;
break;
+ case LONGOPT_UNITS_SI:
+ if(im->extra_flags & FORCE_UNITS) {
+ rrd_set_error("--units can only be used once!");
+ return;
+ }
+ if(strcmp(optarg,"si")==0)
+ im->extra_flags |= FORCE_UNITS_SI;
+ else {
+ rrd_set_error("invalid argument for --units: %s", optarg );
+ return;
+ }
+ break;
case 'X':
im->unitsexponent = atoi(optarg);
break;
case 'o':
im->logarithmic = 1;
- if (isnan(im->minval))
- im->minval=1;
break;
case 'c':
if(sscanf(optarg,
return;
}
- if (im->logarithmic == 1 && (im->minval <= 0 || isnan(im->minval))){
+ if (im->logarithmic == 1 && im->minval <= 0){
rrd_set_error("for a logarithmic yaxis you must specify a lower-limit > 0");
return;
}