fio: update FSF address
[fio.git] / graph.c
diff --git a/graph.c b/graph.c
index 146ed7b148d9c7594c0002be5cfff0884896c888..f82b52add468f95b5d8d180618a971f04bb79cc6 100644 (file)
--- a/graph.c
+++ b/graph.c
@@ -1,7 +1,7 @@
 /*
  * gfio - gui front end for fio - the flexible io tester
  *
- * Copyright (C) 2012 Stephen M. Cameron <stephenmcameron@gmail.com> 
+ * Copyright (C) 2012 Stephen M. Cameron <stephenmcameron@gmail.com>
  *
  * The license below covers all files distributed with fio unless otherwise
  * noted in the file itself.
@@ -17,7 +17,7 @@
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  *
  */
 #include <string.h>
@@ -33,6 +33,7 @@
 #include "graph.h"
 #include "flist.h"
 #include "lib/prio_tree.h"
+#include "cairo_text_helpers.h"
 
 /*
  * Allowable difference to show tooltip
@@ -45,6 +46,7 @@ struct xyvalue {
 
 enum {
        GV_F_ON_PRIO    = 1,
+       GV_F_PRIO_SKIP  = 2,
 };
 
 struct graph_value {
@@ -62,6 +64,7 @@ struct graph_label {
        struct flist_head value_list;
        struct prio_tree_root prio_tree;
        double r, g, b;
+       int hide;
        int value_count;
        struct graph *parent;
 };
@@ -83,10 +86,11 @@ struct graph {
        graph_axis_unit_change_callback x_axis_unit_change_callback;
        graph_axis_unit_change_callback y_axis_unit_change_callback;
        unsigned int base_offset;
-       double left_extra;      
-       double right_extra;     
-       double top_extra;       
-       double bottom_extra;    
+       unsigned int dont_graph_all_zeroes;
+       double left_extra;
+       double right_extra;
+       double top_extra;
+       double bottom_extra;
 
        double xtick_zero;
        double xtick_delta;
@@ -120,10 +124,15 @@ struct graph *graph_new(unsigned int xdim, unsigned int ydim, const char *font)
        g->per_label_limit = -1;
        g->font = font;
        if (!g->font)
-               g->font = "Sans";
+               g->font = GRAPH_DEFAULT_FONT;
        return g;
 }
 
+void graph_set_font(struct graph *g, const char *font)
+{
+       g->font = font;
+}
+
 void graph_x_axis_unit_change_notify(struct graph *g, graph_axis_unit_change_callback f)
 {
        g->x_axis_unit_change_callback = f;
@@ -171,11 +180,12 @@ static double maxdouble(double a, double b)
 static double find_double_values(struct graph_label *l, double_comparator cmp)
 {
        struct flist_head *entry;
-       double answer, tmp;
+       double answer = 0.0, tmp;
        int first = 1;
 
-       assert(!flist_empty(&l->value_list));
-       answer = 0.0; /* shut the compiler up, might need to think harder though. */
+       if (flist_empty(&l->value_list))
+               return 0.0;
+
        flist_for_each(entry, &l->value_list) {
                struct graph_value *i;
 
@@ -198,8 +208,9 @@ static double find_double_data(struct graph *g, double_comparator cmp)
        int first = 1;
        double answer, tmp;
 
-       assert(!flist_empty(&g->label_list));
-       answer = 0.0; /* shut the compiler up, might need to think harder though. */
+       if (flist_empty(&g->label_list))
+               return 0.0;
+
        flist_for_each(entry, &g->label_list) {
                i = flist_entry(entry, struct graph_label, list);
                tmp = find_double_values(i, cmp);
@@ -252,101 +263,39 @@ static void draw_bars(struct graph *bg, cairo_t *cr, struct graph_label *lb,
                cairo_close_path(cr);
                cairo_fill(cr);
                cairo_stroke(cr);
-               bar_num++;      
+               bar_num++;
        }
 }
 
-static void draw_aligned_text(struct graph *g, cairo_t *cr, double x, double y,
-                              double fontsize, const char *text, int alignment)
+static void graph_draw_common(struct graph *g, cairo_t *cr, double *x1,
+                             double *y1, double *x2, double *y2)
 {
-#define CENTERED 0
-#define LEFT_JUSTIFIED 1
-#define RIGHT_JUSTIFIED 2
+       const double shade_col[3][3] = { { 0.55, 0.54, 0.54 },
+                                        { 0.80, 0.78, 0.78 },
+                                        { 0.93, 0.91, 0.91 } };
+       int i;
 
-       double factor, direction;
-       cairo_text_extents_t extents;
+       *x1 = 0.10 * g->xdim;
+       *x2 = 0.95 * g->xdim;
+       *y1 = 0.10 * g->ydim;
+       *y2 = 0.90 * g->ydim;
 
-       switch(alignment) {
-               case CENTERED:
-                       direction = -1.0;
-                       factor = 0.5;
-                       break;
-               case RIGHT_JUSTIFIED:
-                       direction = -1.0;
-                       factor = 1.0;
-                       break;
-               case LEFT_JUSTIFIED:
-               default:
-                       direction = 1.0;
-                       factor = 1.0;
-                       break;
+       /*
+        * Add shade
+        */
+       cairo_set_line_width(cr, 1.0);
+       for (i = 0; i < 3; i++) {
+               float offset = i + 1.0;
+
+               cairo_set_source_rgb(cr, shade_col[i][0], shade_col[i][1], shade_col[i][2]);
+               cairo_move_to(cr, offset + *x1, *y1 - offset);
+               cairo_line_to(cr, *x2 + offset, *y1 - offset);
+               cairo_line_to(cr, *x2 + offset, *y2 - offset);
+               cairo_stroke(cr);
        }
-       cairo_select_font_face (cr, g->font, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
-
-       cairo_set_font_size(cr, fontsize);
-       cairo_text_extents(cr, text, &extents);
-       x = x + direction * (factor * extents.width  + extents.x_bearing);
-       y = y - (extents.height / 2 + extents.y_bearing);
 
-       cairo_move_to(cr, x, y);
-       cairo_show_text(cr, text);
-}
-
-static inline void draw_centered_text(struct graph *g, cairo_t *cr, double x, double y,
-                              double fontsize, const char *text)
-{
-       draw_aligned_text(g, cr, x, y, fontsize, text, CENTERED);
-}
-
-static inline void draw_right_justified_text(struct graph *g, cairo_t *cr,
-                               double x, double y,
-                               double fontsize, const char *text)
-{
-       draw_aligned_text(g, cr, x, y, fontsize, text, RIGHT_JUSTIFIED);
-}
-
-static inline void draw_left_justified_text(struct graph *g, cairo_t *cr,
-                               double x, double y,
-                               double fontsize, const char *text)
-{
-       draw_aligned_text(g, cr, x, y, fontsize, text, LEFT_JUSTIFIED);
-}
-
-static void draw_vertical_centered_text(struct graph *g, cairo_t *cr, double x,
-                                       double y, double fontsize,
-                                       const char *text)
-{
-       double sx, sy;
-       cairo_text_extents_t extents;
-
-       cairo_select_font_face(cr, g->font, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
-
-       cairo_set_font_size(cr, fontsize);
-       cairo_text_extents(cr, text, &extents);
-       sx = x;
-       sy = y;
-       y = y + (extents.width / 2.0 + extents.x_bearing);
-       x = x - (extents.height / 2.0 + extents.y_bearing);
-
-       cairo_move_to(cr, x, y);
-       cairo_save(cr);
-       cairo_translate(cr, -sx, -sy);
-       cairo_rotate(cr, -90.0 * M_PI / 180.0);
-       cairo_translate(cr, sx, sy);
-       cairo_show_text(cr, text);
-       cairo_restore(cr);
-}
-
-static void graph_draw_common(struct graph *g, cairo_t *cr,
-       double *x1, double *y1, double *x2, double *y2)
-{
-        cairo_set_source_rgb(cr, 0, 0, 0);
-        cairo_set_line_width (cr, 0.8);
-
-       *x1 = 0.10 * g->xdim;   
-       *x2 = 0.95 * g->xdim;
-       *y1 = 0.10 * g->ydim;   
-       *y2 = 0.90 * g->ydim;
+       cairo_set_source_rgb(cr, 0, 0, 0);
+       cairo_set_line_width(cr, 1.2);
 
        cairo_move_to(cr, *x1, *y1);
        cairo_line_to(cr, *x1, *y2);
@@ -355,9 +304,9 @@ static void graph_draw_common(struct graph *g, cairo_t *cr,
        cairo_line_to(cr, *x1, *y1);
        cairo_stroke(cr);
 
-       draw_centered_text(g, cr, g->xdim / 2, g->ydim / 20, 20.0, g->title);
-       draw_centered_text(g, cr, g->xdim / 2, g->ydim * 0.97, 14.0, g->xtitle);
-       draw_vertical_centered_text(g, cr, g->xdim * 0.02, g->ydim / 2, 14.0, g->ytitle);
+       draw_centered_text(cr, g->font, g->xdim / 2, g->ydim / 20, 20.0, g->title);
+       draw_centered_text(cr, g->font, g->xdim / 2, g->ydim * 0.97, 14.0, g->xtitle);
+       draw_vertical_centered_text(cr, g->font, g->xdim * 0.02, g->ydim / 2, 14.0, g->ytitle);
        cairo_stroke(cr);
 }
 
@@ -394,15 +343,15 @@ static void graph_draw_x_ticks(struct graph *g, cairo_t *cr,
                        continue;
 
                /* Draw tick mark */
-               cairo_set_line_width(cr, 0.8);
+               cairo_set_line_width(cr, 1.0);
                cairo_move_to(cr, tx, y2);
                cairo_line_to(cr, tx, y2 + (y2 - y1) * 0.03);
                cairo_stroke(cr);
 
                /* draw grid lines */
                cairo_save(cr);
-               cairo_set_dash(cr, dash, 2, 2.0);
-               cairo_set_line_width(cr, 0.5);
+               cairo_set_dash(cr, dash, 2, 0.66);
+               cairo_set_line_width(cr, 0.33);
                cairo_move_to(cr, tx, y1);
                cairo_line_to(cr, tx, y2);
                cairo_stroke(cr);
@@ -412,7 +361,7 @@ static void graph_draw_x_ticks(struct graph *g, cairo_t *cr,
                        continue;
 
                /* draw tickmark label */
-               draw_centered_text(g, cr, tx, y2 * 1.04, 12.0, tm[i].string);
+               draw_centered_text(cr, g->font, tx, y2 * 1.04, 12.0, tm[i].string);
                cairo_stroke(cr);
        }
 }
@@ -424,7 +373,7 @@ static double graph_draw_y_ticks(struct graph *g, cairo_t *cr,
        struct tickmark *tm;
        double ty;
        int i, power_of_ten;
-       static double dash[] = { 2.0, 2.0 };
+       static double dash[] = { 1.0, 2.0 };
 
        nticks = calc_tickmarks(miny, maxy, nticks, &tm, &power_of_ten,
                g->y_axis_unit_change_callback == NULL, g->base_offset);
@@ -463,8 +412,8 @@ static double graph_draw_y_ticks(struct graph *g, cairo_t *cr,
 
                /* draw grid lines */
                cairo_save(cr);
-               cairo_set_dash(cr, dash, 2, 2.0);
-               cairo_set_line_width(cr, 0.5);
+               cairo_set_dash(cr, dash, 2, 0.66);
+               cairo_set_line_width(cr, 0.33);
                cairo_move_to(cr, x1, ty);
                cairo_line_to(cr, x2, ty);
                cairo_stroke(cr);
@@ -474,7 +423,7 @@ static double graph_draw_y_ticks(struct graph *g, cairo_t *cr,
                        continue;
 
                /* draw tickmark label */
-               draw_right_justified_text(g, cr, x1 - (x2 - x1) * 0.025, ty, 12.0, tm[i].string);
+               draw_right_justified_text(cr, g->font, x1 - (x2 - x1) * 0.025, ty, 12.0, tm[i].string);
                cairo_stroke(cr);
        }
 
@@ -511,7 +460,7 @@ void bar_graph_draw(struct graph *bg, cairo_t *cr)
        maxdata = find_max_data(bg);
 
        if (fabs(maxdata - mindata) < 1e-20) {
-               draw_centered_text(bg, cr,
+               draw_centered_text(cr, bg->font,
                        x1 + (x2 - x1) / 2.0,
                        y1 + (y2 - y1) / 2.0, 20.0, "No good data");
                return;
@@ -528,8 +477,8 @@ void bar_graph_draw(struct graph *bg, cairo_t *cr)
                label_offset = bg->xdim * 0.1 + space_per_label * (double) i + space_per_label * 0.1;
                draw_bars(bg, cr, lb, label_offset, bar_width, mindata, maxdata);
                // draw_centered_text(cr, label_offset + (bar_width / 2.0 + bar_width * 0.1), bg->ydim * 0.93,
-               draw_centered_text(bg, cr, x1 + space_per_label * (i + 0.5), bg->ydim * 0.93,
-                       12.0, lb->label); 
+               draw_centered_text(cr, bg->font, x1 + space_per_label * (i + 0.5), bg->ydim * 0.93,
+                       12.0, lb->label);
                i++;
        }
        cairo_stroke(cr);
@@ -568,12 +517,12 @@ static double find_xy_value(struct graph *g, xy_value_extractor getvalue, double
                                first = 0;
                                answer = tmp;
                        }
-                       answer = cmp(tmp, answer);      
+                       answer = cmp(tmp, answer);
                }
        }
 
        return answer;
-} 
+}
 
 void line_graph_draw(struct graph *g, cairo_t *cr)
 {
@@ -637,10 +586,12 @@ void line_graph_draw(struct graph *g, cairo_t *cr)
                goto skip_data;
 
        cairo_set_line_width(cr, 1.5);
+       cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND);
+
        flist_for_each(lentry, &g->label_list) {
                i = flist_entry(lentry, struct graph_label, list);
                first = 1;
-               if (i->r < 0) /* invisible data */
+               if (i->hide || i->r < 0) /* invisible data */
                        continue;
 
                cairo_set_source_rgb(cr, i->r, i->g, i->b);
@@ -651,9 +602,8 @@ void line_graph_draw(struct graph *g, cairo_t *cr)
                        if (first) {
                                cairo_move_to(cr, tx, ty);
                                first = 0;
-                       } else {
+                       } else
                                cairo_line_to(cr, tx, ty);
-                       }
                }
                cairo_stroke(cr);
        }
@@ -688,7 +638,7 @@ static struct graph_label *graph_find_label(struct graph *bg,
 {
        struct flist_head *entry;
        struct graph_label *i;
-       
+
        flist_for_each(entry, &bg->label_list) {
                i = flist_entry(entry, struct graph_label, list);
 
@@ -699,24 +649,25 @@ static struct graph_label *graph_find_label(struct graph *bg,
        return NULL;
 }
 
-void graph_add_label(struct graph *bg, const char *label)
+graph_label_t graph_add_label(struct graph *bg, const char *label)
 {
        struct graph_label *i;
-       
+
        i = graph_find_label(bg, label);
        if (i)
-               return; /* already present. */
+               return i; /* already present. */
        i = calloc(1, sizeof(*i));
        INIT_FLIST_HEAD(&i->value_list);
        i->parent = bg;
        setstring(&i->label, label);
        flist_add_tail(&i->list, &bg->label_list);
        INIT_PRIO_TREE_ROOT(&i->prio_tree);
+       return i;
 }
 
 static void __graph_value_drop(struct graph_label *l, struct graph_value *v)
 {
-       flist_del(&v->list);
+       flist_del_init(&v->list);
        if (v->tooltip)
                free(v->tooltip);
        free(v->value);
@@ -726,14 +677,17 @@ static void __graph_value_drop(struct graph_label *l, struct graph_value *v)
 
 static void graph_value_drop(struct graph_label *l, struct graph_value *v)
 {
-       struct flist_head *entry, *tmp;
+       if (v->flags & GV_F_PRIO_SKIP) {
+               __graph_value_drop(l, v);
+               return;
+       }
 
        /*
         * Find head, the guy that's on the prio tree
         */
        while (!(v->flags & GV_F_ON_PRIO)) {
                assert(!flist_empty(&v->alias));
-               v = flist_entry(v->alias.next, struct graph_value, alias);
+               v = flist_first_entry(&v->alias, struct graph_value, alias);
        }
 
        prio_tree_remove(&l->prio_tree, &v->node);
@@ -741,11 +695,11 @@ static void graph_value_drop(struct graph_label *l, struct graph_value *v)
        /*
         * Free aliases
         */
-       flist_for_each_safe(entry, tmp, &v->alias) {
+       while (!flist_empty(&v->alias)) {
                struct graph_value *a;
 
-               a = flist_entry(entry, struct graph_value, alias);
-               flist_del(&a->alias);
+               a = flist_first_entry(&v->alias, struct graph_value, alias);
+               flist_del_init(&a->alias);
 
                __graph_value_drop(l, a);
        }
@@ -802,7 +756,8 @@ static void graph_label_add_value(struct graph_label *i, void *value,
                        flist_add_tail(&x->alias, &alias->alias);
                } else
                        x->flags = GV_F_ON_PRIO;
-       }
+       } else
+               x->flags = GV_F_PRIO_SKIP;
 
        if (g->per_label_limit != -1 &&
                i->value_count > g->per_label_limit) {
@@ -818,7 +773,7 @@ static void graph_label_add_value(struct graph_label *i, void *value,
                        to_drop = 2;
 
                while (to_drop-- && !flist_empty(&i->value_list)) {
-                       x = flist_entry(i->value_list.next, struct graph_value, list);
+                       x = flist_first_entry(&i->value_list, struct graph_value, list);
                        graph_value_drop(i, x);
 
                        /*
@@ -830,35 +785,48 @@ static void graph_label_add_value(struct graph_label *i, void *value,
        }
 }
 
-int graph_add_data(struct graph *bg, const char *label, const double value)
+int graph_add_data(struct graph *bg, graph_label_t label, const double value)
 {
-       struct graph_label *i;
+       struct graph_label *i = label;
        double *d;
 
        d = malloc(sizeof(*d));
        *d = value;
 
-       i = graph_find_label(bg, label);
-       if (!i)
-               return -1;
        graph_label_add_value(i, d, NULL);
        return 0;
 }
 
-int graph_add_xy_data(struct graph *bg, const char *label,
+static int graph_nonzero_y(struct graph_label *l)
+{
+       struct flist_head *entry;
+
+       flist_for_each(entry, &l->value_list) {
+               struct graph_value *v;
+
+               v = flist_entry(entry, struct graph_value, list);
+               if (gety(v) != 0.0)
+                       return 1;
+       }
+
+       return 0;
+}
+
+int graph_add_xy_data(struct graph *bg, graph_label_t label,
                      const double x, const double y, const char *tooltip)
 {
-       struct graph_label *i;
+       struct graph_label *i = label;
        struct xyvalue *xy;
 
+       if (bg->dont_graph_all_zeroes && y == 0.0 && !graph_nonzero_y(i))
+               i->hide = 1;
+       else
+               i->hide = 0;
+
        xy = malloc(sizeof(*xy));
        xy->x = x;
        xy->y = y;
 
-       i = graph_find_label(bg, label);
-       if (!i)
-               return -1;
-
        graph_label_add_value(i, xy, tooltip);
        return 0;
 }
@@ -868,9 +836,9 @@ static void graph_free_values(struct graph_label *l)
        struct graph_value *i;
 
        while (!flist_empty(&l->value_list)) {
-               i = flist_entry(l->value_list.next, struct graph_value, list);
+               i = flist_first_entry(&l->value_list, struct graph_value, list);
                graph_value_drop(l, i);
-       }       
+       }
 }
 
 static void graph_free_labels(struct graph *g)
@@ -878,18 +846,28 @@ static void graph_free_labels(struct graph *g)
        struct graph_label *i;
 
        while (!flist_empty(&g->label_list)) {
-               i = flist_entry(g->label_list.next, struct graph_label, list);
+               i = flist_first_entry(&g->label_list, struct graph_label, list);
                flist_del(&i->list);
                graph_free_values(i);
                free(i);
-       }       
+       }
 }
 
-void graph_set_color(struct graph *gr, const char *label,
-       double red, double green, double blue)
+void graph_clear_values(struct graph *g)
 {
-       struct flist_head *entry;
+       struct flist_head *node;
        struct graph_label *i;
+
+       flist_for_each(node, &g->label_list) {
+               i = flist_entry(node, struct graph_label, list);
+               graph_free_values(i);
+       }
+}
+
+void graph_set_color(struct graph *gr, graph_label_t label, double red,
+                    double green, double blue)
+{
+       struct graph_label *i = label;
        double r, g, b;
 
        if (red < 0.0) { /* invisible color */
@@ -909,16 +887,9 @@ void graph_set_color(struct graph *gr, const char *label,
                        b = 1.0;
        }
 
-       flist_for_each(entry, &gr->label_list) {
-               i = flist_entry(entry, struct graph_label, list);
-
-               if (strcmp(i->label, label) == 0) {
-                       i->r = r;       
-                       i->g = g;       
-                       i->b = b;       
-                       break;
-               }
-       }
+       i->r = r;
+       i->g = g;
+       i->b = b;
 }
 
 void graph_free(struct graph *bg)
@@ -938,13 +909,14 @@ void line_graph_set_data_count_limit(struct graph *g, int per_label_limit)
        g->per_label_limit = per_label_limit;
 }
 
-void graph_add_extra_space(struct graph *g, double left_percent, double right_percent,
-                                double top_percent, double bottom_percent)
+void graph_add_extra_space(struct graph *g, double left_percent,
+                          double right_percent, double top_percent,
+                          double bottom_percent)
 {
-       g->left_extra = left_percent;   
-       g->right_extra = right_percent; 
-       g->top_extra = top_percent;     
-       g->bottom_extra = bottom_percent;       
+       g->left_extra = left_percent;
+       g->right_extra = right_percent;
+       g->top_extra = top_percent;
+       g->bottom_extra = bottom_percent;
 }
 
 /*
@@ -1006,6 +978,8 @@ const char *graph_find_tooltip(struct graph *g, int ix, int iy)
                struct graph_label *i;
 
                i = flist_entry(entry, struct graph_label, list);
+               if (i->hide)
+                       continue;
 
                INIT_PRIO_TREE_ITER(&iter);
                prio_tree_iter_init(&iter, &i->prio_tree, x, x);
@@ -1036,14 +1010,15 @@ const char *graph_find_tooltip(struct graph *g, int ix, int iy)
                                        }
                                }
                                if (!flist_empty(&v->alias))
-                                       v = flist_entry(v->alias.next, struct graph_value, alias);
+                                       v = flist_first_entry(&v->alias, struct graph_value, alias);
                        } while (v != rootv);
                } while ((n = prio_tree_next(&iter)) != NULL);
 
                /*
                 * If we got matches in one label, don't check others.
                 */
-               break;
+               if (best)
+                       break;
        }
 
        if (best)
@@ -1051,3 +1026,8 @@ const char *graph_find_tooltip(struct graph *g, int ix, int iy)
 
        return NULL;
 }
+
+void graph_set_graph_all_zeroes(struct graph *g, unsigned int set)
+{
+       g->dont_graph_all_zeroes = !set;
+}