* GRAD element for graph, it acts more or less like an AREA,
authoroetiker <oetiker@a5681a0c-68f1-0310-ab6d-d61299d08faa>
Mon, 29 Mar 2010 16:32:54 +0000 (16:32 +0000)
committeroetiker <oetiker@a5681a0c-68f1-0310-ab6d-d61299d08faa>
Mon, 29 Mar 2010 16:32:54 +0000 (16:32 +0000)
  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
doc/rrdgraph_graph.pod
src/rrd_gfx.c
src/rrd_graph.c
src/rrd_graph.h
src/rrd_graph_helper.c

diff --git a/NEWS b/NEWS
index bac4d12..6d889f8 100644 (file)
--- 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
index c740aad..693b1ed 100644 (file)
@@ -262,6 +262,14 @@ you want to use STACK, use the "LINEx:<value>::STACK" form.
 See B<LINE>, however the area between the x-axis and the line will
 be filled.
 
+=head3 B<GRAD>B<:>I<value>[B<#>I<color1>[B<#>I<color2>B<:>I<height>][B<:>[I<legend>][B<:STACK>]]
+
+Similar to B<AREA>, except the area between the line and the x-axis will contain a gradient from color1 to color2.
+
+The I<height> parameter can create three different behaviors. If I<height> > 0, then the gradient is a fixed height, starting at the line going down. If I<height> < 0, then the gradient starts at fixed height above the x-axis, going down to the x-axis. If I<height> == 0, then the gradient goes from the line to x-axis. 
+
+If not present, I<color2> defaults to #00000000 and I<height> defaults to 50.
+
 =head3 B<TICK>B<:>I<vname>B<#>I<rrggbb>[I<aa>][B<:>I<fraction>[B<:>I<legend>]]
 
 Plot a tick mark (a vertical line) for each value of I<vname> that is
index aff036c..416b8a3 100644 (file)
@@ -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)
 {
index eeff31c..5dd3328 100644 (file)
@@ -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;
index 8e28f63..6058a4f 100644 (file)
@@ -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);
index e2dd06c..5c60487 100644 (file)
@@ -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 {