fixed setlocale issues
[rrdtool.git] / src / rrd_graph.c
index cc2861f..074ad44 100644 (file)
@@ -4,16 +4,24 @@
  * rrd__graph.c  make creates ne rrds
  ****************************************************************************/
 
-#if 0
-#include "rrd_tool.h"
-#endif
 
 #include <sys/stat.h>
+
+#include "rrd_tool.h"
+
 #ifdef WIN32
 #include <io.h>
 #include <fcntl.h>
 #endif
 
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
 #include "rrd_graph.h"
 #include "rrd_graph_helper.h"
 
@@ -123,7 +131,7 @@ xtr(image_desc_t *im,time_t mytime){
 }
 
 /* translate data values into y coordinates */
-int
+double
 ytr(image_desc_t *im, double value){
     static double pixie;
     double yval;
@@ -134,26 +142,25 @@ ytr(image_desc_t *im, double value){
        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;
 }
 
 
@@ -188,6 +195,7 @@ enum gfx_if_en if_conv(char *string){
     conv_if(PNG,IF_PNG)
     conv_if(SVG,IF_SVG)
     conv_if(EPS,IF_EPS)
+    conv_if(PDF,IF_PDF)
 
     return (-1);
 }
@@ -432,7 +440,73 @@ expand_range(image_desc_t *im)
 #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
@@ -1379,21 +1453,15 @@ leg_place(image_desc_t *im)
 
 
 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;
@@ -1415,25 +1483,25 @@ horizontal_grid(image_desc_t   *im)
            
            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 {
@@ -1447,33 +1515,40 @@ horizontal_grid(image_desc_t   *im)
            
            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);
@@ -1496,16 +1571,18 @@ horizontal_grid(image_desc_t   *im)
                              im->text_prop[TEXT_PROP_AXIS].size,
                              im->tabwidth, 0.0, GFX_H_RIGHT, GFX_V_CENTER,
                              graph_label );
-              gfx_new_line ( im->canvas,
+              gfx_new_dashed_line ( im->canvas,
                              X0-2,Y0,
                              X1+2,Y0,
-                             MGRIDWIDTH, im->graph_col[GRC_MGRID] );          
+                             MGRIDWIDTH, im->graph_col[GRC_MGRID],
+                             im->grid_dash_on, im->grid_dash_off);            
               
            } else {            
-              gfx_new_line ( im->canvas,
+              gfx_new_dashed_line ( im->canvas,
                              X0-1,Y0,
                              X1+1,Y0,
-                             GRIDWIDTH, im->graph_col[GRC_GRID] );            
+                             GRIDWIDTH, im->graph_col[GRC_GRID],
+                             im->grid_dash_on, im->grid_dash_off);            
               
            }       
        }       
@@ -1556,10 +1633,11 @@ horizontal_log_grid(image_desc_t   *im)
        while(yloglab[minoridx][++i] > 0){          
           Y0 = ytr(im,value * yloglab[minoridx][i]);
           if (Y0 <= im->yorigin - im->ysize) break;
-          gfx_new_line ( im->canvas,
+          gfx_new_dashed_line ( im->canvas,
                          X0-1,Y0,
                          X1+1,Y0,
-                         GRIDWIDTH, im->graph_col[GRC_GRID] );
+                         GRIDWIDTH, im->graph_col[GRC_GRID],
+                         im->grid_dash_on, im->grid_dash_off);
        }
     }
 
