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->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;
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;
{
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);
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);
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;
+}
+
+