From 189da15810deabd739d7c11c6e95fea55739fe60 Mon Sep 17 00:00:00 2001 From: Thomas White Date: Sat, 1 Aug 2020 15:13:49 +0200 Subject: Initial import from archive --- src/gtk-valuegraph.c | 234 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 234 insertions(+) create mode 100644 src/gtk-valuegraph.c (limited to 'src/gtk-valuegraph.c') 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 + * + * synth2d - two-dimensional Fourier synthesis + * + */ + +#include +#include + +#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; in; 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(), >k_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 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); + +} -- cgit v1.2.3