summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: a5e4941)
raw | patch | inline | side by side (parent: a5e4941)
author | oetiker <oetiker@a5681a0c-68f1-0310-ab6d-d61299d08faa> | |
Mon, 15 Apr 2002 21:29:24 +0000 (21:29 +0000) | ||
committer | oetiker <oetiker@a5681a0c-68f1-0310-ab6d-d61299d08faa> | |
Mon, 15 Apr 2002 21:29:24 +0000 (21:29 +0000) |
I had to split horizontal_grid() into calc_horizontal_grid and
draw_horizontal_grid as the calculated info is needed in the adjustment
code. My previous patch had the problem that it adjusted the y-axis too
late, e.g. after the data lines was drawn.
The result of the calc is stored in struct ygrid_scale_t which
image_desc_t has as a member.
--no-gridfit is implemented.
The round-to-integer coordinates for png is moved to the libart code in
rrd_gfx. The 'close path' code is cleaned up so the node list is left
unchanged if you want to save the same graph in multiple formats in one
run. The rounding is done on the scaled coordinates (zoom).
I have made a simple version for logarithmic y scales as such a scale
might have 5 gridlines with 4 difference spacings. This version only
uses y = 10^x values for modifying the scale. -- Peter Speck <speck@ruc.dk>
git-svn-id: svn://svn.oetiker.ch/rrdtool/trunk/program@123 a5681a0c-68f1-0310-ab6d-d61299d08faa
draw_horizontal_grid as the calculated info is needed in the adjustment
code. My previous patch had the problem that it adjusted the y-axis too
late, e.g. after the data lines was drawn.
The result of the calc is stored in struct ygrid_scale_t which
image_desc_t has as a member.
--no-gridfit is implemented.
The round-to-integer coordinates for png is moved to the libart code in
rrd_gfx. The 'close path' code is cleaned up so the node list is left
unchanged if you want to save the same graph in multiple formats in one
run. The rounding is done on the scaled coordinates (zoom).
I have made a simple version for logarithmic y scales as such a scale
might have 5 gridlines with 4 difference spacings. This version only
uses y = 10^x values for modifying the scale. -- Peter Speck <speck@ruc.dk>
git-svn-id: svn://svn.oetiker.ch/rrdtool/trunk/program@123 a5681a0c-68f1-0310-ab6d-d61299d08faa
src/rrd_gfx.c | patch | blob | history | |
src/rrd_graph.c | patch | blob | history | |
src/rrd_graph.h | patch | blob | history |
diff --git a/src/rrd_gfx.c b/src/rrd_gfx.c
index 5fd0060a81443e7e621ce1402d87897d21ecbcb2..4ddd584e5d8423a55a435bb5e5ca3249f9caa40b 100644 (file)
--- a/src/rrd_gfx.c
+++ b/src/rrd_gfx.c
void gfx_close_path (gfx_node_t *node) {
node->closed_path = 1;
+ if (node->path[0].code == ART_MOVETO_OPEN)
+ node->path[0].code = ART_MOVETO;
}
/* create a text node */
return text_width;
}
+static void gfx_libart_close_path(gfx_canvas_t *canvas,
+ gfx_node_t *node, ArtVpath **vec)
+{
+ /* libart must have end==start for closed paths,
+ even if using ART_MOVETO and not ART_MOVETO_OPEN
+ so add extra point which is the same as the starting point */
+ int points_max = node->points; /* scaled array has exact size */
+ int points = node->points - 1;
+ art_vpath_add_point (vec, &points, &points_max, ART_LINETO,
+ (**vec).x, (**vec).y);
+ art_vpath_add_point (vec, &points, &points_max, ART_END, 0, 0);
+}
+
+static void gfx_round_scaled_coordinates(gfx_canvas_t *canvas,
+ gfx_node_t *node, ArtVpath *vec)
+{
+ while (vec->code != ART_END) {
+ vec->x = floor(vec->x - LINEOFFSET + 0.5) + LINEOFFSET;
+ vec->y = floor(vec->y - LINEOFFSET + 0.5) + LINEOFFSET;
+ vec++;
+ }
+}
+
static int gfx_save_png (art_u8 *buffer, FILE *fp,
long width, long height, long bytes_per_pixel);
/* render grafics into png image */
ArtVpath *vec;
double dst[6];
ArtSVP *svp;
- if (node->closed_path) {
- /* libart uses end==start for closed as indicator of closed path */
- gfx_add_point(node, node->path[0].x, node->path[0].y);
- node->closed_path = 0;
- }
art_affine_scale(dst,canvas->zoom,canvas->zoom);
vec = art_vpath_affine_transform(node->path,dst);
+ if (node->closed_path)
+ gfx_libart_close_path(canvas, node, &vec);
+ gfx_round_scaled_coordinates(canvas, node, vec);
if(node->type == GFX_LINE){
svp = art_svp_vpath_stroke ( vec, ART_PATH_STROKE_JOIN_ROUND,
ART_PATH_STROKE_CAP_ROUND,
diff --git a/src/rrd_graph.c b/src/rrd_graph.c
index c58326fa9c4cddb7fb7f620eca377125db79b092..2e6cb9f964edf4ac49528e2d101dda2687c38958 100644 (file)
--- a/src/rrd_graph.c
+++ b/src/rrd_graph.c
}
/* translate data values into y coordinates */
-int
+double
ytr(image_desc_t *im, double value){
static double pixie;
double yval;
pixie = (double) im->ysize / (log10(im->maxval) - log10(im->minval));
yval = im->yorigin;
} else if(!im->logarithmic) {
- yval = im->yorigin - pixie * (value - im->minval) + 0.5;
+ yval = im->yorigin - pixie * (value - im->minval);
} else {
if (value < im->minval) {
yval = im->yorigin;
} else {
- yval = im->yorigin - pixie * (log10(value) - log10(im->minval)) + 0.5;
+ yval = im->yorigin - pixie * (log10(value) - log10(im->minval));
}
}
/* make sure we don't return anything too unreasonable. GD lib can
get terribly slow when drawing lines outside its scope. This is
especially problematic in connection with the rigid option */
if (! im->rigid) {
- return (int)yval;
- } else if ((int)yval > im->yorigin) {
- return im->yorigin+2;
- } else if ((int) yval < im->yorigin - im->ysize){
- return im->yorigin - im->ysize - 2;
- } else {
- return (int)yval;
+ /* keep yval as-is */
+ } else if (yval > im->yorigin) {
+ yval = im->yorigin+2;
+ } else if (yval < im->yorigin - im->ysize){
+ yval = im->yorigin - im->ysize - 2;
}
+ return yval;
}
#endif
}
-
+void
+apply_gridfit(image_desc_t *im)
+{
+ if (isnan(im->minval) || isnan(im->maxval))
+ return;
+ ytr(im,DNAN);
+ if (im->logarithmic) {
+ double ya, yb, ypix, ypixfrac;
+ double log10_range = log10(im->maxval) - log10(im->minval);
+ ya = pow((double)10, floor(log10(im->minval)));
+ while (ya < im->minval)
+ ya *= 10;
+ if (ya > im->maxval)
+ return; /* don't have y=10^x gridline */
+ yb = ya * 10;
+ if (yb <= im->maxval) {
+ /* we have at least 2 y=10^x gridlines.
+ Make sure distance between them in pixels
+ are an integer by expanding im->maxval */
+ double y_pixel_delta = ytr(im, ya) - ytr(im, yb);
+ double factor = y_pixel_delta / floor(y_pixel_delta);
+ 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 */
+ log10_range = log10(im->maxval) - log10(im->minval);
+ }
+ /* make sure first y=10^x gridline is located on
+ integer pixel position by moving scale slightly
+ downwards (sub-pixel movement) */
+ ypix = ytr(im, ya) + im->ysize; /* add im->ysize so it always is positive */
+ ypixfrac = ypix - floor(ypix);
+ if (ypixfrac > 0 && ypixfrac < 1) {
+ 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 */
+ }
+ } else {
+ /* Make sure we have an integer pixel distance between
+ each minor gridline */
+ double ypos1 = ytr(im, im->minval);
+ double ypos2 = ytr(im, im->minval + im->ygrid_scale.gridstep);
+ double y_pixel_delta = ypos1 - ypos2;
+ double factor = y_pixel_delta / floor(y_pixel_delta);
+ double new_range = factor * (im->maxval - im->minval);
+ 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 */
+ /* make sure first minor gridline is on integer pixel y coord */
+ minor_y = gridstep * floor(im->minval / gridstep);
+ while (minor_y < im->minval)
+ minor_y += gridstep;
+ minor_y_px = ytr(im, minor_y) + im->ysize; /* ensure > 0 by adding ysize */
+ minor_y_px_frac = minor_y_px - floor(minor_y_px);
+ if (minor_y_px_frac > 0 && minor_y_px_frac < 1) {
+ double yfrac = minor_y_px_frac / im->ysize;
+ double range = im->maxval - im->minval;
+ im->minval = im->minval - yfrac * range;
+ im->maxval = im->maxval - yfrac * range;
+ ytr(im, DNAN); /* reset precalc */
+ }
+ calc_horizontal_grid(im); /* recalc with changed im->maxval */
+ }
+}
+
/* reduce data reimplementation by Alex */
void
int
-horizontal_grid(image_desc_t *im)
+calc_horizontal_grid(image_desc_t *im)
{
double range;
double scaledrange;
int pixel,i;
- int sgrid,egrid;
- double gridstep;
- double scaledstep;
- char graph_label[100];
- double X0,X1,Y0;
- int labfact,gridind;
+ int gridind;
int decimals, fractionals;
- char labfmt[64];
- labfact=2;
+ im->ygrid_scale.labfact=2;
gridind=-1;
range = im->maxval - im->minval;
scaledrange = range / im->magfact;
fractionals = floor(log10(range));
if(fractionals < 0) /* small amplitude. */
- sprintf(labfmt, "%%%d.%df", decimals - fractionals + 1, -fractionals + 1);
+ sprintf(im->ygrid_scale.labfmt, "%%%d.%df", decimals - fractionals + 1, -fractionals + 1);
else
- sprintf(labfmt, "%%%d.1f", decimals + 1);
- gridstep = pow((double)10, (double)fractionals);
- if(gridstep == 0) /* range is one -> 0.1 is reasonable scale */
- gridstep = 0.1;
+ sprintf(im->ygrid_scale.labfmt, "%%%d.1f", decimals + 1);
+ im->ygrid_scale.gridstep = pow((double)10, (double)fractionals);
+ 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 */
- if(range/gridstep < 5)
- gridstep /= 10;
- if(range/gridstep > 15)
- gridstep *= 10;
- if(range/gridstep > 5) {
- labfact = 1;
- if(range/gridstep > 8)
- labfact = 2;
+ if(range/im->ygrid_scale.gridstep < 5)
+ im->ygrid_scale.gridstep /= 10;
+ if(range/im->ygrid_scale.gridstep > 15)
+ im->ygrid_scale.gridstep *= 10;
+ if(range/im->ygrid_scale.gridstep > 5) {
+ im->ygrid_scale.labfact = 1;
+ if(range/im->ygrid_scale.gridstep > 8)
+ im->ygrid_scale.labfact = 2;
}
else {
- gridstep /= 5;
- labfact = 5;
+ im->ygrid_scale.gridstep /= 5;
+ im->ygrid_scale.labfact = 5;
}
}
else {
for(i=0; i<4;i++) {
if (pixel * ylab[gridind].lfac[i] >= 2 * im->text_prop[TEXT_PROP_AXIS].size) {
- labfact = ylab[gridind].lfac[i];
+ im->ygrid_scale.labfact = ylab[gridind].lfac[i];
break;
}
}
- gridstep = ylab[gridind].grid * im->magfact;
+ im->ygrid_scale.gridstep = ylab[gridind].grid * im->magfact;
}
} else {
- gridstep = im->ygridstep;
- labfact = im->ylabfact;
+ im->ygrid_scale.gridstep = im->ygridstep;
+ im->ygrid_scale.labfact = im->ylabfact;
}
-
- X0=im->xorigin;
- X1=im->xorigin+im->xsize;
+ return 1;
+}
+
+int draw_horizontal_grid(image_desc_t *im)
+{
+ int i;
+ double scaledstep;
+ char graph_label[100];
+ double X0=im->xorigin;
+ double X1=im->xorigin+im->xsize;
- sgrid = (int)( im->minval / gridstep - 1);
- egrid = (int)( im->maxval / gridstep + 1);
- scaledstep = gridstep/im->magfact;
+ 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;
for (i = sgrid; i <= egrid; i++){
- Y0=ytr(im,gridstep*i);
+ double Y0=ytr(im,im->ygrid_scale.gridstep*i);
if ( Y0 >= im->yorigin-im->ysize
&& Y0 <= im->yorigin){
- if(i % labfact == 0){
+ if(i % im->ygrid_scale.labfact == 0){
if (i==0 || im->symbol == ' ') {
if(scaledstep < 1){
if(im->extra_flags & ALTYGRID) {
- sprintf(graph_label,labfmt,scaledstep*i);
+ sprintf(graph_label,im->ygrid_scale.labfmt,scaledstep*i);
}
else {
sprintf(graph_label,"%4.1f",scaledstep*i);
if(im->logarithmic){
res = horizontal_log_grid(im);
} else {
- res = horizontal_grid(im);
+ res = draw_horizontal_grid(im);
}
/* dont draw horizontal grid if there is no min and max val */
expand_range(im); /* make sure the upper and lower limit are
sensible values */
+ 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. ***
im->unitsexponent= 9999;
im->extra_flags= 0;
im->rigid = 0;
+ im->gridfit = 1;
im->imginfo = NULL;
im->lazy = 0;
im->logarithmic = 0;
{"alt-autoscale-max", no_argument, 0, 259 },
{"units-exponent",required_argument, 0, 260},
{"step", required_argument, 0, 261},
+ {"no-gridfit", no_argument, 0, 262},
{0,0,0,0}};
int option_index = 0;
int opt;
case 261:
im->step = atoi(optarg);
break;
+ case 262:
+ im->gridfit = 0;
+ break;
case 's':
if ((parsetime_error = parsetime(optarg, &start_tv))) {
rrd_set_error( "start time: %s", parsetime_error );
diff --git a/src/rrd_graph.h b/src/rrd_graph.h
index de73a72037179f18cbe8acb6edb02daf814af501..6fac38d6ea454f35d3ca4badcd1dc82a947f4caa 100644 (file)
--- a/src/rrd_graph.h
+++ b/src/rrd_graph.h
char *stst; /* strftime string*/
} xlab_t;
+typedef struct ygrid_scale_t { /* y axis grid scaling info */
+ double gridstep;
+ int labfact;
+ char labfmt[64];
+} ygrid_scale_t;
+
/* sensible y label intervals ...*/
typedef struct ylab_t {
rrd_value_t minval,maxval; /* extreme values in the data */
int rigid; /* do not expand range even with
values outside */
+ ygrid_scale_t ygrid_scale; /* calculated y axis grid info */
+ int gridfit; /* adjust y-axis range etc so all
+ grindlines falls in integer pixel values */
char* imginfo; /* construct an <IMG ... tag and return
as first retval */
int lazy; /* only update the image if there is
/* Prototypes */
int xtr(image_desc_t *,time_t);
-int ytr(image_desc_t *, double);
+double ytr(image_desc_t *, double);
enum gf_en gf_conv(char *);
enum gfx_if_en if_conv(char *);
enum tmt_en tmt_conv(char *);
void auto_scale( image_desc_t *, double *, char **, double *);
void si_unit( image_desc_t *);
void expand_range(image_desc_t *);
+void apply_gridfit(image_desc_t *);
void reduce_data( enum cf_en, unsigned long, time_t *, time_t *, unsigned long *, unsigned long *, rrd_value_t **);
int data_fetch( image_desc_t *);
long find_var(image_desc_t *, char *);
time_t find_next_time( time_t, enum tmt_en, long);
int print_calc(image_desc_t *, char ***);
int leg_place(image_desc_t *);
-int horizontal_grid(image_desc_t *);
+int calc_horizontal_grid(image_desc_t *);
+int draw_horizontal_grid(image_desc_t *);
int horizontal_log_grid(image_desc_t *);
void vertical_grid(image_desc_t *);
void axis_paint(image_desc_t *);