/****************************************************************************
- * RRDtool 1.2rc8 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 "rrd_tool.h"
-#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
#include <io.h>
#include <fcntl.h>
#endif
#ifndef RRD_DEFAULT_FONT
/* there is special code later to pick Cour.ttf when running on windows */
-#define RRD_DEFAULT_FONT "VeraMono.ttf"
+#define RRD_DEFAULT_FONT "DejaVuSansMono-Roman.ttf"
#endif
text_prop_t text_prop[] = {
};
xlab_t xlab[] = {
- {0, TMT_SECOND,30, TMT_MINUTE,5, TMT_MINUTE,5, 0,"%H:%M"},
- {2, TMT_MINUTE,1, TMT_MINUTE,5, TMT_MINUTE,5, 0,"%H:%M"},
- {5, TMT_MINUTE,2, TMT_MINUTE,10, TMT_MINUTE,10, 0,"%H:%M"},
- {10, TMT_MINUTE,5, TMT_MINUTE,20, TMT_MINUTE,20, 0,"%H:%M"},
- {30, TMT_MINUTE,10, TMT_HOUR,1, TMT_HOUR,1, 0,"%H:%M"},
- {60, TMT_MINUTE,30, TMT_HOUR,2, TMT_HOUR,2, 0,"%H:%M"},
- {180, TMT_HOUR,1, TMT_HOUR,6, TMT_HOUR,6, 0,"%H:%M"},
- /*{300, TMT_HOUR,3, TMT_HOUR,12, TMT_HOUR,12, 12*3600,"%a %p"}, this looks silly*/
- {600, TMT_HOUR,6, TMT_DAY,1, TMT_DAY,1, 24*3600,"%a"},
- {1800, TMT_HOUR,12, TMT_DAY,1, TMT_DAY,2, 24*3600,"%a"},
- {3600, TMT_DAY,1, TMT_WEEK,1, TMT_WEEK,1, 7*24*3600,"Week %V"},
- {3*3600, TMT_WEEK,1, TMT_MONTH,1, TMT_WEEK,2, 7*24*3600,"Week %V"},
- {6*3600, TMT_MONTH,1, TMT_MONTH,1, TMT_MONTH,1, 30*24*3600,"%b"},
- {48*3600, TMT_MONTH,1, TMT_MONTH,3, TMT_MONTH,3, 30*24*3600,"%b"},
- {10*24*3600, TMT_YEAR,1, TMT_YEAR,1, TMT_YEAR,1, 365*24*3600,"%y"},
- {-1,TMT_MONTH,0,TMT_MONTH,0,TMT_MONTH,0,0,""}
+ {0, 0, TMT_SECOND,30, TMT_MINUTE,5, TMT_MINUTE,5, 0,"%H:%M"},
+ {2, 0, TMT_MINUTE,1, TMT_MINUTE,5, TMT_MINUTE,5, 0,"%H:%M"},
+ {5, 0, TMT_MINUTE,2, TMT_MINUTE,10, TMT_MINUTE,10, 0,"%H:%M"},
+ {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, 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"},
+ {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"},
+ {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[]= {
0xE0505080, /* major grid */
0x000000FF, /* font */
0x802020FF, /* arrow */
- 0x202020FF /* axis */
-};
+ 0x202020FF, /* axis */
+ 0x000000FF /* frame */
+};
/* #define DEBUG */
if (! im->rigid) {
/* keep yval as-is */
} else if (yval > im->yorigin) {
- yval = im->yorigin+2;
+ yval = im->yorigin +0.00001;
} else if (yval < im->yorigin - im->ysize){
- yval = im->yorigin - im->ysize - 2;
+ yval = im->yorigin - im->ysize - 0.00001;
}
return yval;
}
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)
conv_if(FONT,GRC_FONT)
conv_if(ARROW,GRC_ARROW)
conv_if(AXIS,GRC_AXIS)
+ conv_if(FRAME,GRC_FRAME)
return -1;
}
}
-/* 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);
- 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 = '?';
}
delt = im->maxval - im->minval;
adj = delt * 0.1;
fact = 2.0 * pow(10.0,
- floor(log10(max(fabs(im->minval), fabs(im->maxval)))) - 2);
+ floor(log10(max(fabs(im->minval), fabs(im->maxval))/im->magfact)) - 2);
if (delt < fact) {
adj = (fact - delt) * 0.55;
#ifdef DEBUG
-sensiblevalues[i] >=scaled_max)
im->maxval = -sensiblevalues[i]*(im->magfact);
}
- /* no sensiblevalues found. we switch to ALTYGRID mode */
- if (sensiblevalues[i] == 0){
- im->extra_flags |= ALTYGRID;
- }
}
} else {
/* adjust min and max to the grid definition if there is one */
double new_log10_range = factor * log10_range;
double new_ymax_log10 = log10(im->minval) + new_log10_range;
im->maxval = pow(10, new_ymax_log10);
- ytr(im, DNAN); /* reset precalc */
+ ytr(im,DNAN); /* reset precalc */
log10_range = log10(im->maxval) - log10(im->minval);
}
/* make sure first y=10^x gridline is located on
double yfrac = ypixfrac / im->ysize;
im->minval = pow(10, log10(im->minval) - yfrac * log10_range);
im->maxval = pow(10, log10(im->maxval) - yfrac * log10_range);
- ytr(im, DNAN); /* reset precalc */
+ ytr(im,DNAN); /* reset precalc */
}
} else {
/* Make sure we have an integer pixel distance between
double gridstep = im->ygrid_scale.gridstep;
double minor_y, minor_y_px, minor_y_px_frac;
im->maxval = im->minval + new_range;
- ytr(im, DNAN); /* reset precalc */
+ ytr(im,DNAN); /* reset precalc */
/* make sure first minor gridline is on integer pixel y coord */
minor_y = gridstep * floor(im->minval / gridstep);
while (minor_y < im->minval)
double range = im->maxval - im->minval;
im->minval = im->minval - yfrac * range;
im->maxval = im->maxval - yfrac * range;
- ytr(im, DNAN); /* reset precalc */
+ ytr(im,DNAN); /* reset precalc */
}
calc_horizontal_grid(im); /* recalc with changed im->maxval */
}
int i,ii;
int skip;
- /* pull the data from the log files ... */
+ /* pull the data from the rrd files ... */
for (i=0;i< (int)im->gdes_c;i++){
/* only GF_DEF elements fetch data */
if (im->gdes[i].gf != GF_DEF)
if ((strcmp(im->gdes[i].rrd, im->gdes[ii].rrd) == 0)
&& (im->gdes[i].cf == im->gdes[ii].cf)
&& (im->gdes[i].cf_reduce == im->gdes[ii].cf_reduce)
- && (im->gdes[i].start == im->gdes[ii].start)
- && (im->gdes[i].end == im->gdes[ii].end)
- && (im->gdes[i].step == im->gdes[ii].step)) {
+ && (im->gdes[i].start_orig == im->gdes[ii].start_orig)
+ && (im->gdes[i].end_orig == im->gdes[ii].end_orig)
+ && (im->gdes[i].step_orig == im->gdes[ii].step_orig)) {
/* OK, the data is already there.
** Just copy the header portion
*/
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,
* and the resulting number is the step size for the
* resulting data source.
*/
- for(rpi=0;im->gdes[gdi].rpnp[rpi].op != OP_END;rpi++){
- if(im->gdes[gdi].rpnp[rpi].op == OP_VARIABLE ||
- im->gdes[gdi].rpnp[rpi].op == OP_PREV_OTHER){
- long ptr = im->gdes[gdi].rpnp[rpi].ptr;
- if (im->gdes[ptr].ds_cnt == 0) {
+ for(rpi=0;im->gdes[gdi].rpnp[rpi].op != OP_END;rpi++){
+ if(im->gdes[gdi].rpnp[rpi].op == OP_VARIABLE ||
+ im->gdes[gdi].rpnp[rpi].op == OP_PREV_OTHER){
+ long ptr = im->gdes[gdi].rpnp[rpi].ptr;
+ if (im->gdes[ptr].ds_cnt == 0) { /* this is a VDEF data source */
#if 0
- printf("DEBUG: inside CDEF '%s' processing VDEF '%s'\n",
- im->gdes[gdi].vname,
- im->gdes[ptr].vname);
- printf("DEBUG: value from vdef is %f\n",im->gdes[ptr].vf.val);
+ printf("DEBUG: inside CDEF '%s' processing VDEF '%s'\n",
+ im->gdes[gdi].vname,
+ im->gdes[ptr].vname);
+ printf("DEBUG: value from vdef is %f\n",im->gdes[ptr].vf.val);
#endif
im->gdes[gdi].rpnp[rpi].val = im->gdes[ptr].vf.val;
im->gdes[gdi].rpnp[rpi].op = OP_NUMBER;
- } else {
- if ((steparray =
+ } else { /* normal variables and PREF(variables) */
+
+ /* add one entry to the array that keeps track of the step sizes of the
+ * data sources going into the CDEF. */
+ if ((steparray =
rrd_realloc(steparray,
(++stepcnt+1)*sizeof(*steparray)))==NULL){
- rrd_set_error("realloc steparray");
- rpnstack_free(&rpnstack);
- return -1;
+ rrd_set_error("realloc steparray");
+ rpnstack_free(&rpnstack);
+ return -1;
};
steparray[stepcnt-1] = im->gdes[ptr].step;
* to the earliest endpoint of any of the
* rras involved (ptr)
*/
+
if(im->gdes[gdi].start < im->gdes[ptr].start)
im->gdes[gdi].start = im->gdes[ptr].start;
* further save step size and data source
* count of this rra
*/
- im->gdes[gdi].rpnp[rpi].data = im->gdes[ptr].data + im->gdes[ptr].ds;
- im->gdes[gdi].rpnp[rpi].step = im->gdes[ptr].step;
+ im->gdes[gdi].rpnp[rpi].data = im->gdes[ptr].data + im->gdes[ptr].ds;
+ im->gdes[gdi].rpnp[rpi].step = im->gdes[ptr].step;
im->gdes[gdi].rpnp[rpi].ds_cnt = im->gdes[ptr].ds_cnt;
/* backoff the *.data ptr; this is done so
/* move the data pointers to the correct period */
for(rpi=0;im->gdes[gdi].rpnp[rpi].op != OP_END;rpi++){
- if(im->gdes[gdi].rpnp[rpi].op == OP_VARIABLE ||
- im->gdes[gdi].rpnp[rpi].op == OP_PREV_OTHER){
- long ptr = im->gdes[gdi].rpnp[rpi].ptr;
+ if(im->gdes[gdi].rpnp[rpi].op == OP_VARIABLE ||
+ im->gdes[gdi].rpnp[rpi].op == OP_PREV_OTHER){
+ long ptr = im->gdes[gdi].rpnp[rpi].ptr;
long diff = im->gdes[gdi].start - im->gdes[ptr].start;
if(diff > 0)
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
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;
+ 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;
if(isnan(im->ygridstep)){
if(im->extra_flags & ALTYGRID) {
/* find the value with max number of digits. Get number of digits */
- decimals = ceil(log10(max(fabs(im->maxval), fabs(im->minval))));
+ decimals = ceil(log10(max(fabs(im->maxval), fabs(im->minval))*im->viewfactor/im->magfact));
if(decimals <= 0) /* everything is small. make place for zero */
decimals = 1;
- fractionals = floor(log10(range));
- if(fractionals < 0) { /* small amplitude. */
- int len = decimals - fractionals + 1;
- if (im->unitslength < len) im->unitslength = len;
- sprintf(im->ygrid_scale.labfmt, "%%%d.%df", len, -fractionals + 1);
- } else {
- int len = decimals + 1;
- if (im->unitslength < len) im->unitslength = len;
- sprintf(im->ygrid_scale.labfmt, "%%%d.1f", len);
- }
- im->ygrid_scale.gridstep = pow((double)10, (double)fractionals);
+ im->ygrid_scale.gridstep = pow((double)10, floor(log10(range*im->viewfactor/im->magfact)))/im->viewfactor*im->magfact;
+
if(im->ygrid_scale.gridstep == 0) /* range is one -> 0.1 is reasonable scale */
im->ygrid_scale.gridstep = 0.1;
/* should have at least 5 lines but no more then 15 */
im->ygrid_scale.gridstep /= 5;
im->ygrid_scale.labfact = 5;
}
+ fractionals = floor(log10(im->ygrid_scale.gridstep*(double)im->ygrid_scale.labfact*im->viewfactor/im->magfact));
+ if(fractionals < 0) { /* small amplitude. */
+ int len = decimals - fractionals + 1;
+ if (im->unitslength < len+2) im->unitslength = len+2;
+ sprintf(im->ygrid_scale.labfmt, "%%%d.%df%s", len, -fractionals,(im->symbol != ' ' ? " %c" : ""));
+ } else {
+ int len = decimals + 1;
+ if (im->unitslength < len+2) im->unitslength = len+2;
+ sprintf(im->ygrid_scale.labfmt, "%%%d.0f%s", len, ( im->symbol != ' ' ? " %c" : "" ));
+ }
}
else {
for(i=0;ylab[i].grid > 0;i++){
pixel = im->ysize / (scaledrange / ylab[i].grid);
- if (pixel > 5) {
- gridind = i;
- break;
- }
+ gridind = i;
+ if (pixel > 7)
+ break;
}
for(i=0; i<4;i++) {
- if (pixel * ylab[gridind].lfac[i] >= 2 * im->text_prop[TEXT_PROP_AXIS].size) {
- im->ygrid_scale.labfact = ylab[gridind].lfac[i];
+ if (pixel * ylab[gridind].lfac[i] >= 2.5 * im->text_prop[TEXT_PROP_AXIS].size) {
+ 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;
int sgrid = (int)( im->minval / im->ygrid_scale.gridstep - 1);
int egrid = (int)( im->maxval / im->ygrid_scale.gridstep + 1);
- scaledstep = im->ygrid_scale.gridstep/im->magfact;
+ double MaxY;
+ scaledstep = im->ygrid_scale.gridstep/(double)im->magfact*(double)im->viewfactor;
+ 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){
- if (i==0 || im->symbol == ' ') {
- if(scaledstep < 1){
- if(im->extra_flags & ALTYGRID) {
- sprintf(graph_label,im->ygrid_scale.labfmt,scaledstep*im->viewfactor*i);
- }
- else {
- sprintf(graph_label,"%4.1f",scaledstep*im->viewfactor*i);
- }
- } else {
- sprintf(graph_label,"%4.0f",scaledstep*im->viewfactor*i);
- }
+ /* 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);
+ } else {
+ if(MaxY < 10) {
+ sprintf(graph_label,"%4.1f",scaledstep*(double)i);
+ } else {
+ sprintf(graph_label,"%4.0f",scaledstep*(double)i);
+ }
+ }
}else {
- if(scaledstep < 1){
- sprintf(graph_label,"%4.1f %c",scaledstep*im->viewfactor*i, im->symbol);
- } else {
- sprintf(graph_label,"%4.0f %c",scaledstep*im->viewfactor*i, im->symbol);
- }
+ char sisym = ( i == 0 ? ' ' : im->symbol);
+ if(im->extra_flags & ALTYGRID) {
+ sprintf(graph_label,im->ygrid_scale.labfmt,scaledstep*(double)i,sisym);
+ } else {
+ if(MaxY < 10){
+ sprintf(graph_label,"%4.1f %c",scaledstep*(double)i, sisym);
+ } else {
+ sprintf(graph_label,"%4.0f %c",scaledstep*(double)i, sisym);
+ }
+ }
}
+ 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;
+}
+
/* 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;
}
factor=(im->end - im->start)/im->xsize;
xlab_sel=0;
while ( xlab[xlab_sel+1].minsec != -1
- && xlab[xlab_sel+1].minsec <= factor){ xlab_sel++; }
+ && xlab[xlab_sel+1].minsec <= factor) { xlab_sel++; } /* pick the last one */
+ while ( xlab[xlab_sel-1].minsec == xlab[xlab_sel].minsec
+ && xlab[xlab_sel].length > (im->end - im->start)) { xlab_sel--; } /* go back to the smallest size */
im->xlab_user.gridtm = xlab[xlab_sel].gridtm;
im->xlab_user.gridst = xlab[xlab_sel].gridst;
im->xlab_user.mgridtm = xlab[xlab_sel].mgridtm;
# error "your libc has no strftime I guess we'll abort the exercise here."
#endif
gfx_new_text ( im->canvas,
- xtr(im,tilab), Y0+im->text_prop[TEXT_PROP_AXIS].size,
+ xtr(im,tilab), Y0+im->text_prop[TEXT_PROP_AXIS].size*1.4+5,
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_CENTER, GFX_V_TOP,
+ im->tabwidth, 0.0, GFX_H_CENTER, GFX_V_BOTTOM,
graph_label );
}
} else {
res = draw_horizontal_grid(im);
}
-
+
/* dont draw horizontal grid if there is no min and max val */
if (! res ) {
char *nodata = "No Data found";
/* yaxis unit description */
gfx_new_text( im->canvas,
- 7, (im->yorigin - im->ysize/2),
+ 10, (im->yorigin - im->ysize/2),
im->graph_col[GRC_FONT],
im->text_prop[TEXT_PROP_UNIT].font,
im->text_prop[TEXT_PROP_UNIT].size, im->tabwidth,
/* graph title */
gfx_new_text( im->canvas,
- im->ximg/2, im->text_prop[TEXT_PROP_TITLE].size*1.2,
+ im->ximg/2, im->text_prop[TEXT_PROP_TITLE].size*1.3+4,
im->graph_col[GRC_FONT],
im->text_prop[TEXT_PROP_TITLE].font,
im->text_prop[TEXT_PROP_TITLE].size, im->tabwidth, 0.0,
GFX_H_CENTER, GFX_V_CENTER,
im->title);
+ /* rrdtool 'logo' */
+ gfx_new_text( im->canvas,
+ im->ximg-7, 7,
+ ( im->graph_col[GRC_FONT] & 0xffffff00 ) | 0x00000044,
+ im->text_prop[TEXT_PROP_AXIS].font,
+ 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) ) {
boxH = gfx_get_text_width(im->canvas, 0,
im->text_prop[TEXT_PROP_LEGEND].font,
im->text_prop[TEXT_PROP_LEGEND].size,
- im->tabwidth,"M", 0)*1.2;
- boxV = boxH;
+ im->tabwidth,"o", 0) * 1.2;
+ boxV = boxH*1.1;
- /* make sure transparent colors show up all the same */
- node = gfx_new_area(im->canvas,
+ /* make sure transparent colors show up the same way as in the graph */
+ node = gfx_new_area(im->canvas,
X0,Y0-boxV,
X0,Y0,
X0+boxH,Y0,
- im->graph_col[GRC_CANVAS]);
+ im->graph_col[GRC_BACK]);
gfx_add_point ( node, X0+boxH, Y0-boxV );
node = gfx_new_area(im->canvas,
im->gdes[i].col);
gfx_add_point ( node, X0+boxH, Y0-boxV );
node = gfx_new_line(im->canvas,
- X0,Y0-boxV, X0,Y0,
- 1,im->graph_col[GRC_FONT]);
+ X0,Y0-boxV,
+ X0,Y0,
+ 1.0,im->graph_col[GRC_FRAME]);
gfx_add_point(node,X0+boxH,Y0);
gfx_add_point(node,X0+boxH,Y0-boxV);
gfx_close_path(node);
** |v+--+-------------------------------+--------+
** | |..............legends......................|
** +-+-------------------------------------------+
+ ** | watermark |
+ ** +---------------------------------------------+
*/
- int Xvertical=0, Yvertical=0,
- Xtitle =0, Ytitle =0,
+ int Xvertical=0,
+ Ytitle =0,
Xylabel =0,
Xmain =0, Ymain =0,
#ifdef WITH_PIECHART
#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;
im->ximg = im->xsize;
im->yimg = im->ysize;
im->yorigin = im->ysize;
+ ytr(im,DNAN);
return 0;
}
if (im->ylegend[0] != '\0' ) {
Xvertical = im->text_prop[TEXT_PROP_UNIT].size *2;
- Yvertical = gfx_get_text_width(im->canvas, 0,
- im->text_prop[TEXT_PROP_UNIT].font,
- im->text_prop[TEXT_PROP_UNIT].size,
- im->tabwidth,im->ylegend, 0);
}
** automatically has some vertical spacing. The horizontal
** spacing is added here, on each side.
*/
- Xtitle = gfx_get_text_width(im->canvas, 0,
+ /* don't care for the with of the title
+ Xtitle = gfx_get_text_width(im->canvas, 0,
im->text_prop[TEXT_PROP_TITLE].font,
im->text_prop[TEXT_PROP_TITLE].size,
im->tabwidth,
- im->title, 0) + 2*Xspacing;
- Ytitle = im->text_prop[TEXT_PROP_TITLE].size*2.5;
+ im->title, 0) + 2*Xspacing; */
+ Ytitle = im->text_prop[TEXT_PROP_TITLE].size*2.6+10;
}
if (elements) {
im->text_prop[TEXT_PROP_AXIS].font,
im->text_prop[TEXT_PROP_AXIS].size,
im->tabwidth,
- "0", 0) * im->unitslength + im->text_prop[TEXT_PROP_AXIS].size * 2;
+ "0", 0) * im->unitslength;
}
}
im->xorigin = Xspacing + Xylabel;
- if (Xtitle > im->ximg) im->ximg = Xtitle;
+ /* the length of the title should not influence with width of the graph
+ if (Xtitle > im->ximg) im->ximg = Xtitle; */
if (Xvertical) { /* unit description */
im->ximg += Xvertical;
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 */
im->yimg = Ymain + Yxlabel;
-
+
#ifdef WITH_PIECHART
if (im->yimg < Ypie) im->yimg = Ypie;
#endif
im->yimg += Ytitle;
im->yorigin += Ytitle;
} else {
- im->yimg += Yspacing;
- im->yorigin += Yspacing;
+ im->yimg += 1.5*Yspacing;
+ im->yorigin += 1.5*Yspacing;
}
/* reserve space for padding below the graph */
im->yimg += Yspacing;
- ytr(im,DNAN);
-
+
/* Determine where to place the legends onto the image.
** Adjust im->yimg to match the space requirements.
*/
if(leg_place(im)==-1)
return -1;
-
- /* last of three steps: check total height of image */
- if (im->yimg < Yvertical) im->yimg = Yvertical;
+
+ if (im->watermark[0] != '\0') {
+ im->yimg += Ywatermark;
+ }
#if 0
if (Xlegend > im->ximg) {
}
#endif
+ ytr(im,DNAN);
+ return 0;
+}
+
+/* from http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm */
+/* 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;
}
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 */
node=gfx_new_area ( im->canvas,
0, 0,
- im->ximg, 0,
- im->ximg, im->yimg,
+ 0, im->yimg,
+ im->ximg, im->yimg,
im->graph_col[GRC_BACK]);
- gfx_add_point(node,0, im->yimg);
+ gfx_add_point(node,im->ximg, 0);
#ifdef WITH_PIECHART
if (piechart != 2) {
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])){
} /* for */
/* *******************************************************
- ___
- | | ___
+ a ___. (a,t)
+ | | ___
____| | | |
| |___|
- -------|---------------------------------------
+ -------|--t-1--t--------------------------------
- if we know the value of y at time t was a then
+ if we know the value at time t was a then
we draw a square from t-1 to t with the value a.
********************************************************* */
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++){
- if (isnan(im->gdes[i].p_data[ii])){
+ if (isnan(im->gdes[i].p_data[ii]) || (im->slopemode==1 && isnan(im->gdes[i].p_data[ii-1]))){
node = NULL;
continue;
}
if ( node == NULL ) {
- node = gfx_new_line(im->canvas,
- ii-1+im->xorigin,ytr(im,im->gdes[i].p_data[ii]),
- ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii]),
+ last_y = ytr(im,im->gdes[i].p_data[ii]);
+ if ( im->slopemode == 0 ){
+ node = gfx_new_line(im->canvas,
+ ii-1+im->xorigin,last_y,
+ ii+im->xorigin,last_y,
+ im->gdes[i].linewidth,
+ im->gdes[i].col);
+ } else {
+ node = gfx_new_line(im->canvas,
+ ii-1+im->xorigin,ytr(im,im->gdes[i].p_data[ii-1]),
+ ii+im->xorigin,last_y,
im->gdes[i].linewidth,
im->gdes[i].col);
+ }
} else {
- gfx_add_point(node,ii-1+im->xorigin,ytr(im,im->gdes[i].p_data[ii]));
- gfx_add_point(node,ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii]));
+ double new_y = ytr(im,im->gdes[i].p_data[ii]);
+ if ( im->slopemode==0 && ! AlmostEqual2sComplement(new_y,last_y,4)){
+ gfx_add_point(node,ii-1+im->xorigin,new_y);
+ };
+ last_y = new_y;
+ gfx_add_point(node,ii+im->xorigin,new_y);
};
}
} else {
- for(ii=1;ii<im->xsize;ii++){
+ int idxI=-1;
+ double *foreY=malloc(sizeof(double)*im->xsize*2);
+ double *foreX=malloc(sizeof(double)*im->xsize*2);
+ double *backY=malloc(sizeof(double)*im->xsize*2);
+ double *backX=malloc(sizeof(double)*im->xsize*2);
+ int drawem = 0;
+ for(ii=0;ii<=im->xsize;ii++){
+ double ybase,ytop;
+ if ( idxI > 0 && ( drawem != 0 || ii==im->xsize)){
+ int cntI=1;
+ int lastI=0;
+ while (cntI < idxI && AlmostEqual2sComplement(foreY[lastI],foreY[cntI],4) && AlmostEqual2sComplement(foreY[lastI],foreY[cntI+1],4)){cntI++;}
+ node = gfx_new_area(im->canvas,
+ backX[0],backY[0],
+ foreX[0],foreY[0],
+ foreX[cntI],foreY[cntI], im->gdes[i].col);
+ while (cntI < idxI) {
+ lastI = cntI;
+ cntI++;
+ while ( cntI < idxI && AlmostEqual2sComplement(foreY[lastI],foreY[cntI],4) && AlmostEqual2sComplement(foreY[lastI],foreY[cntI+1],4)){cntI++;}
+ gfx_add_point(node,foreX[cntI],foreY[cntI]);
+ }
+ gfx_add_point(node,backX[idxI],backY[idxI]);
+ while (idxI > 1){
+ lastI = idxI;
+ idxI--;
+ while ( idxI > 1 && AlmostEqual2sComplement(backY[lastI], backY[idxI],4) && AlmostEqual2sComplement(backY[lastI],backY[idxI-1],4)){idxI--;}
+ gfx_add_point(node,backX[idxI],backY[idxI]);
+ }
+ idxI=-1;
+ drawem = 0;
+ }
+ if (drawem != 0){
+ drawem = 0;
+ idxI=-1;
+ }
+ if (ii == im->xsize) break;
+
/* keep things simple for now, just draw these bars
do not try to build a big and complex area */
- float ybase,ytop;
+
+
+ if ( im->slopemode == 0 && ii==0){
+ continue;
+ }
if ( isnan(im->gdes[i].p_data[ii]) ) {
+ drawem = 1;
continue;
}
ytop = ytr(im,im->gdes[i].p_data[ii]);
- if ( im->gdes[i].stack ) {
+ if ( lastgdes && im->gdes[i].stack ) {
ybase = ytr(im,lastgdes->p_data[ii]);
} else {
ybase = ytr(im,areazero);
}
if ( ybase == ytop ){
- continue;
+ drawem = 1;
+ continue;
+ }
+ /* every area has to be wound clock-wise,
+ so we have to make sur base remains base */
+ if (ybase > ytop){
+ double extra = ytop;
+ ytop = ybase;
+ ybase = extra;
}
- node = gfx_new_area(im->canvas,
- ii-1+im->xorigin,ybase,
- ii-1+im->xorigin,ytop,
- ii+im->xorigin,ytop,
- im->gdes[i].col
- );
- gfx_add_point(node,ii+im->xorigin,ybase);
- }
+ if ( im->slopemode == 0 ){
+ backY[++idxI] = ybase-0.2;
+ backX[idxI] = ii+im->xorigin-1;
+ foreY[idxI] = ytop+0.2;
+ foreX[idxI] = ii+im->xorigin-1;
+ }
+ backY[++idxI] = ybase-0.2;
+ backX[idxI] = ii+im->xorigin;
+ foreY[idxI] = ytop+0.2;
+ foreX[idxI] = ii+im->xorigin;
+ }
+ /* close up any remaining area */
+ free(foreY);
+ free(foreX);
+ free(backY);
+ free(backX);
} /* else GF_LINE */
} /* if color != 0x0 */
/* make sure we do not run into trouble when stacking on NaN */
if (lastgdes && (im->gdes[i].stack)) {
im->gdes[i].p_data[ii] = lastgdes->p_data[ii];
} else {
- im->gdes[i].p_data[ii] = ytr(im,areazero);
+ im->gdes[i].p_data[ii] = areazero;
}
}
}
}
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,
if (strcmp(im->graphfile,"-")==0) {
fo = im->graphhandle ? im->graphhandle : stdout;
-#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
/* Change translation mode for stdout to BINARY */
_setmode( _fileno( fo ), O_BINARY );
#endif
return (-1);
}
}
- gfx_render (im->canvas,im->ximg,im->yimg,0x0,fo);
+ gfx_render (im->canvas,im->ximg,im->yimg,0x00000000,fo);
if (strcmp(im->graphfile,"-") != 0)
fclose(fo);
return 0;
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;
rrd_graph(int argc, char **argv, char ***prdata, int *xsize, int *ysize, FILE *stream, double *ymin, double *ymax)
{
image_desc_t im;
-
rrd_graph_init(&im);
im.graphhandle = stream;
#endif
#ifdef HAVE_SETLOCALE
setlocale(LC_TIME,"");
+#ifdef HAVE_MBSTOWCS
+ setlocale(LC_CTYPE,"");
+#endif
#endif
im->yorigin=0;
im->xorigin=0;
im->step = 0;
im->ylegend[0] = '\0';
im->title[0] = '\0';
+ im->watermark[0] = '\0';
im->minval = DNAN;
im->maxval = DNAN;
im->unitsexponent= 9999;
im->gridfit = 1;
im->imginfo = NULL;
im->lazy = 0;
+ im->slopemode = 0;
im->logarithmic = 0;
im->ygridstep = DNAN;
im->draw_x_grid = 1;
for(i=0;i<DIM(graph_col);i++)
im->graph_col[i]=graph_col[i];
-#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
{
char *windir;
char rrd_win_default_font[1000];
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';
- strcat(rrd_win_default_font,"\\fonts\\cour.ttf");
+ 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++){
strncpy(text_prop[i].font,rrd_win_default_font,sizeof(text_prop[i].font)-1);
text_prop[i].font[sizeof(text_prop[i].font)-1] = '\0';
long long_tmp;
struct rrd_time_value start_tv, end_tv;
gfx_color_t color;
+ optind = 0; opterr = 0; /* initialize getopt */
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[] =
{
{"only-graph", no_argument, 0, 'j'},
{"alt-y-grid", no_argument, 0, 'Y'},
{"no-minor", no_argument, 0, 'I'},
+ {"slope-mode", no_argument, 0, 'E'},
{"alt-autoscale", no_argument, 0, 'A'},
{"alt-autoscale-max", no_argument, 0, 'M'},
{"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 opt;
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:zgjFYAMX:L:S:T:N",
+ "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 'z':
im->lazy = 1;
break;
+ case 'E':
+ im->slopemode = 1;
+ break;
+
case 'o':
im->logarithmic = 1;
- if (isnan(im->minval))
- im->minval=1;
break;
case 'c':
if(sscanf(optarg,
int ci;
int col_len = col_end - col_start;
switch (col_len){
+ case 3:
+ color = (
+ ((color & 0xF00) * 0x110000) |
+ ((color & 0x0F0) * 0x011000) |
+ ((color & 0x00F) * 0x001100) |
+ 0x000000FF
+ );
+ break;
+ case 4:
+ color = (
+ ((color & 0xF000) * 0x11000) |
+ ((color & 0x0F00) * 0x01100) |
+ ((color & 0x00F0) * 0x00110) |
+ ((color & 0x000F) * 0x00011)
+ );
+ break;
case 6:
color = (color << 8) + 0xff /* shift left by 8 */;
break;
case 'n':{
char prop[15];
double size = 1;
- char font[1024];
+ char font[1024] = "";
if(sscanf(optarg,
"%10[A-Z]:%lf:%1000s",
- prop,&size,font) == 3){
- int sindex;
+ prop,&size,font) >= 2){
+ int sindex,propidx;
if((sindex=text_prop_conv(prop)) != -1){
- im->text_prop[sindex].size=size;
- strcpy(im->text_prop[sindex].font,font);
- if (sindex==0) { /* the default */
- im->text_prop[TEXT_PROP_TITLE].size=size;
- strcpy(im->text_prop[TEXT_PROP_TITLE].font,font);
- im->text_prop[TEXT_PROP_AXIS].size=size;
- strcpy(im->text_prop[TEXT_PROP_AXIS].font,font);
- im->text_prop[TEXT_PROP_UNIT].size=size;
- strcpy(im->text_prop[TEXT_PROP_UNIT].font,font);
- im->text_prop[TEXT_PROP_LEGEND].size=size;
- strcpy(im->text_prop[TEXT_PROP_LEGEND].font,font);
- }
+ for (propidx=sindex;propidx<TEXT_PROP_LAST;propidx++){
+ if (size > 0){
+ im->text_prop[propidx].size=size;
+ }
+ if (strlen(font) > 0){
+ strcpy(im->text_prop[propidx].font,font);
+ }
+ if (propidx==sindex && sindex != 0) break;
+ }
} else {
rrd_set_error("invalid fonttag '%s'",prop);
return;
im->title[150]='\0';
break;
+ case 'R':
+ if ( strcmp( optarg, "normal" ) == 0 )
+ im->canvas->aa_type = AA_NORMAL;
+ else if ( strcmp( optarg, "light" ) == 0 )
+ im->canvas->aa_type = AA_LIGHT;
+ else if ( strcmp( optarg, "mono" ) == 0 )
+ im->canvas->aa_type = AA_NONE;
+ else
+ {
+ rrd_set_error("unknown font-render-mode '%s'", optarg );
+ return;
+ }
+ break;
+
+ case 'B':
+ 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.
else if (!strcmp("TOTAL", func)) gdes->vf.op = VDEF_TOTAL;
else if (!strcmp("FIRST", func)) gdes->vf.op = VDEF_FIRST;
else if (!strcmp("LAST", func)) gdes->vf.op = VDEF_LAST;
+ else if (!strcmp("LSLSLOPE", func)) gdes->vf.op = VDEF_LSLSLOPE;
+ else if (!strcmp("LSLINT", func)) gdes->vf.op = VDEF_LSLINT;
+ else if (!strcmp("LSLCORREL",func)) gdes->vf.op = VDEF_LSLCORREL;
else {
rrd_set_error("Unknown function '%s' in VDEF '%s'\n"
,func
case VDEF_TOTAL:
case VDEF_FIRST:
case VDEF_LAST:
+ case VDEF_LSLSLOPE:
+ case VDEF_LSLINT:
+ case VDEF_LSLCORREL:
if (isnan(param)) {
gdes->vf.param = DNAN;
gdes->vf.val = DNAN;
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 */
dst->vf.when = src->start + (step+1)*src->step;
}
break;
+ case VDEF_LSLSLOPE:
+ case VDEF_LSLINT:
+ case VDEF_LSLCORREL:{
+ /* Bestfit line by linear least squares method */
+
+ int cnt=0;
+ double SUMx, SUMy, SUMxy, SUMxx, SUMyy, slope, y_intercept, correl ;
+ SUMx = 0; SUMy = 0; SUMxy = 0; SUMxx = 0; SUMyy = 0;
+
+ for (step=0;step<steps;step++) {
+ if (finite(data[step*src->ds_cnt])) {
+ cnt++;
+ SUMx += step;
+ SUMxx += step * step;
+ SUMxy += step * data[step*src->ds_cnt];
+ SUMy += data[step*src->ds_cnt];
+ SUMyy += data[step*src->ds_cnt]*data[step*src->ds_cnt];
+ };
+ }
+
+ slope = ( SUMx*SUMy - cnt*SUMxy ) / ( SUMx*SUMx - cnt*SUMxx );
+ y_intercept = ( SUMy - slope*SUMx ) / cnt;
+ correl = (SUMxy - (SUMx*SUMy)/cnt) / sqrt((SUMxx - (SUMx*SUMx)/cnt)*(SUMyy - (SUMy*SUMy)/cnt));
+
+ if (cnt) {
+ if (dst->vf.op == VDEF_LSLSLOPE) {
+ dst->vf.val = slope;
+ dst->vf.when = 0;
+ } else if (dst->vf.op == VDEF_LSLINT) {
+ dst->vf.val = y_intercept;
+ dst->vf.when = 0;
+ } else if (dst->vf.op == VDEF_LSLCORREL) {
+ dst->vf.val = correl;
+ dst->vf.when = 0;
+ };
+
+ } else {
+ dst->vf.val = DNAN;
+ dst->vf.when = 0;
+ }
+ }
+ break;
}
return 0;
}