/****************************************************************************
- * RRDtool 1.2.11 Copyright by Tobi Oetiker, 1997-2005
+ * RRDtool 1.2.15 Copyright by Tobi Oetiker, 1997-2006
****************************************************************************
* rrd__graph.c produce graphs from data in rrdfiles
****************************************************************************/
#include <sys/stat.h>
+#ifdef WIN32
+#include "strftime.h"
+#endif
#include "rrd_tool.h"
-#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
#include <io.h>
#include <fcntl.h>
#endif
{10, 0, TMT_MINUTE,5, TMT_MINUTE,20, TMT_MINUTE,20, 0,"%H:%M"},
{30, 0, TMT_MINUTE,10, TMT_HOUR,1, TMT_HOUR,1, 0,"%H:%M"},
{60, 0, TMT_MINUTE,30, TMT_HOUR,2, TMT_HOUR,2, 0,"%H:%M"},
+ {60, 24*3600, TMT_MINUTE,30, TMT_HOUR,2, TMT_HOUR,4, 0,"%a %H:%M"},
{180, 0, TMT_HOUR,1, TMT_HOUR,6, TMT_HOUR,6, 0,"%H:%M"},
- {180, 1*24*3600, TMT_HOUR,1, TMT_HOUR,6, TMT_HOUR,6, 0,"%a %H:%M"},
+ {180, 24*3600, TMT_HOUR,1, TMT_HOUR,6, TMT_HOUR,12, 0,"%a %H:%M"},
/*{300, 0, TMT_HOUR,3, TMT_HOUR,12, TMT_HOUR,12, 12*3600,"%a %p"}, this looks silly*/
{600, 0, TMT_HOUR,6, TMT_DAY,1, TMT_DAY,1, 24*3600,"%a"},
- {600, 1*24*3600, TMT_HOUR,6, TMT_DAY,1, TMT_DAY,1, 24*3600,"%a %d"},
- {1800, 0, TMT_HOUR,12, TMT_DAY,1, TMT_DAY,2, 24*3600,"%a"},
- {1800, 1*24*3600, TMT_HOUR,12, TMT_DAY,1, TMT_DAY,2, 24*3600,"%a %d"},
- {3600, 0, TMT_DAY,1, TMT_WEEK,1, TMT_WEEK,1, 7*24*3600,"Week %V"},
- {3*3600, 0, TMT_WEEK,1, TMT_MONTH,1, TMT_WEEK,2, 7*24*3600,"Week %V"},
+ {1200, 0, TMT_HOUR,6, TMT_DAY,1, TMT_DAY,1, 24*3600,"%d"},
+ {1800, 0, TMT_HOUR,12, TMT_DAY,1, TMT_DAY,2, 24*3600,"%a %d"},
+ {2400, 0, TMT_HOUR,12, TMT_DAY,1, TMT_DAY,2, 24*3600,"%a"},
+ {3600, 0, TMT_DAY,1, TMT_WEEK,1, TMT_WEEK,1, 7*24*3600,"Week %V"},
+ {3*3600, 0, TMT_WEEK,1, TMT_MONTH,1, TMT_WEEK,2, 7*24*3600,"Week %V"},
{6*3600, 0, TMT_MONTH,1, TMT_MONTH,1, TMT_MONTH,1, 30*24*3600,"%b"},
{48*3600, 0, TMT_MONTH,1, TMT_MONTH,3, TMT_MONTH,3, 30*24*3600,"%b"},
+ {315360, 0, TMT_MONTH,3, TMT_YEAR,1, TMT_YEAR,1, 365*24*3600,"%Y"},
{10*24*3600, 0, TMT_YEAR,1, TMT_YEAR,1, TMT_YEAR,1, 365*24*3600,"%y"},
{-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[]= {
conv_if(VRULE,GF_VRULE)
conv_if(LINE,GF_LINE)
conv_if(AREA,GF_AREA)
- conv_if(STACK,GF_STACK)
+ conv_if(STACK,GF_STACK)
conv_if(TICK,GF_TICK)
conv_if(DEF,GF_DEF)
conv_if(CDEF,GF_CDEF)
}
-/* 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 = '?';
}
break;
}
if (! skip) {
- unsigned long ft_step = im->gdes[i].step ;
+ unsigned long ft_step = im->gdes[i].step ; /* ft_step will record what we got from fetch */
if((rrd_fetch_fn(im->gdes[i].rrd,
im->gdes[i].cf,
return -1;
}
im->gdes[i].data_first = 1;
- im->gdes[i].step = im->step;
if (ft_step < im->gdes[i].step) {
reduce_data(im->gdes[i].cf_reduce,
for(i=0;i<im->gdes_c;i++) {
if((im->gdes[i].gf==GF_LINE) ||
(im->gdes[i].gf==GF_AREA) ||
- (im->gdes[i].gf==GF_TICK) ||
- (im->gdes[i].gf==GF_STACK)) {
+ (im->gdes[i].gf==GF_TICK)) {
if((im->gdes[i].p_data = malloc((im->xsize +1)
* sizeof(rrd_value_t)))==NULL){
rrd_set_error("malloc data_proc");
case GF_TICK:
if (!im->gdes[ii].stack)
paintval = 0.0;
- case GF_STACK:
value = im->gdes[ii].yrule;
if (isnan(value) || (im->gdes[ii].gf == GF_TICK)) {
/* The time of the data doesn't necessarily match
** relevant for min and max
*/
if (finite(paintval) && im->gdes[ii].gf != GF_TICK ) {
- if (isnan(minval) || paintval < minval)
- minval = paintval;
+ if ((isnan(minval) || paintval < minval ) &&
+ ! (im->logarithmic && paintval <= 0.0))
+ minval = paintval;
if (isnan(maxval) || paintval > maxval)
maxval = paintval;
}
im->gdes[ii].p_data[i] = DNAN;
}
break;
+ case GF_STACK:
+ rrd_set_error("STACK should already be turned into LINE or AREA here");
+ return -1;
+ break;
default:
break;
}
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;
}
{
long i,ii,validsteps;
double printval;
- time_t printtime;
+ struct tm tmvdef;
int graphelement = 0;
long vidx;
int max_ii;
char *si_symb = "";
char *percent_s;
int prlines = 1;
+ /* wow initializing tmvdef is quite a task :-) */
+ time_t now = time(NULL);
+ localtime_r(&now,&tmvdef);
if (im->imginfo) prlines++;
for(i=0;i<im->gdes_c;i++){
switch(im->gdes[i].gf){
vidx = im->gdes[i].vidx;
if (im->gdes[vidx].gf==GF_VDEF) { /* simply use vals */
printval = im->gdes[vidx].vf.val;
- printtime = im->gdes[vidx].vf.when;
+ localtime_r(&im->gdes[vidx].vf.when,&tmvdef);
} else { /* need to calculate max,min,avg etcetera */
max_ii =((im->gdes[vidx].end
- im->gdes[vidx].start)
}
} /* prepare printval */
- if (!strcmp(im->gdes[i].format,"%c")) { /* VDEF time print */
- char ctime_buf[128]; /* PS: for ctime_r, must be >= 26 chars */
- int iii=0;
- ctime_r(&printtime,ctime_buf);
- while(isprint(ctime_buf[iii])){iii++;}
- ctime_buf[iii]='\0';
- if (im->gdes[i].gf == GF_PRINT){
- (*prdata)[prlines-2] = malloc((FMT_LEG_LEN+2)*sizeof(char));
- sprintf((*prdata)[prlines-2],"%s (%lu)",ctime_buf,printtime);
- (*prdata)[prlines-1] = NULL;
- } else {
- sprintf(im->gdes[i].legend,"%s (%lu)",ctime_buf,printtime);
- graphelement = 1;
- }
- } else {
if ((percent_s = strstr(im->gdes[i].format,"%S")) != NULL) {
/* Magfact is set to -1 upon entry to print_calc. If it
* is still less than 0, then we need to run auto_scale.
auto_scale(im,&printval,&si_symb,&magfact);
}
- if (im->gdes[i].gf == GF_PRINT){
+ if (im->gdes[i].gf == GF_PRINT){
(*prdata)[prlines-2] = malloc((FMT_LEG_LEN+2)*sizeof(char));
(*prdata)[prlines-1] = NULL;
- if (bad_format(im->gdes[i].format)) {
- rrd_set_error("bad format for PRINT in '%s'", im->gdes[i].format);
+ if (im->gdes[i].strftm){
+ strftime((*prdata)[prlines-2],FMT_LEG_LEN,im->gdes[i].format,&tmvdef);
+ } else {
+ if (bad_format(im->gdes[i].format)) {
+ rrd_set_error("bad format for PRINT in '%s'", im->gdes[i].format);
return -1;
- }
+ }
+
#ifdef HAVE_SNPRINTF
- snprintf((*prdata)[prlines-2],FMT_LEG_LEN,im->gdes[i].format,printval,si_symb);
+ snprintf((*prdata)[prlines-2],FMT_LEG_LEN,im->gdes[i].format,printval,si_symb);
#else
- sprintf((*prdata)[prlines-2],im->gdes[i].format,printval,si_symb);
+ sprintf((*prdata)[prlines-2],im->gdes[i].format,printval,si_symb);
#endif
- } else {
+ }
+ } else {
/* GF_GPRINT */
- if (bad_format(im->gdes[i].format)) {
+ if (im->gdes[i].strftm){
+ strftime(im->gdes[i].legend,FMT_LEG_LEN,im->gdes[i].format,&tmvdef);
+ } else {
+ if (bad_format(im->gdes[i].format)) {
rrd_set_error("bad format for GPRINT in '%s'", im->gdes[i].format);
return -1;
- }
+ }
#ifdef HAVE_SNPRINTF
- snprintf(im->gdes[i].legend,FMT_LEG_LEN-2,im->gdes[i].format,printval,si_symb);
+ snprintf(im->gdes[i].legend,FMT_LEG_LEN-2,im->gdes[i].format,printval,si_symb);
#else
- sprintf(im->gdes[i].legend,im->gdes[i].format,printval,si_symb);
+ sprintf(im->gdes[i].legend,im->gdes[i].format,printval,si_symb);
#endif
- graphelement = 1;
- }
- }
+ }
+ graphelement = 1;
+ }
break;
case GF_LINE:
case GF_AREA:
case GF_TICK:
- case GF_STACK:
+ graphelement = 1;
+ break;
case GF_HRULE:
+ if(isnan(im->gdes[i].yrule)) { /* we must set this here or the legend printer can not decide to print the legend */
+ im->gdes[i].yrule=im->gdes[im->gdes[i].vidx].vf.val;
+ };
+ graphelement = 1;
+ break;
case GF_VRULE:
+ if(im->gdes[i].xrule == 0) { /* again ... the legend printer needs it*/
+ im->gdes[i].xrule = im->gdes[im->gdes[i].vidx].vf.when;
+ };
graphelement = 1;
break;
case GF_COMMENT:
case GF_SHIFT:
case GF_XPORT:
break;
+ case GF_STACK:
+ rrd_set_error("STACK should already be turned into LINE or AREA here");
+ return -1;
+ break;
}
}
return graphelement;
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;
} else {
prt_fctn = '\0';
}
+ /* only valid control codes */
+ if (prt_fctn != 'l' &&
+ prt_fctn != 'r' &&
+ prt_fctn != 'j' &&
+ prt_fctn != 't' &&
+ prt_fctn != '\0' &&
+ prt_fctn != 'g' ) {
+ free(legspace);
+ rrd_set_error("Uknown control code at the end of '%s\\%c'",im->gdes[i].legend,prt_fctn);
+ return -1;
+
+ }
/* remove exess space */
while (prt_fctn=='g' &&
leg_cc > 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;
double range;
double scaledrange;
int pixel,i;
- int gridind;
+ int gridind=0;
int decimals, fractionals;
im->ygrid_scale.labfact=2;
- gridind=-1;
range = im->maxval - im->minval;
scaledrange = range / im->magfact;
else {
for(i=0;ylab[i].grid > 0;i++){
pixel = im->ysize / (scaledrange / ylab[i].grid);
- if (pixel > 7) {
- gridind = i;
- break;
- }
+ gridind = i;
+ if (pixel > 7)
+ break;
}
for(i=0; i<4;i++) {
if (pixel * ylab[gridind].lfac[i] >= 2.5 * im->text_prop[TEXT_PROP_AXIS].size) {
- im->ygrid_scale.labfact = ylab[gridind].lfac[i];
+ im->ygrid_scale.labfact = ylab[gridind].lfac[i];
break;
- }
+ }
}
im->ygrid_scale.gridstep = ylab[gridind].grid * im->magfact;
int i;
double scaledstep;
char graph_label[100];
+ int nlabels=0;
double X0=im->xorigin;
double X1=im->xorigin+im->xsize;
MaxY = scaledstep*(double)egrid;
for (i = sgrid; i <= egrid; i++){
double Y0=ytr(im,im->ygrid_scale.gridstep*i);
+ double YN=ytr(im,im->ygrid_scale.gridstep*(i+1));
if ( Y0 >= im->yorigin-im->ysize
&& Y0 <= im->yorigin){
- if(i % im->ygrid_scale.labfact == 0){
+ /* Make sure at least 2 grid labels are shown, even if it doesn't agree
+ with the chosen settings. Add a label if required by settings, or if
+ there is only one label so far and the next grid line is out of bounds. */
+ if(i % im->ygrid_scale.labfact == 0 || ( nlabels==1 && (YN < im->yorigin-im->ysize || YN > im->yorigin) )){
if (im->symbol == ' ') {
if(im->extra_flags & ALTYGRID) {
sprintf(graph_label,im->ygrid_scale.labfmt,scaledstep*(double)i);
}
}
}
+ nlabels++;
gfx_new_text ( im->canvas,
X0-im->text_prop[TEXT_PROP_AXIS].size, Y0,
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;
+}
+
+static int AlmostEqual2sComplement (float A, float B, int maxUlps)
+{
+
+ int aInt = *(int*)&A;
+ int bInt = *(int*)&B;
+ int intDiff;
+ /* Make sure maxUlps is non-negative and small enough that the
+ default NAN won't compare as equal to anything. */
+
+ /* assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024); */
+
+ /* Make aInt lexicographically ordered as a twos-complement int */
+
+ if (aInt < 0)
+ aInt = 0x80000000l - aInt;
+
+ /* Make bInt lexicographically ordered as a twos-complement int */
+
+ if (bInt < 0)
+ bInt = 0x80000000l - bInt;
+
+ intDiff = abs(aInt - bInt);
+
+ if (intDiff <= maxUlps)
+ return 1;
+
+ return 0;
+}
+
/* 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.},
+ {0,0,0,0,0, 0,0,0,0,0} /* last line */ };
+
+ 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, pre_value;
+ 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 && yloglab[mid][0] > 0);
+ if(mid) mid--;
+
+ /* find first value in yloglab */
+ for(flab = 0; yloglab[mid][flab] < 10 && 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 */
+ pre_value = DNAN;
+ while(1) {
+
+ value = yloglab[mid][flab] * pow(10.0, val_exp);
+ if ( AlmostEqual2sComplement(value,pre_value,4) ) break; /* it seems we are not converging */
+
+ pre_value = value;
+
+ 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);
}
}
- pixperstep = pixpex * minstep;
- if(pixperstep > 5){minoridx = i;}
- if(pixperstep > 2 * im->text_prop[TEXT_PROP_LEGEND].size){majoridx = i;}
+ 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;
+ }
}
-
- 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;
}
5.5, im->tabwidth, 270,
GFX_H_RIGHT, GFX_V_TOP,
"RRDTOOL / TOBI OETIKER");
+
+ /* graph watermark */
+ if(im->watermark[0] != '\0') {
+ gfx_new_text( im->canvas,
+ im->ximg/2, im->yimg-6,
+ ( im->graph_col[GRC_FONT] & 0xffffff00 ) | 0x00000044,
+ im->text_prop[TEXT_PROP_AXIS].font,
+ 5.5, im->tabwidth, 0,
+ GFX_H_CENTER, GFX_V_BOTTOM,
+ im->watermark);
+ }
/* graph labels */
if( !(im->extra_flags & NOLEGEND) & !(im->extra_flags & ONLY_GRAPH) ) {
** |v+--+-------------------------------+--------+
** | |..............legends......................|
** +-+-------------------------------------------+
+ ** | watermark |
+ ** +---------------------------------------------+
*/
int Xvertical=0,
Ytitle =0,
#if 0
Xlegend =0, Ylegend =0,
#endif
- Xspacing =15, Yspacing =15;
+ Xspacing =15, Yspacing =15,
+
+ Ywatermark =4;
if (im->extra_flags & ONLY_GRAPH) {
im->xorigin =0;
xtr(im,0);
/* The vertical size is interesting... we need to compare
- ** the sum of {Ytitle, Ymain, Yxlabel, Ylegend} with Yvertical
- ** however we need to know {Ytitle+Ymain+Yxlabel} in order to
- ** start even thinking about Ylegend.
+ ** the sum of {Ytitle, Ymain, Yxlabel, Ylegend, Ywatermark} with
+ ** Yvertical however we need to know {Ytitle+Ymain+Yxlabel}
+ ** in order to start even thinking about Ylegend or Ywatermark.
**
** Do it in three portions: First calculate the inner part,
- ** then do the legend, then adjust the total height of the img.
+ ** then do the legend, then adjust the total height of the img,
+ ** adding space for a watermark if one exists;
*/
/* reserve space for main and/or pie */
*/
if(leg_place(im)==-1)
return -1;
-
+
+ if (im->watermark[0] != '\0') {
+ im->yimg += Ywatermark;
+ }
#if 0
if (Xlegend > im->ximg) {
/* yes we are loosing precision by doing tos with floats instead of doubles
but it seems more stable this way. */
-static int AlmostEqual2sComplement (float A, float B, int maxUlps)
-{
-
- int aInt = *(int*)&A;
- int bInt = *(int*)&B;
- int intDiff;
- /* Make sure maxUlps is non-negative and small enough that the
- default NAN won't compare as equal to anything. */
-
- /* assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024); */
-
- /* Make aInt lexicographically ordered as a twos-complement int */
-
- if (aInt < 0)
- aInt = 0x80000000l - aInt;
-
- /* Make bInt lexicographically ordered as a twos-complement int */
-
- if (bInt < 0)
- bInt = 0x80000000l - bInt;
-
- intDiff = abs(aInt - bInt);
-
- if (intDiff <= maxUlps)
- return 1;
-
- return 0;
-}
/* draw that picture thing ... */
int
gfx_node_t *node;
double areazero = 0.0;
- enum gf_en stack_gf = GF_PRINT;
graph_desc_t *lastgdes = NULL;
/* if we are lazy and there is nothing to PRINT ... quit now */
for (ii = 0; ii < im->xsize; ii++)
{
if (!isnan(im->gdes[i].p_data[ii]) &&
- im->gdes[i].p_data[ii] > 0.0)
- {
- /* generate a tick */
- gfx_new_line(im->canvas, im -> xorigin + ii,
- im -> yorigin - (im -> gdes[i].yrule * im -> ysize),
- im -> xorigin + ii,
- im -> yorigin,
- 1.0,
- im -> gdes[i].col );
- }
+ im->gdes[i].p_data[ii] != 0.0)
+ {
+ if (im -> gdes[i].yrule > 0 ) {
+ gfx_new_line(im->canvas,
+ im -> xorigin + ii, im->yorigin,
+ im -> xorigin + ii, im->yorigin - im -> gdes[i].yrule * im -> ysize,
+ 1.0,
+ im -> gdes[i].col );
+ } else if ( im -> gdes[i].yrule < 0 ) {
+ gfx_new_line(im->canvas,
+ im -> xorigin + ii, im->yorigin - im -> ysize,
+ im -> xorigin + ii, im->yorigin - ( 1 - im -> gdes[i].yrule ) * im -> ysize,
+ 1.0,
+ im -> gdes[i].col );
+
+ }
+ }
}
break;
case GF_LINE:
case GF_AREA:
- stack_gf = im->gdes[i].gf;
- case GF_STACK:
/* fix data points at oo and -oo */
for(ii=0;ii<im->xsize;ii++){
if (isinf(im->gdes[i].p_data[ii])){
********************************************************* */
if (im->gdes[i].col != 0x0){
/* GF_LINE and friend */
- if(stack_gf == GF_LINE ){
+ if(im->gdes[i].gf == GF_LINE ){
double last_y=0.0;
node = NULL;
for(ii=1;ii<im->xsize;ii++){
}
break;
#endif
+ case GF_STACK:
+ rrd_set_error("STACK should already be turned into LINE or AREA here");
+ return -1;
+ break;
} /* switch */
}
switch(im->gdes[i].gf){
case GF_HRULE:
- if(isnan(im->gdes[i].yrule)) { /* fetch variable */
- im->gdes[i].yrule = im->gdes[im->gdes[i].vidx].vf.val;
- };
if(im->gdes[i].yrule >= im->minval
&& im->gdes[i].yrule <= im->maxval)
gfx_new_line(im->canvas,
1.0,im->gdes[i].col);
break;
case GF_VRULE:
- if(im->gdes[i].xrule == 0) { /* fetch variable */
- im->gdes[i].xrule = im->gdes[im->gdes[i].vidx].vf.when;
- };
if(im->gdes[i].xrule >= im->start
&& im->gdes[i].xrule <= im->end)
gfx_new_line(im->canvas,
im->gdes[im->gdes_c-1].step=im->step;
+ im->gdes[im->gdes_c-1].step_orig=im->step;
im->gdes[im->gdes_c-1].stack=0;
+ im->gdes[im->gdes_c-1].linewidth=0;
im->gdes[im->gdes_c-1].debug=0;
im->gdes[im->gdes_c-1].start=im->start;
+ im->gdes[im->gdes_c-1].start_orig=im->start;
im->gdes[im->gdes_c-1].end=im->end;
+ im->gdes[im->gdes_c-1].end_orig=im->end;
im->gdes[im->gdes_c-1].vname[0]='\0';
im->gdes[im->gdes_c-1].data=NULL;
im->gdes[im->gdes_c-1].ds_namv=NULL;
im->gdes[im->gdes_c-1].col = 0x0;
im->gdes[im->gdes_c-1].legend[0]='\0';
im->gdes[im->gdes_c-1].format[0]='\0';
+ im->gdes[im->gdes_c-1].strftm=0;
im->gdes[im->gdes_c-1].rrd[0]='\0';
im->gdes[im->gdes_c-1].ds=-1;
+ im->gdes[im->gdes_c-1].cf_reduce=CF_AVERAGE;
+ im->gdes[im->gdes_c-1].cf=CF_AVERAGE;
im->gdes[im->gdes_c-1].p_data=NULL;
im->gdes[im->gdes_c-1].yrule=DNAN;
im->gdes[im->gdes_c-1].xrule=0;
/* copies input untill the first unescaped colon is found
or until input ends. backslashes have to be escaped as well */
int
-scan_for_col(char *input, int len, char *output)
+scan_for_col(const char *const input, int len, char *const output)
{
int inp,outp=0;
for (inp=0;
#endif
#ifdef HAVE_SETLOCALE
setlocale(LC_TIME,"");
+#ifdef HAVE_MBSTOWCS
+ setlocale(LC_CTYPE,"");
+#endif
#endif
im->yorigin=0;
im->xorigin=0;
im->xsize = 400;
im->ysize = 100;
im->step = 0;
- im->step_orig = 0;
im->ylegend[0] = '\0';
im->title[0] = '\0';
+ im->watermark[0] = '\0';
im->minval = DNAN;
im->maxval = DNAN;
im->unitsexponent= 9999;
windir = getenv("windir");
/* %windir% is something like D:\windows or C:\winnt */
if (windir != NULL) {
- strncpy(rrd_win_default_font,windir,999);
- rrd_win_default_font[999] = '\0';
+ strncpy(rrd_win_default_font,windir,500);
+ rrd_win_default_font[500] = '\0';
strcat(rrd_win_default_font,"\\fonts\\");
strcat(rrd_win_default_font,RRD_DEFAULT_FONT);
for(i=0;i<DIM(text_prop);i++){
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'},
{"font-smoothing-threshold", required_argument, 0, 'B'},
+ {"watermark", required_argument, 0, 'W'},
{"alt-y-mrtg", no_argument, 0, 1000}, /* this has no effect it is just here to save old apps from crashing when they use it */
{0,0,0,0}};
int option_index = 0;
int col_start,col_end;
opt = getopt_long(argc, argv,
- "s:e:x:y:v:w:h:iu:l:rb:oc:n:m:t:f:a:I:zgjFYAMEX:L:S:T:NR:B:",
+ "s:e:x:y:v:w:h:iu:l:rb:oc:n:m:t:f:a:I:zgjFYAMEX:L:S:T:NR:B:W:",
long_options, &option_index);
if (opt == EOF)
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,
im->canvas->font_aa_threshold = atof(optarg);
break;
+ case 'W':
+ strncpy(im->watermark,optarg,100);
+ im->watermark[99]='\0';
+ break;
+
case '?':
if (optopt != 0)
rrd_set_error("unknown option '%c'", optopt);
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;
}
/* '%s', '%S' and '%%' are allowed */
if (*ptr == 's' || *ptr == 'S' || *ptr == '%') ptr++;
+ /* %c is allowed (but use only with vdef!) */
+ else if (*ptr == 'c') {
+ ptr++;
+ n=1;
+ }
+
/* or else '% 6.2lf' and such are allowed */
else {
-
/* optional padding character */
if (*ptr == ' ' || *ptr == '+' || *ptr == '-') ptr++;
-
+
/* This should take care of 'm.n' with all three optional */
while (*ptr >= '0' && *ptr <= '9') ptr++;
if (*ptr == '.') ptr++;
int
vdef_parse(gdes,str)
struct graph_desc_t *gdes;
-char *str;
+const char *const str;
{
/* A VDEF currently is either "func" or "param,func"
* so the parsing is rather simple. Change if needed.
if (cnt) {
if (dst->vf.op == VDEF_TOTAL) {
dst->vf.val = sum*src->step;
- dst->vf.when = cnt*src->step; /* not really "when" */
+ dst->vf.when = 0; /* no time component */
} else {
dst->vf.val = sum/cnt;
dst->vf.when = 0; /* no time component */
if (cnt) {
if (dst->vf.op == VDEF_LSLSLOPE) {
dst->vf.val = slope;
- dst->vf.when = cnt*src->step;
+ dst->vf.when = 0;
} else if (dst->vf.op == VDEF_LSLINT) {
dst->vf.val = y_intercept;
- dst->vf.when = cnt*src->step;
+ dst->vf.when = 0;
} else if (dst->vf.op == VDEF_LSLCORREL) {
dst->vf.val = correl;
- dst->vf.when = cnt*src->step;
+ dst->vf.when = 0;
};
} else {
dst->vf.val = DNAN;
dst->vf.when = 0;
}
- }
- break;
+ }
+ break;
}
return 0;
}