/** * uber_line_graph_stylize_line: * @graph: A #UberLineGraph. * * XXX * * Returns: None. * Side effects: None. */ static void uber_line_graph_stylize_line (UberLineGraph *graph, /* IN */ LineInfo *info, /* IN */ cairo_t *cr) /* IN */ { UberLineGraphPrivate *priv; g_return_if_fail(UBER_IS_LINE_GRAPH(graph)); g_return_if_fail(info != NULL); priv = graph->priv; if (info->dashes) { cairo_set_dash(cr, info->dashes, info->num_dashes, info->dash_offset); } else { cairo_set_dash(cr, NULL, 0, 0); } cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND); cairo_set_line_width(cr, info->width); cairo_set_antialias(cr, priv->antialias); cairo_set_source_rgba(cr, info->color.red / 65535., info->color.green / 65535., info->color.blue / 65535., info->alpha); }
/** * uber_line_graph_set_line_dash: * @graph: A #UberLineGraph. * * XXX * * Returns: None. * Side effects: None. */ void uber_line_graph_set_line_dash (UberLineGraph *graph, /* IN */ guint line, /* IN */ const gdouble *dashes, /* IN */ gint num_dashes, /* IN */ gdouble offset) /* IN */ { UberLineGraphPrivate *priv; LineInfo *info; g_return_if_fail(UBER_IS_LINE_GRAPH(graph)); g_return_if_fail(dashes != NULL); g_return_if_fail(num_dashes > 0); g_return_if_fail(line > 0); g_return_if_fail(line <= graph->priv->lines->len); priv = graph->priv; info = &g_array_index(priv->lines, LineInfo, line - 1); if (info->dashes) { g_free(info->dashes); info->dashes = NULL; info->num_dashes = 0; info->dash_offset = 0; } if (dashes) { info->dashes = g_new0(gdouble, num_dashes); memcpy(info->dashes, dashes, sizeof(gdouble) * num_dashes); info->num_dashes = num_dashes; info->dash_offset = offset; } }
/** * uber_line_graph_get_antialias: * @graph: A #UberLineGraph. * * Retrieves the antialias mode for the graph. * * Returns: A cairo_antialias_t. * Side effects: None. */ cairo_antialias_t uber_line_graph_get_antialias (UberLineGraph *graph) /* IN */ { g_return_val_if_fail(UBER_IS_LINE_GRAPH(graph), 0); return graph->priv->antialias; }
/** * uber_line_graph_get_next_data: * @graph: A #UberGraph. * * XXX * * Returns: None. * Side effects: None. */ static gboolean uber_line_graph_get_next_data (UberGraph *graph) /* IN */ { UberLineGraphPrivate *priv; LineInfo *line; gdouble val; gboolean ret = FALSE; gint i; g_return_val_if_fail(UBER_IS_LINE_GRAPH(graph), FALSE); priv = UBER_LINE_GRAPH(graph)->priv; /* * Retrieve the next data point. */ if (priv->func) { for (i = 0; i < priv->lines->len; i++) { val = 0.; line = &g_array_index(priv->lines, LineInfo, i); if (!(ret = priv->func(UBER_LINE_GRAPH(graph), i + 1, &val, priv->func_data))) { val = -INFINITY; } g_ring_append_val(line->raw_data, val); /* * TODO: Scale value. */ g_ring_append_val(line->scaled_data, val); } } return ret; }
/** * uber_line_graph_set_stride: * @graph: A #UberGraph. * @stride: The number of data points within the graph. * * XXX * * Returns: None. * Side effects: None. */ static void uber_line_graph_set_stride (UberGraph *graph, /* IN */ guint stride) /* IN */ { UberLineGraphPrivate *priv; LineInfo *line; gint i; g_return_if_fail(UBER_IS_LINE_GRAPH(graph)); priv = UBER_LINE_GRAPH(graph)->priv; priv->stride = stride; /* * TODO: Support changing stride after lines have been added. */ if (priv->lines->len) { for (i = 0; i < priv->lines->len; i++) { line = &g_array_index(priv->lines, LineInfo, i); g_ring_unref(line->raw_data); g_ring_unref(line->scaled_data); line->raw_data = g_ring_sized_new(sizeof(gdouble), priv->stride, NULL); line->scaled_data = g_ring_sized_new(sizeof(gdouble), priv->stride, NULL); uber_line_graph_init_ring(line->raw_data); uber_line_graph_init_ring(line->scaled_data); } return; } }
/** * uber_line_graph_set_autoscale: * @graph: A #UberLineGraph. * @autoscale: Should we autoscale. * * Sets if we should autoscale the range of the graph when a new input * value is outside the visible range. * * Returns: None. * Side effects: None. */ void uber_line_graph_set_autoscale (UberLineGraph *graph, /* IN */ gboolean autoscale) /* IN */ { UberLineGraphPrivate *priv; g_return_if_fail(UBER_IS_LINE_GRAPH(graph)); priv = graph->priv; priv->autoscale = autoscale; }
/** * uber_line_graph_get_yrange: * @graph: A #UberGraph. * * XXX * * Returns: None. * Side effects: None. */ static void uber_line_graph_get_yrange (UberGraph *graph, /* IN */ UberRange *range) /* OUT */ { UberLineGraphPrivate *priv; g_return_if_fail(UBER_IS_LINE_GRAPH(graph)); g_return_if_fail(range != NULL); priv = UBER_LINE_GRAPH(graph)->priv; *range = priv->range; }
/** * uber_line_graph_set_range: * @graph: A #UberLineGraph. * * XXX * * Returns: None. * Side effects: None. */ void uber_line_graph_set_range (UberLineGraph *graph, /* IN */ const UberRange *range) /* IN */ { UberLineGraphPrivate *priv; g_return_if_fail(UBER_IS_LINE_GRAPH(graph)); g_return_if_fail(range != NULL); priv = graph->priv; priv->range = *range; }
/** * uber_line_graph_set_antialias: * @graph: A #UberLineGraph. * * XXX * * Returns: None. * Side effects: None. */ void uber_line_graph_set_antialias (UberLineGraph *graph, /* IN */ cairo_antialias_t antialias) /* IN */ { UberLineGraphPrivate *priv; g_return_if_fail(UBER_IS_LINE_GRAPH(graph)); priv = graph->priv; priv->antialias = antialias; uber_graph_redraw(UBER_GRAPH(graph)); }
/** * uber_line_graph_set_line_alpha: * @graph: A #UberLineGraph. * * XXX * * Returns: None. * Side effects: None. */ void uber_line_graph_set_line_alpha (UberLineGraph *graph, /* IN */ gint line, /* IN */ gdouble alpha) /* IN */ { UberLineGraphPrivate *priv; LineInfo *info; g_return_if_fail(UBER_IS_LINE_GRAPH(graph)); g_return_if_fail(line > 0); g_return_if_fail(line <= graph->priv->lines->len); priv = graph->priv; info = &g_array_index(priv->lines, LineInfo, line - 1); info->alpha = alpha; }
/** * uber_line_graph_add_line: * @graph: A #UberLineGraph. * @color: A #GdkColor for the line or %NULL. * * Adds a new line to the graph. If color is %NULL, the next value * in the default color list will be used. * * See uber_line_graph_remove_line(). * * Returns: The line identifier. * Side effects: None. */ gint uber_line_graph_add_line (UberLineGraph *graph, /* IN */ const GdkColor *color, /* IN */ UberLabel *label) /* IN */ { UberLineGraphPrivate *priv; LineInfo info = { 0 }; g_return_val_if_fail(UBER_IS_LINE_GRAPH(graph), 0); priv = graph->priv; info.alpha = 1.0; info.width = 1.0; /* * Retrieve the lines color. */ if (color) { info.color = *color; } else { gdk_color_parse("#729fcf", &info.color); } /* * Allocate buffers for data points. */ info.raw_data = g_ring_sized_new(sizeof(gdouble), priv->stride, NULL); uber_line_graph_init_ring(info.raw_data); /* * Store the newly crated line. */ g_array_append_val(priv->lines, info); /* * Mark the graph for full redraw. */ uber_graph_redraw(UBER_GRAPH(graph)); /* * Attach label. */ if (label) { uber_line_graph_bind_label(graph, priv->lines->len, label); uber_graph_add_label(UBER_GRAPH(graph), label); uber_label_set_color(label, &info.color); } /* * Line indexes start from 1. */ return priv->lines->len; }
/** * uber_line_graph_set_line_width: * @graph: A #UberLineGraph. * * XXX * * Returns: None. * Side effects: None. */ void uber_line_graph_set_line_width (UberLineGraph *graph, /* IN */ gint line, /* IN */ gdouble width) /* IN */ { UberLineGraphPrivate *priv; LineInfo *info; g_return_if_fail(UBER_IS_LINE_GRAPH(graph)); g_return_if_fail(line > 0); g_return_if_fail(line <= graph->priv->lines->len); priv = graph->priv; info = &g_array_index(graph->priv->lines, LineInfo, line - 1); info->width = width; uber_graph_redraw(UBER_GRAPH(graph)); }
/** * uber_line_graph_get_next_data: * @graph: A #UberGraph. * * XXX * * Returns: None. * Side effects: None. */ static gboolean uber_line_graph_get_next_data (UberGraph *graph) /* IN */ { UberLineGraphPrivate *priv; gboolean scale_changed = FALSE; gboolean ret = FALSE; LineInfo *line; gdouble val; gint i; g_return_val_if_fail(UBER_IS_LINE_GRAPH(graph), FALSE); priv = UBER_LINE_GRAPH(graph)->priv; /* * Retrieve the next data point. */ if (priv->func) { for (i = 0; i < priv->lines->len; i++) { val = 0.; line = &g_array_index(priv->lines, LineInfo, i); if (!(ret = priv->func(UBER_LINE_GRAPH(graph), i + 1, &val, priv->func_data))) { val = -INFINITY; } g_ring_append_val(line->raw_data, val); if (priv->autoscale) { if (val < priv->range.begin) { priv->range.begin = val - (val * SCALE_FACTOR); priv->range.range = priv->range.end - priv->range.begin; scale_changed = TRUE; } else if (val > priv->range.end) { priv->range.end = val + (val * SCALE_FACTOR); priv->range.range = priv->range.end - priv->range.begin; scale_changed = TRUE; } } } } if (scale_changed) { uber_graph_scale_changed(graph); } return ret; }
/** * uber_line_graph_downscale: * @graph: A #UberGraph. * * XXX * * Returns: None. * Side effects: None. */ static gboolean uber_line_graph_downscale (UberGraph *graph) /* IN */ { UberLineGraphPrivate *priv; gboolean ret = FALSE; gdouble val = 0; gdouble cur; LineInfo *line; gint i; gint j; g_return_val_if_fail(UBER_IS_LINE_GRAPH(graph), FALSE); priv = UBER_LINE_GRAPH(graph)->priv; /* * If we are set to autoscale, ignore request. */ if (!priv->autoscale) { return FALSE; } /* * Determine the largest value available. */ for (i = 0; i < priv->lines->len; i++) { line = &g_array_index(priv->lines, LineInfo, i); for (j = 0; j < line->raw_data->len; j++) { cur = g_ring_get_index(line->raw_data, gdouble, j); val = (cur > val) ? cur : val; } } /* * Downscale if we can. */ if (val != priv->range.begin) { if ((val * (1. + SCALE_FACTOR)) < priv->range.end) { priv->range.end = val * (1. + SCALE_FACTOR); priv->range.range = priv->range.end - priv->range.begin; ret = TRUE; } } return ret; }
/** * uber_line_graph_render: * @graph: A #UberGraph. * * Render the entire contents of the graph. * * Returns: None. * Side effects: None. */ static void uber_line_graph_render (UberGraph *graph, /* IN */ cairo_t *cr, /* IN */ GdkRectangle *rect) /* IN */ { UberLineGraphPrivate *priv; LineInfo *line; gint i; g_return_if_fail(UBER_IS_LINE_GRAPH(graph)); priv = UBER_LINE_GRAPH(graph)->priv; /* * Render each line to the graph. */ for (i = 0; i < priv->lines->len; i++) { line = &g_array_index(priv->lines, LineInfo, i); uber_line_graph_render_line(UBER_LINE_GRAPH(graph), cr, rect, line); } }
/** * uber_line_graph_color: * @graph: A #UberLineGraph. * * XXX * * Returns: None. * Side effects: None. */ static void uber_line_graph_color_changed (UberLabel *label, /* IN */ GdkColor *color, /* IN */ UberLineGraph *graph) /* IN */ { UberLineGraphPrivate *priv; LineInfo *info; gint i; g_return_if_fail(UBER_IS_LINE_GRAPH(graph)); g_return_if_fail(color != NULL); priv = graph->priv; for (i = 0; i < priv->lines->len; i++) { info = &g_array_index(priv->lines, LineInfo, i); if (info->label == label) { info->color = *color; } } uber_graph_redraw(UBER_GRAPH(graph)); }
/** * uber_line_graph_set_data_func: * @graph: A #UberLineGraph. * * XXX * * Returns: None. * Side effects: None. */ void uber_line_graph_set_data_func (UberLineGraph *graph, /* IN */ UberLineGraphFunc func, /* IN */ gpointer user_data, /* IN */ GDestroyNotify notify) /* IN */ { UberLineGraphPrivate *priv; g_return_if_fail(UBER_IS_LINE_GRAPH(graph)); priv = graph->priv; /* * Free existing data func if neccessary. */ if (priv->func_notify) { priv->func_notify(priv->func_data); } /* * Store data func. */ priv->func = func; priv->func_data = user_data; priv->func_notify = notify; }
void uber_line_graph_bind_label (UberLineGraph *graph, /* IN */ guint line, /* IN */ UberLabel *label) /* IN */ { UberLineGraphPrivate *priv; LineInfo *info; g_return_if_fail(UBER_IS_LINE_GRAPH(graph)); g_return_if_fail(UBER_IS_LABEL(label)); g_return_if_fail(line > 0); g_return_if_fail(line <= graph->priv->lines->len); priv = graph->priv; info = &g_array_index(priv->lines, LineInfo, line - 1); if (info->label_id) { g_signal_handler_disconnect(info->label, info->label_id); } info->label = label; info->label_id = g_signal_connect(label, "color-changed", G_CALLBACK(uber_line_graph_color_changed), graph); }
/** * uber_line_graph_get_range: * @graph: (in): A #UberLineGraph. * * XXX * * Returns: An #UberRange which should not be modified or freed. * Side effects: None. */ const UberRange* uber_line_graph_get_range (UberLineGraph *graph) /* IN */ { g_return_val_if_fail(UBER_IS_LINE_GRAPH(graph), NULL); return &graph->priv->range; }
/** * uber_line_graph_render_fast: * @graph: A #UberGraph. * * XXX * * Returns: None. * Side effects: None. */ static void uber_line_graph_render_fast (UberGraph *graph, /* IN */ cairo_t *cr, /* IN */ GdkRectangle *rect, /* IN */ guint epoch, /* IN */ gfloat each) /* IN */ { UberLineGraphPrivate *priv; LineInfo *line; gdouble last_y; gdouble y; gint i; g_return_if_fail(UBER_IS_LINE_GRAPH(graph)); g_return_if_fail(cr != NULL); g_return_if_fail(rect != NULL); priv = UBER_LINE_GRAPH(graph)->priv; /* * Prepare cairo line styling. */ cairo_set_line_width(cr, 1.0); cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND); cairo_set_antialias(cr, CAIRO_ANTIALIAS_DEFAULT); /* * Render most recent data point for each line. */ for (i = 0; i < priv->lines->len; i++) { line = &g_array_index(priv->lines, LineInfo, i); gdk_cairo_set_source_color(cr, &line->color); /* * Calculate positions. */ y = g_ring_get_index(line->scaled_data, gdouble, 0); last_y = g_ring_get_index(line->scaled_data, gdouble, 1); /* * Don't try to draw before we have real values. */ if ((isnan(y) || isinf(y)) || (isnan(last_y) || isinf(last_y))) { continue; } /* * Translate position from bottom right corner. */ y = RECT_BOTTOM(*rect) - y; last_y = RECT_BOTTOM(*rect) - last_y; /* * Convert relative position to fixed from bottom pixel. */ cairo_new_path(cr); cairo_move_to(cr, epoch, y); cairo_curve_to(cr, epoch - (each / 2.), y, epoch - (each / 2.), last_y, epoch - each, last_y); cairo_stroke(cr); } }
/** * uber_line_graph_render: * @graph: A #UberGraph. * @cr: A #cairo_t context. * @area: Full area to render contents within. * @line: The line to render. * * Render a particular line to the graph. * * Returns: None. * Side effects: None. */ static void uber_line_graph_render_line (UberLineGraph *graph, /* IN */ cairo_t *cr, /* IN */ GdkRectangle *area, /* IN */ LineInfo *line) /* IN */ { UberLineGraphPrivate *priv; guint x_epoch; guint x; guint y; guint last_x; guint last_y; gdouble val; gdouble each; gint i; g_return_if_fail(UBER_IS_LINE_GRAPH(graph)); priv = graph->priv; /* * Calculate number of pixels per data point. */ each = area->width / ((gfloat)priv->stride - 1.); /* * Determine the end of our drawing area for relative coordinates. */ x_epoch = area->x + area->width; /* * Prepare cairo settings. */ cairo_set_line_width(cr, 1.0); cairo_set_antialias(cr, priv->antialias); gdk_cairo_set_source_color(cr, &line->color); /* * Force a new path. */ cairo_new_path(cr); /* * Draw the line contents as bezier curves. */ for (i = 0; i < line->raw_data->len; i++) { /* * Retrieve data point. */ val = g_ring_get_index(line->raw_data, gdouble, i); /* * Once we get to -INFINITY, we must be at the end of the data * sequence. This may not always be true in the future. */ if (val == -INFINITY) { break; } /* * Calculate X/Y coordinate. */ y = area->y + area->height - val; x = x_epoch - (each * i); if (i == 0) { /* * Just move to the right position on first entry. */ cairo_move_to(cr, x, y); goto next; } else { /* * Draw curve to data point using the last X/Y positions as * control points. */ cairo_curve_to(cr, last_x - (each / 2.), last_y, last_x - (each / 2.), y, x, y); } next: last_y = y; last_x = x; } /* * Stroke the line content. */ cairo_stroke(cr); }
/** * uber_line_graph_render: * @graph: A #UberGraph. * @cr: A #cairo_t context. * @area: Full area to render contents within. * @line: The line to render. * * Render a particular line to the graph. * * Returns: None. * Side effects: None. */ static void uber_line_graph_render_line (UberLineGraph *graph, /* IN */ cairo_t *cr, /* IN */ GdkRectangle *area, /* IN */ LineInfo *line, /* IN */ guint epoch, /* IN */ gfloat each) /* IN */ { UberLineGraphPrivate *priv; UberRange pixel_range; GdkRectangle vis; guint x; guint last_x; gdouble y; gdouble last_y; gdouble val; gint i; g_return_if_fail(UBER_IS_LINE_GRAPH(graph)); priv = graph->priv; uber_graph_get_content_area(UBER_GRAPH(graph), &vis); pixel_range.begin = area->y + 1; pixel_range.end = area->y + area->height; pixel_range.range = pixel_range.end - pixel_range.begin; /* * Prepare cairo settings. */ uber_line_graph_stylize_line(graph, line, cr); /* * Force a new path. */ cairo_new_path(cr); /* * Draw the line contents as bezier curves. */ for (i = 0; i < line->raw_data->len; i++) { /* * Retrieve data point. */ val = g_ring_get_index(line->raw_data, gdouble, i); /* * Once we get to -INFINITY, we must be at the end of the data * sequence. This may not always be true in the future. */ if (val == -INFINITY) { break; } /* * Translate value to coordinate system. */ if (!priv->scale(&priv->range, &pixel_range, &val, priv->scale_data)) { break; } /* * Calculate X/Y coordinate. */ y = (gint)(RECT_BOTTOM(*area) - val) - .5; x = epoch - (each * i); if (i == 0) { /* * Just move to the right position on first entry. */ cairo_move_to(cr, x, y); goto next; } else { /* * Draw curve to data point using the last X/Y positions as * control points. */ cairo_curve_to(cr, last_x - (each / 2.), last_y, last_x - (each / 2.), y, x, y); } next: last_y = y; last_x = x; } /* * Stroke the line content. */ cairo_stroke(cr); }
/** * uber_line_graph_get_autoscale: * @graph: A #UberLineGraph. * * XXX * * Returns: None. * Side effects: None. */ gboolean uber_line_graph_get_autoscale (UberLineGraph *graph) /* IN */ { g_return_val_if_fail(UBER_IS_LINE_GRAPH(graph), FALSE); return graph->priv->autoscale; }
/** * uber_line_graph_render_fast: * @graph: A #UberGraph. * * XXX * * Returns: None. * Side effects: None. */ static void uber_line_graph_render_fast (UberGraph *graph, /* IN */ cairo_t *cr, /* IN */ GdkRectangle *rect, /* IN */ guint epoch, /* IN */ gfloat each) /* IN */ { UberLineGraphPrivate *priv; UberRange pixel_range; LineInfo *line; gdouble last_y; gdouble y; gint i; g_return_if_fail(UBER_IS_LINE_GRAPH(graph)); g_return_if_fail(cr != NULL); g_return_if_fail(rect != NULL); priv = UBER_LINE_GRAPH(graph)->priv; pixel_range.begin = rect->y + 1; pixel_range.end = rect->y + rect->height; pixel_range.range = pixel_range.end - pixel_range.begin; /* * Render most recent data point for each line. */ for (i = 0; i < priv->lines->len; i++) { line = &g_array_index(priv->lines, LineInfo, i); uber_line_graph_stylize_line(UBER_LINE_GRAPH(graph), line, cr); /* * Calculate positions. */ y = g_ring_get_index(line->raw_data, gdouble, 0); last_y = g_ring_get_index(line->raw_data, gdouble, 1); /* * Don't try to draw before we have real values. */ if ((isnan(y) || isinf(y)) || (isnan(last_y) || isinf(last_y))) { continue; } /* * Translate to coordinate scale. */ if (!priv->scale(&priv->range, &pixel_range, &y, priv->scale_data) || !priv->scale(&priv->range, &pixel_range, &last_y, priv->scale_data)) { continue; } /* * Translate position from bottom right corner. */ y = (gint)(RECT_BOTTOM(*rect) - y) - .5; last_y = (gint)(RECT_BOTTOM(*rect) - last_y) - .5; /* * Convert relative position to fixed from bottom pixel. */ cairo_new_path(cr); cairo_move_to(cr, epoch, y); cairo_curve_to(cr, epoch - (each / 2.), y, epoch - (each / 2.), last_y, epoch - each, last_y); cairo_stroke(cr); } }