From 49593a6456b8dd4e82f82d8c76aaf21ce876ea92 Mon Sep 17 00:00:00 2001 From: oetiker Date: Mon, 29 Mar 2010 16:32:54 +0000 Subject: [PATCH] * GRAD element for graph, it acts more or less like an AREA, except you can specify a second color and a height which is used to create a gradient from one color to the next by Rian Shelley git-svn-id: svn://svn.oetiker.ch/rrdtool/trunk/program@2058 a5681a0c-68f1-0310-ab6d-d61299d08faa --- NEWS | 16 ++++++++++- doc/rrdgraph_graph.pod | 8 ++++++ src/rrd_gfx.c | 36 +++++++++++++++++++++++ src/rrd_graph.c | 78 ++++++++++++++++++++++++++++++++++++++++++-------- src/rrd_graph.h | 24 ++++++++++++++-- src/rrd_graph_helper.c | 52 +++++++++++++++++++++++++++++---- 6 files changed, 194 insertions(+), 20 deletions(-) diff --git a/NEWS b/NEWS index bac4d12..6d889f8 100644 --- a/NEWS +++ b/NEWS @@ -1,8 +1,22 @@ ##################################### -Major Changes between 1.3.x and 1.4.x +Major Changes between 1.4.x and ... ------------------------------------- $Id$ +RRD Graph +--------- +* GRAD element for graph, it acts more or less like an AREA, + except you can specify a second color and a height which is used to + create a gradient from one color to the next + by Rian Shelley + + + + +##################################### +Major Changes between 1.3.x and 1.4.x +------------------------------------- + RRD Caching Daemon (rrdcached) ------------------------------ by Florian Forster and Kevin Brintnall diff --git a/doc/rrdgraph_graph.pod b/doc/rrdgraph_graph.pod index c740aad..693b1ed 100644 --- a/doc/rrdgraph_graph.pod +++ b/doc/rrdgraph_graph.pod @@ -262,6 +262,14 @@ you want to use STACK, use the "LINEx:::STACK" form. See B, however the area between the x-axis and the line will be filled. +=head3 BB<:>I[B<#>I[B<#>IB<:>I][B<:>[I][B<:STACK>]] + +Similar to B, except the area between the line and the x-axis will contain a gradient from color1 to color2. + +The I parameter can create three different behaviors. If I > 0, then the gradient is a fixed height, starting at the line going down. If I < 0, then the gradient starts at fixed height above the x-axis, going down to the x-axis. If I == 0, then the gradient goes from the line to x-axis. + +If not present, I defaults to #00000000 and I defaults to 50. + =head3 BB<:>IB<#>I[I][B<:>I[B<:>I]] Plot a tick mark (a vertical line) for each value of I that is diff --git a/src/rrd_gfx.c b/src/rrd_gfx.c index aff036c..416b8a3 100644 --- a/src/rrd_gfx.c +++ b/src/rrd_gfx.c @@ -105,6 +105,42 @@ void gfx_add_point( cairo_line_to(cr, x, y); } +/* add a point to a line or to an area */ +void gfx_add_rect_fadey( + image_desc_t *im, + double x1,double y1, + double x2,double y2, + double py, + gfx_color_t color1, + gfx_color_t color2, + double height) +{ + cairo_t *cr = im->cr; + + cairo_new_path(cr); + gfx_area_fit(im, &x1, &y1); + gfx_area_fit(im, &x2, &y2); + cairo_line_to(cr, x1, y1); + cairo_line_to(cr, x1, y2); + cairo_line_to(cr, x2, y2); + cairo_line_to(cr, x2, y1); + cairo_close_path(cr); + cairo_pattern_t* p; + if (height < 0) { + p = cairo_pattern_create_linear(x1,y1,x2,y1+height); + } else if (height > 0) { + p = cairo_pattern_create_linear(x1,(y2+py)/2+height,x2,(y2+py)/2); + } else { + p = cairo_pattern_create_linear(x1,y1,x2,(y2+py)/2); + } + //cairo_pattern_t* p = cairo_pattern_create_linear(x1,py+50,x2,py); + cairo_pattern_add_color_stop_rgba(p, 1, color1.red,color1.green,color1.blue,color1.alpha); + cairo_pattern_add_color_stop_rgba(p, 0, color2.red,color2.green,color2.blue,color2.alpha); + cairo_set_source(cr, p); + cairo_pattern_destroy(p); + cairo_fill(cr); +} + void gfx_close_path( image_desc_t *im) { diff --git a/src/rrd_graph.c b/src/rrd_graph.c index eeff31c..5dd3328 100644 --- a/src/rrd_graph.c +++ b/src/rrd_graph.c @@ -234,6 +234,7 @@ enum gf_en gf_conv( conv_if(VRULE, GF_VRULE); conv_if(LINE, GF_LINE); conv_if(AREA, GF_AREA); + conv_if(GRAD, GF_GRAD); conv_if(STACK, GF_STACK); conv_if(TICK, GF_TICK); conv_if(TEXTALIGN, GF_TEXTALIGN); @@ -1216,8 +1217,11 @@ int data_proc( /* memory for the processed data */ 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)) { + if ((im->gdes[i].gf == GF_LINE) + || (im->gdes[i].gf == GF_AREA) + || (im->gdes[i].gf == GF_TICK) + || (im->gdes[i].gf == GF_GRAD) + ) { if ((im->gdes[i].p_data = (rrd_value_t*)malloc((im->xsize + 1) * sizeof(rrd_value_t))) == NULL) { rrd_set_error("malloc data_proc"); @@ -1238,6 +1242,7 @@ int data_proc( switch (im->gdes[ii].gf) { case GF_LINE: case GF_AREA: + case GF_GRAD: case GF_TICK: if (!im->gdes[ii].stack) paintval = 0.0; @@ -1631,6 +1636,7 @@ int print_calc( break; case GF_LINE: case GF_AREA: + case GF_GRAD: case GF_TICK: graphelement = 1; break; @@ -3424,6 +3430,7 @@ int graph_paint( break; case GF_LINE: case GF_AREA: + case GF_GRAD: /* fix data points at oo and -oo */ for (ii = 0; ii < im->xsize; ii++) { if (isinf(im->gdes[i].p_data[ii])) { @@ -3522,6 +3529,8 @@ int graph_paint( cairo_stroke(im->cr); cairo_restore(im->cr); } else { + double lastx=0; + double lasty=0; int idxI = -1; double *foreY = (double *) malloc(sizeof(double) * im->xsize * 2); @@ -3552,12 +3561,17 @@ int graph_paint( [cntI + 1], 4)) { cntI++; } - gfx_new_area(im, - backX[0], backY[0], - foreX[0], foreY[0], - foreX[cntI], - foreY[cntI], im->gdes[i].col); - while (cntI < idxI) { + if (im->gdes[i].gf != GF_GRAD) { + gfx_new_area(im, + backX[0], backY[0], + foreX[0], foreY[0], + foreX[cntI], + foreY[cntI], im->gdes[i].col); + } else { + lastx = foreX[cntI]; + lasty = foreY[cntI]; + } + while (cntI < idxI) { lastI = cntI; cntI++; while (cntI < idxI @@ -3573,9 +3587,32 @@ int graph_paint( + 1], 4)) { cntI++; } - gfx_add_point(im, foreX[cntI], foreY[cntI]); + if (im->gdes[i].gf != GF_GRAD) { + gfx_add_point(im, foreX[cntI], foreY[cntI]); + } else { + gfx_add_rect_fadey(im, + lastx, foreY[0], + foreX[cntI], foreY[cntI], lasty, + im->gdes[i].col, + im->gdes[i].col2, + im->gdes[i].gradheight + ); + lastx = foreX[cntI]; + lasty = foreY[cntI]; + } } - gfx_add_point(im, backX[idxI], backY[idxI]); + if (im->gdes[i].gf != GF_GRAD) { + gfx_add_point(im, backX[idxI], backY[idxI]); + } else { + gfx_add_rect_fadey(im, + lastx, foreY[0], + backX[idxI], backY[idxI], lasty, + im->gdes[i].col, + im->gdes[i].col2, + im->gdes[i].gradheight); + lastx = backX[idxI]; + lasty = backY[idxI]; + } while (idxI > 1) { lastI = idxI; idxI--; @@ -3592,11 +3629,23 @@ int graph_paint( - 1], 4)) { idxI--; } - gfx_add_point(im, backX[idxI], backY[idxI]); + if (im->gdes[i].gf != GF_GRAD) { + gfx_add_point(im, backX[idxI], backY[idxI]); + } else { + gfx_add_rect_fadey(im, + lastx, foreY[0], + backX[idxI], backY[idxI], lasty, + im->gdes[i].col, + im->gdes[i].col2, + im->gdes[i].gradheight); + lastx = backX[idxI]; + lasty = backY[idxI]; + } } idxI = -1; drawem = 0; - gfx_close_path(im); + if (im->gdes[i].gf != GF_GRAD) + gfx_close_path(im); } if (drawem != 0) { drawem = 0; @@ -3790,6 +3839,11 @@ int gdes_alloc( im->gdes[im->gdes_c - 1].col.green = 0.0; im->gdes[im->gdes_c - 1].col.blue = 0.0; im->gdes[im->gdes_c - 1].col.alpha = 0.0; + im->gdes[im->gdes_c - 1].col2.red = 0.0; + im->gdes[im->gdes_c - 1].col2.green = 0.0; + im->gdes[im->gdes_c - 1].col2.blue = 0.0; + im->gdes[im->gdes_c - 1].col2.alpha = 0.0; + im->gdes[im->gdes_c - 1].gradheight = 50.0; 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; diff --git a/src/rrd_graph.h b/src/rrd_graph.h index 8e28f63..6058a4f 100644 --- a/src/rrd_graph.h +++ b/src/rrd_graph.h @@ -52,7 +52,7 @@ enum grc_en { GRC_CANVAS = 0, GRC_BACK, GRC_SHADEA, GRC_SHADEB, #define GRIDWIDTH 0.4 enum gf_en { GF_PRINT = 0, GF_GPRINT, GF_COMMENT, GF_HRULE, GF_VRULE, GF_LINE, - GF_AREA, GF_STACK, GF_TICK, GF_TEXTALIGN, + GF_AREA,GF_GRAD, GF_STACK, GF_TICK, GF_TEXTALIGN, GF_DEF, GF_CDEF, GF_VDEF, GF_SHIFT, GF_XPORT }; @@ -160,7 +160,8 @@ typedef struct graph_desc_t { long ds; /* data source number */ enum cf_en cf; /* consolidation function */ enum cf_en cf_reduce; /* consolidation function for reduce_data() */ - struct gfx_color_t col; /* graph color */ + struct gfx_color_t col, col2; /* graph color */ + double gradheight; char format[FMT_LEG_LEN + 5]; /* format for PRINT AND GPRINT */ char legend[FMT_LEG_LEN + 5]; /* legend */ int strftm; /* should the VDEF legend be formated with strftime */ @@ -438,6 +439,25 @@ void gfx_add_point( double x, double y); +/* create a rect that has a gradient from color1 to color2 in height pixels + * height > 0: + * gradient starts at top and goes down a fixed number of pixels (fire style) + * height < 0: + * gradient starts at bottom and goes up a fixed number of pixels (constant style) + * height == 0: + * gradient is stretched between two points + */ +void gfx_add_rect_fadey( + image_desc_t *im, + double x1,double y1, + double x2,double y2, + double py, + gfx_color_t color1, + gfx_color_t color2, + double height); + + + /* close current path so it ends at the same point as it started */ void gfx_close_path( image_desc_t *im); diff --git a/src/rrd_graph_helper.c b/src/rrd_graph_helper.c index e2dd06c..5c60487 100644 --- a/src/rrd_graph_helper.c +++ b/src/rrd_graph_helper.c @@ -532,7 +532,7 @@ int rrd_parse_PVHLAST( graph_desc_t *const gdp, image_desc_t *const im) { - int i, j, k; + int i, j, k, j2; int colorfound = 0; char tmpstr[MAX_VNAME_LEN + 10]; /* vname#RRGGBBAA\0 */ static int spacecnt = 0; @@ -567,10 +567,22 @@ int rrd_parse_PVHLAST( rrd_set_error("Cannot parse line '%s'", line); return 1; } - - j = i; + + j = i; while (j > 0 && tmpstr[j] != '#') j--; + //see if there is a second color + j2 = j-1; + while (j2 > 0 && tmpstr[j2] != '#') + j2--; + if (j && j2) { //yes, swap j and j2, so that j is first color, j2 is second + int tmp = j; + j = j2; + j2 = tmp; + tmpstr[j2] = '\0'; + } else { + j2 = 0; + } if (j) { tmpstr[j] = '\0'; @@ -578,8 +590,10 @@ int rrd_parse_PVHLAST( /* We now have: * tmpstr[0] containing vname * tmpstr[j] if j!=0 then containing color - * i size of vname + color + * tmpstr[j2] if j2!=0 then containing second color + * i size of vname * j if j!=0 then size of vname + * j2 if j2!=0 then size of vname + first color */ /* Number or vname ? @@ -644,6 +658,33 @@ int rrd_parse_PVHLAST( dprintf("- parsed color %0.0f,%0.0f,%0.0f,%0.0f\n", gdp->col.red, gdp->col.green, gdp->col.blue, gdp->col.alpha); colorfound = 1; + if (j2) { //second color? + j2++; + dprintf("- examining second color '%s'\n", &tmpstr[j2]); + //TODO: maybe rrd_parse_color should take a pointer to gdp->col instead of gdp + struct gfx_color_t firstcol = gdp->col; + if (rrd_parse_color(&tmpstr[j2], gdp)) { + rrd_set_error("Could not parse color in '%s'", &tmpstr[j2]); + return 1; + } + dprintf("- parsed color %0.0f,%0.0f,%0.0f,%0.0f\n", gdp->col.red, + gdp->col.green, gdp->col.blue, gdp->col.alpha); + gdp->col2 = gdp->col; + gdp->col = firstcol; + //we now have a mandatory grid height + (*eaten) += i; + if (line[*eaten] != '\0') { + (*eaten)++; + } + dprintf("- examining gradient height\n"); + i = scan_for_col(&line[*eaten], MAX_VNAME_LEN + 9, tmpstr); + sscanf(tmpstr, "%lf%n", &gdp->gradheight, &j); + if (i != j) { + rrd_set_error("Could not parse gradient height in '%s'", tmpstr); + return 1; + } + dprintf("- parsed gradientheight %0.0f\n", gdp->gradheight); + } } else { dprintf("- no color present in '%s'\n", tmpstr); } @@ -1113,6 +1154,7 @@ void rrd_graph_script( case GF_HRULE: /* value#color[:legend] */ case GF_LINE: /* vname-or-value[#color[:legend]][:STACK] */ case GF_AREA: /* vname-or-value[#color[:legend]][:STACK] */ + case GF_GRAD: /* vname-or-value[#color[:legend][#color[:gradientheight]]][:STACK] */ case GF_TICK: /* vname#color[:num[:legend]] */ if (rrd_parse_PVHLAST(argv[i], &eaten, gdp, im)) return; @@ -1122,7 +1164,7 @@ void rrd_graph_script( case GF_STACK: /* vname-or-value[#color[:legend]] */ if (rrd_parse_PVHLAST(argv[i], &eaten, gdp, im)) return; - if (last_gf == GF_LINE || last_gf == GF_AREA) { + if (last_gf == GF_LINE || last_gf == GF_AREA || last_gf == GF_GRAD) { gdp->gf = last_gf; gdp->linewidth = last_linewidth; } else { -- 2.11.0