summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 82830f4)
raw | patch | inline | side by side (parent: 82830f4)
author | oetiker <oetiker@a5681a0c-68f1-0310-ab6d-d61299d08faa> | |
Mon, 29 Mar 2010 16:32:54 +0000 (16:32 +0000) | ||
committer | oetiker <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
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 | patch | blob | history | |
doc/rrdgraph_graph.pod | patch | blob | history | |
src/rrd_gfx.c | patch | blob | history | |
src/rrd_graph.c | patch | blob | history | |
src/rrd_graph.h | patch | blob | history | |
src/rrd_graph_helper.c | patch | blob | history |
index bac4d122f5355cb68899a4dc4c0d99582227148b..6d889f86fbc42cd64f7176b9eaac59c2cd6511f7 100644 (file)
--- a/NEWS
+++ b/NEWS
#####################################
-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 c740aad38c26e05816bbc44711add5af7f7042ca..693b1edd9ff6cc34285c25c90b6d73f6993a026a 100644 (file)
--- a/doc/rrdgraph_graph.pod
+++ b/doc/rrdgraph_graph.pod
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
diff --git a/src/rrd_gfx.c b/src/rrd_gfx.c
index aff036c781cd0c02381056206275fdb64dc7ed26..416b8a32ada2244d9567498688c920e6b526afb7 100644 (file)
--- a/src/rrd_gfx.c
+++ b/src/rrd_gfx.c
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 eeff31c41ca33db51c06eb9737ac97a7ca156c9c..5dd332861bfe5074f394f22481ea10ee0c6765b0 100644 (file)
--- a/src/rrd_graph.c
+++ b/src/rrd_graph.c
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);
/* 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");
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;
break;
case GF_LINE:
case GF_AREA:
+ case GF_GRAD:
case GF_TICK:
graphelement = 1;
break;
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])) {
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);
[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
+ 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--;
- 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;
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 8e28f63fec38544333b0e92e5b5596ab0d3116bb..6058a4f54e0e6908e0f03028db2e332041169b8f 100644 (file)
--- a/src/rrd_graph.h
+++ b/src/rrd_graph.h
#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
};
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 */
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 e2dd06ce1e0c77f241b289f3633dc0107a6a8e06..5c604877df8c69b21a6e872860d77afb8950927a 100644 (file)
--- a/src/rrd_graph_helper.c
+++ b/src/rrd_graph_helper.c
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;
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';
/* 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 ?
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);
}
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;
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 {