aboutsummaryrefslogtreecommitdiff
path: root/src/gtk-valuegraph.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gtk-valuegraph.c')
-rw-r--r--src/gtk-valuegraph.c234
1 files changed, 234 insertions, 0 deletions
diff --git a/src/gtk-valuegraph.c b/src/gtk-valuegraph.c
new file mode 100644
index 0000000..90a3469
--- /dev/null
+++ b/src/gtk-valuegraph.c
@@ -0,0 +1,234 @@
+/*
+ * gtk-valuegraph.c
+ *
+ * A widget to display a graph of a sequence of values
+ *
+ * (c) 2006-2007 Thomas White <taw27@cam.ac.uk>
+ *
+ * synth2d - two-dimensional Fourier synthesis
+ *
+ */
+
+#include <gtk/gtk.h>
+#include <math.h>
+
+#include "gtk-valuegraph.h"
+
+static GtkObjectClass *parent_class = NULL;
+
+static void gtk_value_graph_destroy(GtkObject *gtk_value_graph) {
+ parent_class->destroy(gtk_value_graph);
+}
+
+GtkWidget *gtk_value_graph_new() {
+
+ GtkValueGraph *gtk_value_graph;
+
+ gtk_value_graph = GTK_VALUE_GRAPH(gtk_type_new(gtk_value_graph_get_type()));
+ gtk_value_graph->data = NULL;
+ gtk_value_graph->n = 0;
+
+ return GTK_WIDGET(gtk_value_graph);
+
+}
+
+static GObject *gtk_value_graph_constructor(GType type, guint n_construct_properties, GObjectConstructParam *construct_properties) {
+
+ GtkValueGraphClass *class;
+ GObjectClass *p_class;
+ GObject *obj;
+
+ class = GTK_VALUE_GRAPH_CLASS(g_type_class_peek(gtk_value_graph_get_type()));
+ p_class = G_OBJECT_CLASS(g_type_class_peek_parent(class));
+
+ obj = p_class->constructor(type, n_construct_properties, construct_properties);
+
+ return obj;
+
+}
+
+static void gtk_value_graph_class_init(GtkValueGraphClass *class) {
+
+ GtkObjectClass *object_class;
+ GObjectClass *g_object_class;
+
+ object_class = (GtkObjectClass *) class;
+ g_object_class = G_OBJECT_CLASS(class);
+
+ object_class->destroy = gtk_value_graph_destroy;
+ g_object_class->constructor = gtk_value_graph_constructor;
+
+ parent_class = gtk_type_class(gtk_drawing_area_get_type());
+
+}
+
+
+static gint gtk_value_graph_draw(GtkWidget *graph, GdkEventExpose *event, gpointer data) {
+
+ GtkValueGraph *vg;
+ unsigned int bw_left, bw_right, bw_top, bw_bottom;
+ PangoLayout *y0_layout;
+ PangoLayout *y1_layout;
+ PangoLayout *x0_layout;
+ PangoLayout *x1_layout;
+ PangoRectangle y0_extent, y1_extent, x0_extent, x1_extent;
+ unsigned int width, height;
+ char tmp[32];
+ unsigned int i;
+
+ vg = GTK_VALUE_GRAPH(graph);
+
+ /* Blank white background */
+ gdk_draw_rectangle(graph->window, graph->style->white_gc, TRUE, 0, 0, graph->allocation.width, graph->allocation.height);
+
+ /* Create PangoLayouts for labels */
+ y0_layout = gtk_widget_create_pango_layout(graph, "0");
+ pango_layout_get_pixel_extents(y0_layout, NULL, &y0_extent);
+
+ if ( fabs(log(vg->ymax)/log(10)) < 3 ) {
+ snprintf(tmp, 31, "%.4f", vg->ymax);
+ } else {
+ snprintf(tmp, 31, "%1.1e", vg->ymax);
+ }
+ y1_layout = gtk_widget_create_pango_layout(graph, tmp);
+ pango_layout_get_pixel_extents(y1_layout, NULL, &y1_extent);
+
+ x0_layout = gtk_widget_create_pango_layout(graph, "0");
+ pango_layout_get_pixel_extents(x0_layout, NULL, &x0_extent);
+
+ if ( vg->xmax < 1000 ) {
+ snprintf(tmp, 31, "%i", vg->xmax);
+ } else {
+ snprintf(tmp, 31, "%1.1e", (double)vg->xmax);
+ }
+ x1_layout = gtk_widget_create_pango_layout(graph, tmp);
+ pango_layout_get_pixel_extents(x1_layout, NULL, &x1_extent);
+
+ /* Determine border widths */
+ bw_left = 1+((y1_extent.width > y0_extent.width) ? y1_extent.width : y0_extent.width);
+ bw_right = 1+x1_extent.width/2;
+ bw_top = 1+y1_extent.height/2;
+ bw_bottom = 1+((x1_extent.height > x0_extent.height) ? x1_extent.height : x0_extent.height);
+ width = graph->allocation.width;
+ height = graph->allocation.height;
+
+ /* Draw axis lines */
+ gdk_draw_line(graph->window, graph->style->black_gc, bw_left, height-1-bw_bottom, bw_left, bw_top);
+ gdk_draw_line(graph->window, graph->style->black_gc, bw_left, height-1-bw_bottom, width-1-bw_right, height-1-bw_bottom);
+
+ /* Label axes */
+ gdk_draw_layout(graph->window, graph->style->black_gc, 1+bw_left-x0_extent.width/2, height-1-bw_bottom, x0_layout);
+ gdk_draw_layout(graph->window, graph->style->black_gc, width-bw_right-x1_extent.width/2, height-1-bw_bottom, x1_layout);
+ gdk_draw_layout(graph->window, graph->style->black_gc, bw_left-y0_extent.width-1, height-1-bw_bottom-y0_extent.height/2, y0_layout);
+ gdk_draw_layout(graph->window, graph->style->black_gc, bw_left-y1_extent.width-1, 1, y1_layout);
+
+ /* Plot data */
+ for ( i=0; i<vg->n; i++ ) {
+
+ unsigned int x, y;
+ double xd, yd;
+
+ xd = (((double)width-bw_left-bw_right)/(double)vg->xmax)*(double)(i+1); /* Graph axes go from 1 */
+ x = bw_left + xd;
+ yd = (((double)height-bw_top-bw_bottom)/(double)vg->ymax)*(double)vg->data[i];
+ y = height-bw_bottom - yd;
+
+ gdk_draw_point(graph->window, graph->style->black_gc, x, y);
+
+ }
+
+ return 0;
+
+}
+
+static void gtk_value_graph_init(GtkValueGraph *gtk_value_graph) {
+ gtk_widget_set_size_request(GTK_WIDGET(gtk_value_graph), 100, 200);
+ g_signal_connect(G_OBJECT(gtk_value_graph), "expose_event", G_CALLBACK(gtk_value_graph_draw), NULL);
+}
+
+guint gtk_value_graph_get_type(void) {
+
+ static guint gtk_value_graph_type = 0;
+
+ if ( !gtk_value_graph_type ) {
+
+ GtkTypeInfo gtk_value_graph_info = {
+ "GtkValueGraph",
+ sizeof(GtkValueGraph),
+ sizeof(GtkValueGraphClass),
+ (GtkClassInitFunc) gtk_value_graph_class_init,
+ (GtkObjectInitFunc) gtk_value_graph_init,
+ NULL,
+ NULL,
+ (GtkClassInitFunc) NULL,
+ };
+ gtk_value_graph_type = gtk_type_unique(gtk_drawing_area_get_type(), &gtk_value_graph_info);
+
+ }
+
+ return gtk_value_graph_type;
+
+}
+
+static double gtk_value_graph_peak(double *data, unsigned int n) {
+
+ unsigned int i;
+ double max;
+
+ if ( n == 0 ) return 1;
+
+ max = 0;
+ for ( i=0; i<n; i++ ) {
+ if ( data[i] > max ) max = data[i];
+ }
+
+ return max;
+
+}
+
+/* Calculate the best range for a axis with maximum value n */
+static double gtk_value_graph_axis_max(double n) {
+
+ double mantissa, exponent, test;
+return n;
+ if ( n == 0 ) return 1;
+
+ /* Convert to standard form */
+ exponent = rint(log(n)/log(10));
+ mantissa = n / pow(10, exponent);
+
+ /* Check if the value can be exactly represented */
+ test = mantissa * 10;
+ test = rint(test);
+ test /= 10;
+ if ( fabs(test - mantissa) > 0.001 ) {
+
+ /* Round the mantissa upwards */
+ mantissa += 0.1;
+ mantissa *= 10;
+ mantissa = rint(mantissa);
+ mantissa /= 10;
+
+ } /* Else don't touch it */
+
+ return mantissa*pow(10, exponent);
+
+}
+
+void gtk_value_graph_set_data(GtkValueGraph *vg, double *data, unsigned int n) {
+
+ double dmax;
+
+ /* Recalculate axes */
+ dmax = gtk_value_graph_peak(data, n);
+ vg->data = data;
+ vg->n = n;
+ vg->xmax = gtk_value_graph_axis_max(n);
+ vg->ymax = gtk_value_graph_axis_max(dmax);
+
+ //printf("n=%i, dmax=%f => xmax=%i, ymax=%f\n", n, dmax, vg->xmax, vg->ymax);
+
+ /* Schedule redraw */
+ gtk_widget_queue_draw_area(GTK_WIDGET(vg), 0, 0, GTK_WIDGET(vg)->allocation.width, GTK_WIDGET(vg)->allocation.height);
+
+}