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,
}
/* 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 );
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 *);