/****************************************************************************
- * RRDtool 1.1.x Copyright Tobias Oetiker, 1997 - 2002
+ * RRDtool 1.2.19 Copyright by Tobi Oetiker, 1997-2007
****************************************************************************
- * rrd__graph.c make creates ne rrds
+ * rrd__graph.c produce graphs from data in rrdfiles
****************************************************************************/
#include <sys/stat.h>
+#ifdef WIN32
+#include "strftime.h"
+#endif
#include "rrd_tool.h"
-#ifdef WIN32
+#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
#include <io.h>
#include <fcntl.h>
#endif
/* some constant definitions */
+
#ifndef RRD_DEFAULT_FONT
-#ifdef WIN32
-#define RRD_DEFAULT_FONT "c:/winnt/fonts/COUR.TTF"
-#else
-#define RRD_DEFAULT_FONT "/usr/share/fonts/truetype/openoffice/ariosor.ttf"
-/* #define RRD_DEFAULT_FONT "/usr/share/fonts/truetype/Arial.ttf" */
+/* there is special code later to pick Cour.ttf when running on windows */
+#define RRD_DEFAULT_FONT "DejaVuSansMono-Roman.ttf"
#endif
-#endif
-
text_prop_t text_prop[] = {
- { 10.0, RRD_DEFAULT_FONT }, /* default */
- { 12.0, RRD_DEFAULT_FONT }, /* title */
- { 8.0, RRD_DEFAULT_FONT }, /* axis */
- { 10.0, RRD_DEFAULT_FONT }, /* unit */
- { 10.0, RRD_DEFAULT_FONT } /* legend */
+ { 8.0, RRD_DEFAULT_FONT }, /* default */
+ { 9.0, RRD_DEFAULT_FONT }, /* title */
+ { 7.0, RRD_DEFAULT_FONT }, /* axis */
+ { 8.0, RRD_DEFAULT_FONT }, /* unit */
+ { 8.0, RRD_DEFAULT_FONT } /* legend */
};
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"},
+ {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[]= {
0xF0F0F0FF, /* background */
0xD0D0D0FF, /* shade A */
0xA0A0A0FF, /* shade B */
- 0x909090FF, /* grid */
- 0xE05050FF, /* major grid */
+ 0x90909080, /* grid */
+ 0xE0505080, /* major grid */
0x000000FF, /* font */
- 0x000000FF, /* frame */
- 0xFF0000FF /* arrow */
-};
+ 0x802020FF, /* arrow */
+ 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(VDEF,GF_VDEF)
+#ifdef WITH_PIECHART
conv_if(PART,GF_PART)
+#endif
conv_if(XPORT,GF_XPORT)
+ conv_if(SHIFT,GF_SHIFT)
return (-1);
}
conv_if(GRID,GRC_GRID)
conv_if(MGRID,GRC_MGRID)
conv_if(FONT,GRC_FONT)
- conv_if(FRAME,GRC_FRAME)
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;
- int symbcenter = 6;
- double digits;
+/* find SI magnitude symbol for the numbers on the y-axis*/
+void
+si_unit(
+ image_desc_t *im /* image description */
+)
+{
+
+ double digits,viewdigits=0;
+ digits = floor( log( max( fabs(im->minval),fabs(im->maxval)))/log((double)im->base));
+
if (im->unitsexponent != 9999) {
/* unitsexponent = 9, 6, 3, 0, -3, -6, -9, etc */
- digits = floor(im->unitsexponent / 3);
+ viewdigits = floor(im->unitsexponent / 3);
} else {
- digits = floor( log( max( fabs(im->minval),fabs(im->maxval)))/log((double)im->base));
+ viewdigits = digits;
}
- im->magfact = pow((double)im->base , digits);
+ im->magfact = pow((double)im->base , digits);
+
#ifdef DEBUG
printf("digits %6.3f im->magfact %6.3f\n",digits,im->magfact);
#endif
- if ( ((digits+symbcenter) < sizeof(symbol)) &&
- ((digits+symbcenter) >= 0) )
- im->symbol = symbol[(int)digits+symbcenter];
+ im->viewfactor = im->magfact / pow((double)im->base , viewdigits);
+
+ if ( ((viewdigits+si_symbcenter) < sizeof(si_symbol)) &&
+ ((viewdigits+si_symbcenter) >= 0) )
+ im->symbol = si_symbol[(int)viewdigits+si_symbcenter];
else
- im->symbol = ' ';
+ im->symbol = '?';
}
/* move min and max values around to become sensible */
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
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 */
}
** into one interval for the destination.
*/
- for (dst_row=0;row_cnt>=reduce_factor;dst_row++) {
+ for (dst_row=0;(long int)row_cnt>=reduce_factor;dst_row++) {
for (col=0;col<(*ds_cnt);col++) {
rrd_value_t newval=DNAN;
unsigned long validval=0;
relevant rrds ... */
int
-data_fetch( image_desc_t *im )
+data_fetch(image_desc_t *im )
{
- int i,ii;
+ int i,ii;
int skip;
- /* pull the data from the log files ... */
- for (i=0;i<im->gdes_c;i++){
+ /* 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)
continue;
continue;
if ((strcmp(im->gdes[i].rrd, im->gdes[ii].rrd) == 0)
&& (im->gdes[i].cf == im->gdes[ii].cf)
- && (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].cf_reduce == im->gdes[ii].cf_reduce)
+ && (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,
im->gdes[i].data_first = 1;
if (ft_step < im->gdes[i].step) {
- reduce_data(im->gdes[i].cf,
+ reduce_data(im->gdes[i].cf_reduce,
ft_step,
&im->gdes[i].start,
&im->gdes[i].end,
}
}
- /* lets see if the required data source is realy there */
- for(ii=0;ii<im->gdes[i].ds_cnt;ii++){
+ /* lets see if the required data source is really there */
+ for(ii=0;ii<(int)im->gdes[i].ds_cnt;ii++){
if(strcmp(im->gdes[i].ds_namv[ii],im->gdes[i].ds_nam) == 0){
im->gdes[i].ds=ii; }
}
switch (im->gdes[gdi].gf) {
case GF_XPORT:
break;
+ case GF_SHIFT: {
+ graph_desc_t *vdp = &im->gdes[im->gdes[gdi].vidx];
+
+ /* remove current shift */
+ vdp->start -= vdp->shift;
+ vdp->end -= vdp->shift;
+
+ /* vdef */
+ if (im->gdes[gdi].shidx >= 0)
+ vdp->shift = im->gdes[im->gdes[gdi].shidx].vf.val;
+ /* constant */
+ else
+ vdp->shift = im->gdes[gdi].shval;
+
+ /* normalize shift to multiple of consolidated step */
+ vdp->shift = (vdp->shift / (long)vdp->step) * (long)vdp->step;
+
+ /* apply shift */
+ vdp->start += vdp->shift;
+ vdp->end += vdp->shift;
+ break;
+ }
case GF_VDEF:
/* A VDEF has no DS. This also signals other parts
* of rrdtool that this is a VDEF value, not a CDEF.
* 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].start > im->gdes[ptr].start) {
- im->gdes[gdi].rpnp[rpi].data += im->gdes[gdi].rpnp[rpi].ds_cnt;
- }
+ 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)
+ im->gdes[gdi].rpnp[rpi].data += (diff / im->gdes[ptr].step) * im->gdes[ptr].ds_cnt;
}
}
-
if(steparray == NULL){
rrd_set_error("rpn expressions without DEF"
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)) { /* not a number or VDEF */
+ if (isnan(value) || (im->gdes[ii].gf == GF_TICK)) {
/* The time of the data doesn't necessarily match
** the time of the graph. Beware.
*/
vidx = im->gdes[ii].vidx;
- if ( (gr_time >= im->gdes[vidx].start) &&
- (gr_time <= im->gdes[vidx].end) ) {
+ if (im->gdes[vidx].gf == GF_VDEF) {
+ value = im->gdes[vidx].vf.val;
+ } else if (((long int)gr_time >= (long int)im->gdes[vidx].start) &&
+ ((long int)gr_time <= (long int)im->gdes[vidx].end) ) {
value = im->gdes[vidx].data[
(unsigned long) floor(
(double)(gr_time - im->gdes[vidx].start)
** 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;
}
+ /* make sure min is smaller than max */
+ if (im->minval > im->maxval) {
+ im->minval = 0.99 * im->maxval;
+ }
+
/* make sure min and max are not equal */
if (im->minval == im->maxval) {
im->maxval *= 1.01;
)
{
struct tm tm;
- tm = *localtime(&start);
+ localtime_r(&start, &tm);
switch(baseint){
case TMT_SECOND:
tm.tm_sec -= tm.tm_sec % basestep; break;
{
struct tm tm;
time_t madetime;
- tm = *localtime(¤t);
+ localtime_r(¤t, &tm);
do {
switch(baseint){
case TMT_SECOND:
{
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 */
- if (im->gdes[i].gf == GF_PRINT){
- (*prdata)[prlines-2] = malloc((FMT_LEG_LEN+2)*sizeof(char));
- sprintf((*prdata)[prlines-2],"%s (%lu)",
- ctime(&printtime),printtime);
- (*prdata)[prlines-1] = NULL;
- } else {
- sprintf(im->gdes[i].legend,"%s (%lu)",
- ctime(&printtime),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 [G]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)) {
- rrd_set_error("bad format for [G]PRINT in '%s'", 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_DEF:
case GF_CDEF:
case GF_VDEF:
+#ifdef WITH_PIECHART
case GF_PART:
+#endif
+ 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;
{
/* graph labels */
int interleg = im->text_prop[TEXT_PROP_LEGEND].size*2.0;
- int box =im->text_prop[TEXT_PROP_LEGEND].size*1.5;
int border = im->text_prop[TEXT_PROP_LEGEND].size*2.0;
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;
char prt_fctn; /*special printfunctions */
int *legspace;
- if( !(im->extra_flags & NOLEGEND) ) {
+ if( !(im->extra_flags & NOLEGEND) & !(im->extra_flags & ONLY_GRAPH) ) {
if ((legspace = malloc(im->gdes_c*sizeof(int)))==NULL){
rrd_set_error("malloc for legspace");
return -1;
for(i=0;i<im->gdes_c;i++){
fill_last = fill;
+
+ /* hid legends for rules which are not displayed */
+
+ if(!(im->extra_flags & FORCE_RULES_LEGEND)) {
+ if (im->gdes[i].gf == GF_HRULE &&
+ (im->gdes[i].yrule < im->minval || im->gdes[i].yrule > im->maxval))
+ im->gdes[i].legend[0] = '\0';
+
+ if (im->gdes[i].gf == GF_VRULE &&
+ (im->gdes[i].xrule < im->start || im->gdes[i].xrule > im->end))
+ im->gdes[i].legend[0] = '\0';
+ }
leg_cc = strlen(im->gdes[i].legend);
/* is there a controle code ant the end of the legend string ? */
- if (leg_cc >= 2 && im->gdes[i].legend[leg_cc-2] == '\\') {
+ /* and it is not a tab \\t */
+ if (leg_cc >= 2 && im->gdes[i].legend[leg_cc-2] == '\\' && im->gdes[i].legend[leg_cc-1] != 't') {
prt_fctn = im->gdes[i].legend[leg_cc-1];
leg_cc -= 2;
im->gdes[i].legend[leg_cc] = '\0';
} else {
prt_fctn = '\0';
}
+ /* only valid control codes */
+ if (prt_fctn != 'l' &&
+ prt_fctn != 'n' && /* a synonym for l */
+ prt_fctn != 'r' &&
+ prt_fctn != 'j' &&
+ prt_fctn != 'c' &&
+ prt_fctn != 's' &&
+ prt_fctn != 't' &&
+ prt_fctn != '\0' &&
+ prt_fctn != 'g' ) {
+ free(legspace);
+ rrd_set_error("Unknown control code at the end of '%s\\%c'",im->gdes[i].legend,prt_fctn);
+ return -1;
+
+ }
+
/* remove exess space */
+ if ( prt_fctn == 'n' ){
+ prt_fctn='l';
+ }
+
while (prt_fctn=='g' &&
leg_cc > 0 &&
im->gdes[i].legend[leg_cc-1]==' '){
/* no interleg space if string ends in \g */
fill += legspace[i];
}
- if (im->gdes[i].gf != GF_GPRINT &&
- im->gdes[i].gf != GF_COMMENT) {
- fill += box;
- }
fill += gfx_get_text_width(im->canvas, fill+border,
im->text_prop[TEXT_PROP_LEGEND].font,
im->text_prop[TEXT_PROP_LEGEND].size,
im->tabwidth,
- im->gdes[i].legend);
+ im->gdes[i].legend, 0);
leg_c++;
} else {
legspace[i]=0;
}
- if (prt_fctn != '\0'){
+ if (prt_fctn != '\0'){
leg_x = border;
if (leg_c >= 2 && prt_fctn == 'j') {
glue = (im->ximg - fill - 2* border) / (leg_c-1);
for(ii=mark;ii<=i;ii++){
if(im->gdes[ii].legend[0]=='\0')
- continue;
+ continue; /* skip empty legends */
im->gdes[ii].leg_x = leg_x;
im->gdes[ii].leg_y = leg_y;
leg_x +=
im->text_prop[TEXT_PROP_LEGEND].font,
im->text_prop[TEXT_PROP_LEGEND].size,
im->tabwidth,
- im->gdes[ii].legend)
+ im->gdes[ii].legend, 0)
+ legspace[ii]
+ glue;
- if (im->gdes[ii].gf != GF_GPRINT &&
- im->gdes[ii].gf != GF_COMMENT)
- leg_x += box;
- }
- leg_y = leg_y + im->text_prop[TEXT_PROP_LEGEND].size*1.2;
- if (prt_fctn == 's') leg_y -= im->text_prop[TEXT_PROP_LEGEND].size*1.2;
+ }
+ 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. */
- sprintf(im->ygrid_scale.labfmt, "%%%d.%df", decimals - fractionals + 1, -fractionals + 1);
- else
- sprintf(im->ygrid_scale.labfmt, "%%%d.1f", decimals + 1);
- 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 (gridind == -1 && 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*i);
- }
- else {
- sprintf(graph_label,"%4.1f",scaledstep*i);
- }
- } else {
- sprintf(graph_label,"%4.0f",scaledstep*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*i, im->symbol);
- } else {
- sprintf(graph_label,"%4.0f %c",scaledstep*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/1.5, Y0,
+ 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,
MGRIDWIDTH, im->graph_col[GRC_MGRID],
im->grid_dash_on, im->grid_dash_off);
- } else {
+ } else if (!(im->extra_flags & NOMINOR)) {
gfx_new_dashed_line ( im->canvas,
X0-1,Y0,
X1+1,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);
+ }
+ }
+ 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/1.5, 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;
}
long factor;
char graph_label[100];
double X0,Y0,Y1; /* points for filled graph and more*/
-
+ struct tm tm;
/* the type of time grid is determined by finding
the number of seconds per pixel in the graph */
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;
/* paint the minor grid */
- for(ti = find_first_time(im->start,
- im->xlab_user.gridtm,
- im->xlab_user.gridst),
- timajor = find_first_time(im->start,
- im->xlab_user.mgridtm,
- im->xlab_user.mgridst);
- ti < im->end;
- ti = find_next_time(ti,im->xlab_user.gridtm,im->xlab_user.gridst)
- ){
- /* are we inside the graph ? */
- if (ti < im->start || ti > im->end) continue;
- while (timajor < ti) {
- timajor = find_next_time(timajor,
- im->xlab_user.mgridtm, im->xlab_user.mgridst);
- }
- if (ti == timajor) continue; /* skip as falls on major grid line */
- X0 = xtr(im,ti);
- gfx_new_dashed_line(im->canvas,X0,Y0+1, X0,Y1-1,GRIDWIDTH,
- im->graph_col[GRC_GRID],
- im->grid_dash_on, im->grid_dash_off);
-
+ if (!(im->extra_flags & NOMINOR))
+ {
+ for(ti = find_first_time(im->start,
+ im->xlab_user.gridtm,
+ im->xlab_user.gridst),
+ timajor = find_first_time(im->start,
+ im->xlab_user.mgridtm,
+ im->xlab_user.mgridst);
+ ti < im->end;
+ ti = find_next_time(ti,im->xlab_user.gridtm,im->xlab_user.gridst)
+ ){
+ /* are we inside the graph ? */
+ if (ti < im->start || ti > im->end) continue;
+ while (timajor < ti) {
+ timajor = find_next_time(timajor,
+ im->xlab_user.mgridtm, im->xlab_user.mgridst);
+ }
+ if (ti == timajor) continue; /* skip as falls on major grid line */
+ X0 = xtr(im,ti);
+ gfx_new_dashed_line(im->canvas,X0,Y0+1, X0,Y1-1,GRIDWIDTH,
+ im->graph_col[GRC_GRID],
+ im->grid_dash_on, im->grid_dash_off);
+
+ }
}
/* paint the major grid */
}
/* paint the labels below the graph */
- for(ti = find_first_time(im->start,
+ for(ti = find_first_time(im->start - im->xlab_user.precis/2,
im->xlab_user.labtm,
im->xlab_user.labst);
- ti <= im->end;
+ ti <= im->end - im->xlab_user.precis/2;
ti = find_next_time(ti,im->xlab_user.labtm,im->xlab_user.labst)
){
tilab= ti + im->xlab_user.precis/2; /* correct time for the label */
/* are we inside the graph ? */
- if (ti < im->start || ti > im->end) continue;
+ if (tilab < im->start || tilab > im->end) continue;
#if HAVE_STRFTIME
- strftime(graph_label,99,im->xlab_user.stst,localtime(&tilab));
+ localtime_r(&tilab, &tm);
+ strftime(graph_label,99,im->xlab_user.stst, &tm);
#else
# 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/1.5,
+ 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 );
}
)
{
/* draw x and y axis */
- gfx_new_line ( im->canvas, im->xorigin+im->xsize,im->yorigin,
+ /* gfx_new_line ( im->canvas, im->xorigin+im->xsize,im->yorigin,
im->xorigin+im->xsize,im->yorigin-im->ysize,
- GRIDWIDTH, im->graph_col[GRC_GRID]);
+ GRIDWIDTH, im->graph_col[GRC_AXIS]);
gfx_new_line ( im->canvas, im->xorigin,im->yorigin-im->ysize,
im->xorigin+im->xsize,im->yorigin-im->ysize,
- GRIDWIDTH, im->graph_col[GRC_GRID]);
+ GRIDWIDTH, im->graph_col[GRC_AXIS]); */
gfx_new_line ( im->canvas, im->xorigin-4,im->yorigin,
im->xorigin+im->xsize+4,im->yorigin,
- MGRIDWIDTH, im->graph_col[GRC_GRID]);
+ MGRIDWIDTH, im->graph_col[GRC_AXIS]);
gfx_new_line ( im->canvas, im->xorigin,im->yorigin+4,
im->xorigin,im->yorigin-im->ysize-4,
- MGRIDWIDTH, im->graph_col[GRC_GRID]);
+ MGRIDWIDTH, im->graph_col[GRC_AXIS]);
- /* arrow for X axis direction */
+ /* arrow for X and Y axis direction */
gfx_new_area ( im->canvas,
- im->xorigin+im->xsize+3, im->yorigin-3,
- im->xorigin+im->xsize+3, im->yorigin+4,
- im->xorigin+im->xsize+8, im->yorigin+0.5, /* LINEOFFSET */
+ im->xorigin+im->xsize+2, im->yorigin-2,
+ im->xorigin+im->xsize+2, im->yorigin+3,
+ im->xorigin+im->xsize+7, im->yorigin+0.5, /* LINEOFFSET */
im->graph_col[GRC_ARROW]);
-
-
-
+
+ gfx_new_area ( im->canvas,
+ im->xorigin-2, im->yorigin-im->ysize-2,
+ im->xorigin+3, im->yorigin-im->ysize-2,
+ im->xorigin+0.5, im->yorigin-im->ysize-7, /* LINEOFFSET */
+ im->graph_col[GRC_ARROW]);
+
}
void
} 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 description */
- if (im->canvas->imgformat != IF_PNG) {
- gfx_new_text( im->canvas,
- 7, (im->yorigin - im->ysize/2),
- im->graph_col[GRC_FONT],
- im->text_prop[TEXT_PROP_AXIS].font,
- im->text_prop[TEXT_PROP_AXIS].size, im->tabwidth, 270.0,
- GFX_H_CENTER, GFX_V_CENTER,
- im->ylegend);
- } else {
- /* horrible hack until we can actually print vertically */
- {
- int n;
- int l=strlen(im->ylegend);
- char s[2];
- for (n=0;n<strlen(im->ylegend);n++) {
- s[0]=im->ylegend[n];
- s[1]='\0';
- gfx_new_text(im->canvas,7,im->text_prop[TEXT_PROP_AXIS].size*(l-n),
- im->graph_col[GRC_FONT],
- im->text_prop[TEXT_PROP_AXIS].font,
- im->text_prop[TEXT_PROP_AXIS].size, im->tabwidth, 270.0,
- GFX_H_CENTER, GFX_V_CENTER,
- s);
- }
- }
- }
-
+ /* yaxis unit description */
+ gfx_new_text( im->canvas,
+ 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,
+ RRDGRAPH_YLEGEND_ANGLE,
+ GFX_H_LEFT, GFX_V_CENTER,
+ im->ylegend);
+
/* graph title */
gfx_new_text( im->canvas,
- im->ximg/2, im->text_prop[TEXT_PROP_TITLE].size,
+ 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) ) {
- for(i=0;i<im->gdes_c;i++){
- if(im->gdes[i].legend[0] =='\0')
- continue;
-
- /* im->gdes[i].leg_y is the bottom of the legend */
- X0 = im->gdes[i].leg_x;
- Y0 = im->gdes[i].leg_y;
- /* Box needed? */
- if ( im->gdes[i].gf != GF_GPRINT
- && im->gdes[i].gf != GF_COMMENT) {
- int boxH, boxV;
-
- boxH = gfx_get_text_width(im->canvas, 0,
- im->text_prop[TEXT_PROP_AXIS].font,
- im->text_prop[TEXT_PROP_AXIS].size,
- im->tabwidth,"M") * 1.25;
- boxV = boxH;
-
- node = gfx_new_area(im->canvas,
- X0,Y0-boxV,
- X0,Y0,
- X0+boxH,Y0,
- im->gdes[i].col);
- gfx_add_point ( node, X0+boxH, Y0-boxV );
- node = gfx_new_line(im->canvas,
- X0,Y0-boxV, X0,Y0,
- 1,0x000000FF);
- gfx_add_point(node,X0+boxH,Y0);
- gfx_add_point(node,X0+boxH,Y0-boxV);
- gfx_close_path(node);
- X0 += boxH / 1.25 * 2;
- }
- gfx_new_text ( im->canvas, X0, Y0,
+ if( !(im->extra_flags & NOLEGEND) & !(im->extra_flags & ONLY_GRAPH) ) {
+ for(i=0;i<im->gdes_c;i++){
+ if(im->gdes[i].legend[0] =='\0')
+ continue;
+
+ /* im->gdes[i].leg_y is the bottom of the legend */
+ X0 = im->gdes[i].leg_x;
+ Y0 = im->gdes[i].leg_y;
+ gfx_new_text ( im->canvas, X0, Y0,
im->graph_col[GRC_FONT],
- im->text_prop[TEXT_PROP_AXIS].font,
- im->text_prop[TEXT_PROP_AXIS].size,
+ im->text_prop[TEXT_PROP_LEGEND].font,
+ im->text_prop[TEXT_PROP_LEGEND].size,
im->tabwidth,0.0, GFX_H_LEFT, GFX_V_BOTTOM,
im->gdes[i].legend );
- }
- }
- }
+ /* The legend for GRAPH items starts with "M " to have
+ enough space for the box */
+ if ( im->gdes[i].gf != GF_PRINT &&
+ im->gdes[i].gf != GF_GPRINT &&
+ im->gdes[i].gf != GF_COMMENT) {
+ int boxH, boxV;
+
+ boxH = gfx_get_text_width(im->canvas, 0,
+ im->text_prop[TEXT_PROP_LEGEND].font,
+ im->text_prop[TEXT_PROP_LEGEND].size,
+ im->tabwidth,"o", 0) * 1.2;
+ boxV = boxH*1.1;
+
+ /* 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_BACK]);
+ gfx_add_point ( node, X0+boxH, Y0-boxV );
+
+ node = gfx_new_area(im->canvas,
+ X0,Y0-boxV,
+ X0,Y0,
+ X0+boxH,Y0,
+ im->gdes[i].col);
+ gfx_add_point ( node, X0+boxH, Y0-boxV );
+ node = gfx_new_line(im->canvas,
+ 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);
+ }
+ }
+ }
+}
/*****************************************************
return size;
}
+#ifdef WITH_PIECHART
void
pie_part(image_desc_t *im, gfx_color_t color,
double PieCenterX, double PieCenterY, double Radius,
}
}
+#endif
+
int
-graph_size_location(image_desc_t *im, int elements, int piechart )
+graph_size_location(image_desc_t *im, int elements
+
+#ifdef WITH_PIECHART
+, int piechart
+#endif
+
+ )
{
/* The actual size of the image to draw is determined from
** several sources. The size given on the command line is
** |v+--+-------------------------------+--------+
** | |..............legends......................|
** +-+-------------------------------------------+
+ ** | watermark |
+ ** +---------------------------------------------+
*/
- int Xvertical=0, Yvertical=0,
- Xtitle =0, Ytitle =0,
- Xylabel =0, Yylabel =0,
+ int Xvertical=0,
+ Ytitle =0,
+ Xylabel =0,
Xmain =0, Ymain =0,
+#ifdef WITH_PIECHART
Xpie =0, Ypie =0,
- Xxlabel =0, Yxlabel =0,
+#endif
+ Yxlabel =0,
#if 0
Xlegend =0, Ylegend =0,
#endif
- Xspacing =10, Yspacing =10;
+ 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_LEGEND].size *2;
- Yvertical = im->text_prop[TEXT_PROP_LEGEND].size * (strlen(im->ylegend)+1);
+ if (im->ylegend[0] != '\0' ) {
+ Xvertical = im->text_prop[TEXT_PROP_UNIT].size *2;
}
+
if (im->title[0] != '\0') {
/* The title is placed "inbetween" two text lines so it
** 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) + 2*Xspacing;
- Ytitle = im->text_prop[TEXT_PROP_TITLE].size*2;
+ im->title, 0) + 2*Xspacing; */
+ Ytitle = im->text_prop[TEXT_PROP_TITLE].size*2.6+10;
}
if (elements) {
Xmain=im->xsize;
Ymain=im->ysize;
if (im->draw_x_grid) {
- Xxlabel=Xmain;
- Yxlabel=im->text_prop[TEXT_PROP_LEGEND].size *2;
+ Yxlabel=im->text_prop[TEXT_PROP_AXIS].size *2.5;
}
- if (im->draw_y_grid) {
- Xylabel=im->text_prop[TEXT_PROP_LEGEND].size *6;
- Yylabel=Ymain;
+ if (im->draw_y_grid || im->forceleftspace ) {
+ Xylabel=gfx_get_text_width(im->canvas, 0,
+ im->text_prop[TEXT_PROP_AXIS].font,
+ im->text_prop[TEXT_PROP_AXIS].size,
+ im->tabwidth,
+ "0", 0) * im->unitslength;
}
}
+#ifdef WITH_PIECHART
if (piechart) {
im->piesize=im->xsize<im->ysize?im->xsize:im->ysize;
Xpie=im->piesize;
Ypie=im->piesize;
}
+#endif
/* Now calculate the total size. Insert some spacing where
desired. im->xorigin and im->yorigin need to correspond
** forget about it at all; the legend will have to fit in the
** size already allocated.
*/
- im->ximg = Xylabel + Xmain + Xpie + Xspacing;
+ im->ximg = Xylabel + Xmain + 2 * Xspacing;
+
+#ifdef WITH_PIECHART
+ im->ximg += Xpie;
+#endif
+
if (Xmain) im->ximg += Xspacing;
+#ifdef WITH_PIECHART
if (Xpie) im->ximg += Xspacing;
+#endif
+
im->xorigin = Xspacing + Xylabel;
- if (Xtitle > im->ximg) im->ximg = Xtitle;
- if (Xvertical) {
+
+ /* 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;
im->xorigin += 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->yorigin = im->yimg - Yxlabel;
+
/* reserve space for the title *or* some padding above the graph */
if (Ytitle) {
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
+#ifdef WITH_PIECHART
/* The pie is placed in the upper right hand corner,
** just below the title (if any) and with sufficient
** padding.
im->pie_x = im->ximg/2;
im->pie_y = im->yorigin-Ypie/2;
}
+#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. */
+
+
/* draw that picture thing ... */
int
graph_paint(image_desc_t *im, char ***calcpr)
{
int i,ii;
int lazy = lazy_check(im);
+#ifdef WITH_PIECHART
int piechart = 0;
double PieStart=0.0;
+#endif
FILE *fo;
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 */
if (lazy && im->prt_c==0) return 0;
-
+
/* pull the data from the rrd files ... */
if(data_fetch(im)==-1)
return -1;
-
+
/* evaluate VDEF and CDEF operations ... */
if(data_calc(im)==-1)
return -1;
-
+
+#ifdef WITH_PIECHART
/* check if we need to draw a piechart */
for(i=0;i<im->gdes_c;i++){
if (im->gdes[i].gf == GF_PART) {
break;
}
}
+#endif
/* calculate and PRINT and GPRINT definitions. We have to do it at
* this point because it will affect the length of the legends
*/
i=print_calc(im,calcpr);
if(i<0) return -1;
- if(((i==0)&&(piechart==0)) || lazy) return 0;
+ if(((i==0)
+#ifdef WITH_PIECHART
+&&(piechart==0)
+#endif
+) || lazy) return 0;
+#ifdef WITH_PIECHART
/* If there's only the pie chart to draw, signal this */
if (i==0) piechart=2;
+#endif
/* get actual drawing data and find min and max values*/
if(data_proc(im)==-1)
if (!calc_horizontal_grid(im))
return -1;
+
if (im->gridfit)
apply_gridfit(im);
+
/**************************************************************
*** Calculating sizes and locations became a bit confusing ***
*** so I moved this into a separate function. ***
**************************************************************/
- if(graph_size_location(im,i,piechart)==-1)
+ if(graph_size_location(im,i
+#ifdef WITH_PIECHART
+,piechart
+#endif
+)==-1)
return -1;
/* the actual graph is created by going through the individual
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) {
+#endif
node=gfx_new_area ( im->canvas,
im->xorigin, im->yorigin,
im->xorigin + im->xsize, im->yorigin,
areazero = im->minval;
if (im->maxval < 0.0)
areazero = im->maxval;
-
- axis_paint(im);
- }
+#ifdef WITH_PIECHART
+ }
+#endif
+#ifdef WITH_PIECHART
if (piechart) {
pie_part(im,im->graph_col[GRC_CANVAS],im->pie_x,im->pie_y,im->piesize*0.5,0,2*M_PI);
}
+#endif
for(i=0;i<im->gdes_c;i++){
switch(im->gdes[i].gf){
case GF_HRULE:
case GF_VRULE:
case GF_XPORT:
+ case GF_SHIFT:
break;
case GF_TICK:
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 */
-
- if (im->gdes[i].col != 0x0){
+
+ /* *******************************************************
+ a ___. (a,t)
+ | | ___
+ ____| | | |
+ | |___|
+ -------|--t-1--t--------------------------------
+
+ 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-1])
- && ! isnan(im->gdes[i].p_data[ii])){
- if (node == NULL){
- node = gfx_new_line(im->canvas,
+ 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 ) {
+ 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,ytr(im,im->gdes[i].p_data[ii]),
+ ii+im->xorigin,last_y,
im->gdes[i].linewidth,
im->gdes[i].col);
- } else {
- gfx_add_point(node,ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii]));
- }
- } else {
- node = NULL;
- }
+ }
+ } else {
+ 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 {
- int area_start=-1;
- node = NULL;
- for(ii=1;ii<im->xsize;ii++){
- /* open an area */
- if ( ! isnan(im->gdes[i].p_data[ii-1])
- && ! isnan(im->gdes[i].p_data[ii])){
- if (node == NULL){
- float ybase = 0.0;
-/*
- if (im->gdes[i].gf == GF_STACK) {
-*/
- if ( (im->gdes[i].gf == GF_STACK)
- || (im->gdes[i].stack) ) {
-
- ybase = ytr(im,lastgdes->p_data[ii-1]);
- } else {
- ybase = ytr(im,areazero);
- }
- area_start = ii-1;
- node = gfx_new_area(im->canvas,
- ii-1+im->xorigin,ybase,
- ii-1+im->xorigin,ytr(im,im->gdes[i].p_data[ii-1]),
- ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii]),
- im->gdes[i].col
- );
- } else {
- gfx_add_point(node,ii+im->xorigin,ytr(im,im->gdes[i].p_data[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 */
- if ( node != NULL && (ii+1==im->xsize || isnan(im->gdes[i].p_data[ii]) )){
- /* GF_AREA STACK type*/
-/*
- if (im->gdes[i].gf == GF_STACK ) {
-*/
- if ( (im->gdes[i].gf == GF_STACK)
- || (im->gdes[i].stack) ) {
- int iii;
- for (iii=ii-1;iii>area_start;iii--){
- gfx_add_point(node,iii+im->xorigin,ytr(im,lastgdes->p_data[iii]));
- }
- } else {
- gfx_add_point(node,ii+im->xorigin,ytr(im,areazero));
- };
- node=NULL;
- };
- }
+
+ 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 ( lastgdes && im->gdes[i].stack ) {
+ ybase = ytr(im,lastgdes->p_data[ii]);
+ } else {
+ ybase = ytr(im,areazero);
+ }
+ if ( ybase == ytop ){
+ 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;
+ }
+ 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 */
for(ii=0;ii<im->xsize;ii++){
if (isnan(im->gdes[i].p_data[ii])) {
- double ybase = 0.0;
- if (lastgdes) {
- ybase = ytr(im,lastgdes->p_data[ii-1]);
- };
- if (isnan(ybase) || !lastgdes ){
- ybase = ytr(im,areazero);
+ if (lastgdes && (im->gdes[i].stack)) {
+ im->gdes[i].p_data[ii] = lastgdes->p_data[ii];
+ } else {
+ im->gdes[i].p_data[ii] = areazero;
}
- im->gdes[i].p_data[ii] = ybase;
}
}
lastgdes = &(im->gdes[i]);
break;
+#ifdef WITH_PIECHART
case GF_PART:
if(isnan(im->gdes[i].yrule)) /* fetch variable */
im->gdes[i].yrule = im->gdes[im->gdes[i].vidx].vf.val;
PieStart += im->gdes[i].yrule;
}
break;
+#endif
+ case GF_STACK:
+ rrd_set_error("STACK should already be turned into LINE or AREA here");
+ return -1;
+ break;
+
} /* switch */
}
+#ifdef WITH_PIECHART
if (piechart==2) {
im->draw_x_grid=0;
im->draw_y_grid=0;
}
+#endif
+
+
/* grid_paint also does the text */
- grid_paint(im);
+ if( !(im->extra_flags & ONLY_GRAPH) )
+ grid_paint(im);
+
+
+ if( !(im->extra_flags & ONLY_GRAPH) )
+ axis_paint(im);
/* the RULES are the last thing to paint ... */
for(i=0;i<im->gdes_c;i++){
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) {
-#ifdef WIN32
+ fo = im->graphhandle ? im->graphhandle : stdout;
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
/* Change translation mode for stdout to BINARY */
- _setmode( _fileno( stdout ), O_BINARY );
+ _setmode( _fileno( fo ), O_BINARY );
#endif
- fo = stdout;
} else {
if ((fo = fopen(im->graphfile,"wb")) == NULL) {
rrd_set_error("Opening '%s' for write: %s",im->graphfile,
- strerror(errno));
+ rrd_strerror(errno));
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;
int
gdes_alloc(image_desc_t *im){
- long def_step = (im->end-im->start)/im->xsize;
-
- if (im->step > def_step) /* step can be increassed ... no decreassed */
- def_step = im->step;
-
im->gdes_c++;
-
if ((im->gdes = (graph_desc_t *) rrd_realloc(im->gdes, (im->gdes_c)
* sizeof(graph_desc_t)))==NULL){
rrd_set_error("realloc graph_descs");
}
- im->gdes[im->gdes_c-1].step=def_step;
+ 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].data_first=0;
im->gdes[im->gdes_c-1].p_data=NULL;
im->gdes[im->gdes_c-1].rpnp=NULL;
+ im->gdes[im->gdes_c-1].shift=0;
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;
** - script parsing now in rrd_graph_script()
*/
int
-rrd_graph(int argc, char **argv, char ***prdata, int *xsize, int *ysize)
+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;
+
rrd_graph_options(argc,argv,&im);
if (rrd_test_error()) {
im_free(&im);
strncpy(im.graphfile,argv[optind],MAXPATH-1);
im.graphfile[MAXPATH-1]='\0';
- rrd_graph_script(argc,argv,&im);
+ rrd_graph_script(argc,argv,&im,1);
if (rrd_test_error()) {
im_free(&im);
return -1;
*xsize=im.ximg;
*ysize=im.yimg;
+ *ymin=im.minval;
+ *ymax=im.maxval;
if (im.imginfo) {
char *filename;
if (!(*prdata)) {
void
rrd_graph_init(image_desc_t *im)
{
- int i;
+ unsigned int i;
#ifdef HAVE_TZSET
tzset();
#endif
#ifdef HAVE_SETLOCALE
setlocale(LC_TIME,"");
+#ifdef HAVE_MBSTOWCS
+ setlocale(LC_CTYPE,"");
#endif
-
+#endif
+ im->yorigin=0;
+ im->xorigin=0;
+ im->minval=0;
im->xlab_user.minsec = -1;
im->ximg=0;
im->yimg=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->unitslength= 6;
+ im->forceleftspace = 0;
+ im->symbol = ' ';
+ im->viewfactor = 1.0;
im->extra_flags= 0;
im->rigid = 0;
im->gridfit = 1;
im->imginfo = NULL;
im->lazy = 0;
+ im->slopemode = 0;
im->logarithmic = 0;
im->ygridstep = DNAN;
im->draw_x_grid = 1;
im->canvas = gfx_new_canvas();
im->grid_dash_on = 1;
im->grid_dash_off = 1;
-
+ im->tabwidth = 40.0;
+
for(i=0;i<DIM(graph_col);i++)
im->graph_col[i]=graph_col[i];
+#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,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';
+ }
+ }
+ }
+#endif
+ {
+ char *deffont;
+ deffont = getenv("RRD_DEFAULT_FONT");
+ if (deffont != NULL) {
+ for(i=0;i<DIM(text_prop);i++){
+ strncpy(text_prop[i].font,deffont,sizeof(text_prop[i].font)-1);
+ text_prop[i].font[sizeof(text_prop[i].font)-1] = '\0';
+ }
+ }
+ }
for(i=0;i<DIM(text_prop);i++){
im->text_prop[i].size = text_prop[i].size;
- im->text_prop[i].font = text_prop[i].font;
+ strcpy(im->text_prop[i].font,text_prop[i].font);
}
}
char scan_gtm[12],scan_mtm[12],scan_ltm[12],col_nam[12];
time_t start_tmp=0,end_tmp=0;
long long_tmp;
- struct time_value start_tv, end_tv;
+ 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[] =
{
{"lazy", no_argument, 0, 'z'},
{"zoom", required_argument, 0, 'm'},
{"no-legend", no_argument, 0, 'g'},
- {"alt-y-grid", no_argument, 0, 257 },
- {"alt-autoscale", no_argument, 0, 258 },
- {"alt-autoscale-max", no_argument, 0, 259 },
- {"units-exponent",required_argument, 0, 260},
- {"step", required_argument, 0, 261},
- {"no-gridfit", no_argument, 0, 262},
+ {"force-rules-legend",no_argument,0, 'F'},
+ {"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:z:g",
+ "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)
break;
switch(opt) {
- case 257:
+ case 'I':
+ im->extra_flags |= NOMINOR;
+ break;
+ case 'Y':
im->extra_flags |= ALTYGRID;
break;
- case 258:
+ case 'A':
im->extra_flags |= ALTAUTOSCALE;
break;
- case 259:
+ case 'M':
im->extra_flags |= ALTAUTOSCALE_MAX;
break;
+ case 'j':
+ im->extra_flags |= ONLY_GRAPH;
+ break;
case 'g':
im->extra_flags |= NOLEGEND;
break;
- case 260:
+ 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 261:
+ case 'L':
+ im->unitslength = atoi(optarg);
+ im->forceleftspace = 1;
+ break;
+ case 'T':
+ im->tabwidth = atof(optarg);
+ break;
+ case 'S':
im->step = atoi(optarg);
break;
- case 262:
+ case 'N':
im->gridfit = 0;
break;
case 's':
&im->xlab_user.precis,
&stroff) == 7 && stroff != 0){
strncpy(im->xlab_form, optarg+stroff, sizeof(im->xlab_form) - 1);
- if((im->xlab_user.gridtm = tmt_conv(scan_gtm)) == -1){
+ im->xlab_form[sizeof(im->xlab_form)-1] = '\0';
+ if((int)(im->xlab_user.gridtm = tmt_conv(scan_gtm)) == -1){
rrd_set_error("unknown keyword %s",scan_gtm);
return;
- } else if ((im->xlab_user.mgridtm = tmt_conv(scan_mtm)) == -1){
+ } else if ((int)(im->xlab_user.mgridtm = tmt_conv(scan_mtm)) == -1){
rrd_set_error("unknown keyword %s",scan_mtm);
return;
- } else if ((im->xlab_user.labtm = tmt_conv(scan_ltm)) == -1){
+ } else if ((int)(im->xlab_user.labtm = tmt_conv(scan_ltm)) == -1){
rrd_set_error("unknown keyword %s",scan_ltm);
return;
}
im->imginfo = optarg;
break;
case 'a':
- if((im->canvas->imgformat = if_conv(optarg)) == -1) {
+ if((int)(im->canvas->imgformat = if_conv(optarg)) == -1) {
rrd_set_error("unsupported graphics format '%s'",optarg);
return;
}
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,
- "%10[A-Z]#%8lx",
- col_nam,&color) == 2){
+ "%10[A-Z]#%n%8lx%n",
+ col_nam,&col_start,&color,&col_end) == 2){
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 8:
+ break;
+ default:
+ rrd_set_error("the color format is #RRGGBB[AA]");
+ return;
+ }
if((ci=grc_conv(col_nam)) != -1){
im->graph_col[ci]=color;
} else {
rrd_set_error("invalid color name '%s'",col_nam);
+ return;
}
} else {
rrd_set_error("invalid color def format");
}
break;
case 'n':{
- /* originally this used char *prop = "" and
- ** char *font = "dummy" however this results
- ** in a SEG fault, at least on RH7.1
- **
- ** The current implementation isn't proper
- ** either, font is never freed and prop uses
- ** a fixed width string
- */
- char prop[100];
+ char prop[15];
double size = 1;
- char *font;
+ char font[1024] = "";
- font=malloc(255);
if(sscanf(optarg,
- "%10[A-Z]:%lf:%s",
- prop,&size,font) == 3){
- int sindex;
+ "%10[A-Z]:%lf:%1000s",
+ prop,&size,font) >= 2){
+ int sindex,propidx;
if((sindex=text_prop_conv(prop)) != -1){
- im->text_prop[sindex].size=size;
- im->text_prop[sindex].font=font;
- if (sindex==0) { /* the default */
- im->text_prop[TEXT_PROP_TITLE].size=size;
- im->text_prop[TEXT_PROP_TITLE].font=font;
- im->text_prop[TEXT_PROP_AXIS].size=size;
- im->text_prop[TEXT_PROP_AXIS].font=font;
- im->text_prop[TEXT_PROP_UNIT].size=size;
- im->text_prop[TEXT_PROP_UNIT].font=font;
- im->text_prop[TEXT_PROP_LEGEND].size=size;
- 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;
}
im->start = start_tmp;
im->end = end_tmp;
+ im->step = max((long)im->step, (im->end-im->start)/im->xsize);
}
int
return n;
}
}
-int
-rrd_graph_legend(graph_desc_t *gdp, char *line)
-{
- int i;
-
- i=scan_for_col(line,FMT_LEG_LEN,gdp->legend);
-
- return (strlen(&line[i])==0);
-}
int bad_format(char *fmt) {
/* '%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++;
while (*ptr >= '0' && *ptr <= '9') ptr++;
- /* Either 'le' or 'lf' must follow here */
+ /* Either 'le', 'lf' or 'lg' must follow here */
if (*ptr++ != 'l') return 1;
- if (*ptr == 'e' || *ptr == 'f') ptr++;
+ if (*ptr == 'e' || *ptr == 'f' || *ptr == 'g') ptr++;
else return 1;
n++;
}
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.
n=0;
sscanf(str,"%le,%29[A-Z]%n",¶m,func,&n);
- if (n==strlen(str)) { /* matched */
+ if (n== (int)strlen(str)) { /* matched */
;
} else {
n=0;
sscanf(str,"%29[A-Z]%n",func,&n);
- if (n==strlen(str)) { /* matched */
+ if (n== (int)strlen(str)) { /* matched */
param=DNAN;
} else {
rrd_set_error("Unknown function string '%s' in VDEF '%s'"
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;
}