@@ -1573,10 +1651,11 @@ horizontal_log_grid(image_desc_t   *im)
        while(yloglab[majoridx][++i] > 0){          
           Y0 = ytr(im,value * yloglab[majoridx][i]);    
           if (Y0 <= im->yorigin - im->ysize) break;
-          gfx_new_line ( im->canvas,
+          gfx_new_dashed_line ( im->canvas,
                          X0-2,Y0,
                          X1+2,Y0,
-                         MGRIDWIDTH, im->graph_col[GRC_MGRID] );
+                         MGRIDWIDTH, im->graph_col[GRC_MGRID],
+                         im->grid_dash_on, im->grid_dash_off);
           
           sprintf(graph_label,"%3.0e",value * yloglab[majoridx][i]);
           gfx_new_text ( im->canvas,
@@ -1597,7 +1676,7 @@ vertical_grid(
     image_desc_t   *im )
 {   
     int xlab_sel;              /* which sort of label and grid ? */
-    time_t ti, tilab;
+    time_t ti, tilab, timajor;
     long factor;
     char graph_label[100];
     double X0,Y0,Y1; /* points for filled graph and more*/
@@ -1630,14 +1709,24 @@ vertical_grid(
     /* paint the minor grid */
     for(ti = find_first_time(im->start,
                            im->xlab_user.gridtm,
-                           im->xlab_user.gridst);
+                           im->xlab_user.gridst),
+        timajor = find_first_time(im->start,
+                           im->xlab_user.mgridtm,
+                           im->xlab_user.mgridst);
        ti < im->end; 
        ti = find_next_time(ti,im->xlab_user.gridtm,im->xlab_user.gridst)
        ){
        /* are we inside the graph ? */
        if (ti < im->start || ti > im->end) continue;
+       while (timajor < ti) {
+           timajor = find_next_time(timajor,
+                   im->xlab_user.mgridtm, im->xlab_user.mgridst);
+       }
+       if (ti == timajor) continue; /* skip as falls on major grid line */
        X0 = xtr(im,ti);       
-       gfx_new_line(im->canvas,X0,Y0+1, X0,Y1-1,GRIDWIDTH, im->graph_col[GRC_GRID]);
+       gfx_new_dashed_line(im->canvas,X0,Y0+1, X0,Y1-1,GRIDWIDTH,
+          im->graph_col[GRC_GRID],
+          im->grid_dash_on, im->grid_dash_off);
        
     }
 
@@ -1651,7 +1740,9 @@ vertical_grid(
        /* are we inside the graph ? */
        if (ti < im->start || ti > im->end) continue;
        X0 = xtr(im,ti);
-       gfx_new_line(im->canvas,X0,Y0+2, X0,Y1-2,MGRIDWIDTH, im->graph_col[GRC_MGRID]);
+       gfx_new_dashed_line(im->canvas,X0,Y0+3, X0,Y1-2,MGRIDWIDTH,
+          im->graph_col[GRC_MGRID],
+          im->grid_dash_on, im->grid_dash_off);
        
     }
     /* paint the labels below the graph */
@@ -1751,7 +1842,7 @@ grid_paint(image_desc_t   *im)
        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 */
@@ -2129,6 +2220,11 @@ graph_paint(image_desc_t *im, char ***calcpr)
     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.              ***
@@ -2434,6 +2530,14 @@ rrd_graph(int argc, char **argv, char ***prdata, int *xsize, int *ysize)
 {
     image_desc_t   im;
 
+#ifdef HAVE_TZSET
+    tzset();
+#endif
+#ifdef HAVE_SETLOCALE
+    setlocale(LC_TIME,"");
+#endif
+            
+            
     rrd_graph_init(&im);
 
     rrd_graph_options(argc,argv,&im);
@@ -2507,6 +2611,7 @@ rrd_graph_init(image_desc_t *im)
     im->unitsexponent= 9999;
     im->extra_flags= 0;
     im->rigid = 0;
+    im->gridfit = 1;
     im->imginfo = NULL;
     im->lazy = 0;
     im->logarithmic = 0;
@@ -2518,6 +2623,8 @@ rrd_graph_init(image_desc_t *im)
     im->gdes_c = 0;
     im->gdes = NULL;
     im->canvas = gfx_new_canvas();
+    im->grid_dash_on = 1;
+    im->grid_dash_off = 1;
 
     for(i=0;i<DIM(graph_col);i++)
         im->graph_col[i]=graph_col[i];
@@ -2571,6 +2678,7 @@ rrd_graph_options(int argc, char *argv[],image_desc_t *im)
            {"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;
@@ -2602,6 +2710,9 @@ rrd_graph_options(int argc, char *argv[],image_desc_t *im)
        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 );