#include <gtk/gtk.h>
#include "tickmarks.h"
+#include "graph.h"
struct xyvalue {
double x, y;
char *xtitle;
char *ytitle;
unsigned int xdim, ydim;
+ double xoffset, yoffset;
struct graph_label *labels;
struct graph_label *tail;
int per_label_limit;
const char *font;
+ graph_axis_unit_change_callback x_axis_unit_change_callback;
+ graph_axis_unit_change_callback y_axis_unit_change_callback;
+ double left_extra;
+ double right_extra;
+ double top_extra;
+ double bottom_extra;
};
+void graph_set_size(struct graph *g, unsigned int xdim, unsigned int ydim)
+{
+ g->xdim = xdim;
+ g->ydim = ydim;
+}
+
+void graph_set_position(struct graph *g, double xoffset, double yoffset)
+{
+ g->xoffset = xoffset;
+ g->yoffset = yoffset;
+}
+
struct graph *graph_new(unsigned int xdim, unsigned int ydim, const char *font)
{
struct graph *g;
g = calloc(1, sizeof(*g));
- g->xdim = xdim;
- g->ydim = ydim;
+ graph_set_size(g, xdim, ydim);
g->per_label_limit = -1;
g->font = font;
if (!g->font)
return g;
}
+void graph_x_axis_unit_change_notify(struct graph *g, graph_axis_unit_change_callback f)
+{
+ g->x_axis_unit_change_callback = f;
+}
+
+void graph_y_axis_unit_change_notify(struct graph *g, graph_axis_unit_change_callback f)
+{
+ g->y_axis_unit_change_callback = f;
+}
+
static int count_labels(struct graph_label *labels)
{
int count = 0;
}
}
-static void draw_centered_text(struct graph *g, cairo_t *cr, double x, double y,
- double fontsize, const char *text)
+static void draw_aligned_text(struct graph *g, cairo_t *cr, double x, double y,
+ double fontsize, const char *text, int alignment)
{
+#define CENTERED 0
+#define LEFT_JUSTIFIED 1
+#define RIGHT_JUSTIFIED 2
+
+ double factor, direction;
cairo_text_extents_t extents;
+ 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;
+ }
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 - (extents.width / 2 + extents.x_bearing);
+ 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)
cairo_set_source_rgb(cr, 0, 0, 0);
cairo_set_line_width (cr, 0.8);
- /* for now just set margins at 10% of width. This is not very good. */
- *x1 = g->xdim / 10.0;
- *x2 = 9.0 * *x1;
- *y1 = g->ydim / 10.0;
- *y2 = 9.0 * *y1;
+ *x1 = 0.15 * g->xdim;
+ *x2 = 0.95 * g->xdim;
+ *y1 = 0.10 * g->ydim;
+ *y2 = 0.90 * g->ydim;
cairo_move_to(cr, *x1, *y1);
cairo_line_to(cr, *x1, *y2);
{
struct tickmark *tm;
double tx;
- int i;
+ int i, power_of_ten;
static double dash[] = { 1.0, 2.0 };
- nticks = calc_tickmarks(minx, maxx, nticks, &tm);
+ nticks = calc_tickmarks(minx, maxx, nticks, &tm, &power_of_ten,
+ g->x_axis_unit_change_callback == NULL);
+ if (g->x_axis_unit_change_callback)
+ g->x_axis_unit_change_callback(g, power_of_ten);
for (i = 0; i < nticks; i++) {
tx = (((tm[i].value) - minx) / (maxx - minx)) * (x2 - x1) + x1;
{
struct tickmark *tm;
double ty;
- int i;
+ int i, power_of_ten;
static double dash[] = { 2.0, 2.0 };
- nticks = calc_tickmarks(miny, maxy, nticks, &tm);
+ nticks = calc_tickmarks(miny, maxy, nticks, &tm, &power_of_ten,
+ g->y_axis_unit_change_callback == NULL);
+ if (g->y_axis_unit_change_callback)
+ g->y_axis_unit_change_callback(g, power_of_ten);
for (i = 0; i < nticks; i++) {
ty = y2 - (((tm[i].value) - miny) / (maxy - miny)) * (y2 - y1);
cairo_restore(cr);
/* draw tickmark label */
- draw_centered_text(g, cr, x1 - (x2 - x1) * 0.04, ty, 12.0, tm[i].string);
+ draw_right_justified_text(g, cr, x1 - (x2 - x1) * 0.025, ty, 12.0, tm[i].string);
cairo_stroke(cr);
}
}
struct graph_label *lb;
cairo_save(cr);
+ cairo_translate(cr, bg->xoffset, bg->yoffset);
graph_draw_common(bg, cr, &x1, &y1, &x2, &y2);
nlabels = count_labels(bg->labels);
static double find_xy_value(struct graph *g, xy_value_extractor getvalue, double_comparator cmp)
{
- double tmp, answer;
+ double tmp, answer = 0.0;
struct graph_label *i;
struct graph_value *j;
int first = 1;
void line_graph_draw(struct graph *g, cairo_t *cr)
{
double x1, y1, x2, y2;
- double minx, miny, maxx, maxy;
- double tx, ty;
+ double minx, miny, maxx, maxy, gminx, gminy, gmaxx, gmaxy;
+ double tx, ty, top_extra, bottom_extra, left_extra, right_extra;
struct graph_label *i;
struct graph_value *j;
int good_data = 1, first = 1;
cairo_save(cr);
+ cairo_translate(cr, g->xoffset, g->yoffset);
graph_draw_common(g, cr, &x1, &y1, &x2, &y2);
minx = find_xy_value(g, getx, mindouble);
maxy = 100.0;
}
- graph_draw_x_ticks(g, cr, x1, y1, x2, y2, minx, maxx, 10);
- graph_draw_y_ticks(g, cr, x1, y1, x2, y2, miny, maxy, 10);
+ top_extra = 0.0;
+ bottom_extra = 0.0;
+ left_extra = 0.0;
+ right_extra = 0.0;
+
+ if (g->top_extra > 0.001)
+ top_extra = fabs(maxy - miny) * g->top_extra;
+ if (g->bottom_extra > 0.001)
+ bottom_extra = fabs(maxy - miny) * g->bottom_extra;
+ if (g->left_extra > 0.001)
+ left_extra = fabs(maxx - minx) * g->left_extra;
+ if (g->right_extra > 0.001)
+ right_extra = fabs(maxx - minx) * g->right_extra;
+
+ gminx = minx - left_extra;
+ gmaxx = maxx + right_extra;
+ gminy = miny - bottom_extra;
+ gmaxy = maxy + top_extra;
+
+ graph_draw_x_ticks(g, cr, x1, y1, x2, y2, gminx, gmaxx, 10);
+ graph_draw_y_ticks(g, cr, x1, y1, x2, y2, gminy, gmaxy, 10);
if (!good_data)
goto skip_data;
continue;
cairo_set_source_rgb(cr, i->r, i->g, i->b);
for (j = i->values; j; j = j->next) {
- tx = ((getx(j) - minx) / (maxx - minx)) * (x2 - x1) + x1;
- ty = y2 - ((gety(j) - miny) / (maxy - miny)) * (y2 - y1);
+ tx = ((getx(j) - gminx) / (gmaxx - gminx)) * (x2 - x1) + x1;
+ ty = y2 - ((gety(j) - gminy) / (gmaxy - gminy)) * (y2 - y1);
if (first) {
cairo_move_to(cr, tx, ty);
first = 0;
if (i->parent->per_label_limit != -1 &&
i->value_count > i->parent->per_label_limit) {
- x = i->values;
- i->values = i->values->next;
- free(x->value);
- free(x);
- i->value_count--;
+ int to_drop = 1;
+
+ /*
+ * If the limit was dynamically reduced, making us more
+ * than 1 entry ahead after adding this one, drop two
+ * entries. This will make us (eventually) reach the
+ * specified limit.
+ */
+ if (i->value_count - i->parent->per_label_limit >= 2)
+ to_drop = 2;
+
+ while (to_drop--) {
+ x = i->values;
+ i->values = i->values->next;
+ free(x->value);
+ free(x);
+ i->value_count--;
+ }
}
}
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)
+{
+ g->left_extra = left_percent;
+ g->right_extra = right_percent;
+ g->top_extra = top_percent;
+ g->bottom_extra = bottom_percent;
+}
+
+