Mercurial > hg > xmms-sid
view src/xs_curve.c @ 429:a991d1fe7e94
Bugfixes
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Mon, 01 Jan 2007 08:09:00 +0000 |
parents | 0f255a575720 |
children | 54d86ee98b98 |
line wrap: on
line source
#include <stdlib.h> #include <string.h> #include <math.h> #include "xs_curve.h" #include <gtk/gtkdrawingarea.h> #include <gtk/gtkmain.h> #include <gtk/gtksignal.h> #define RADIUS 3 /* radius of the control points */ #define MIN_DISTANCE 8 /* min distance between control points */ #define GRAPH_MASK (GDK_EXPOSURE_MASK | \ GDK_POINTER_MOTION_MASK | \ GDK_POINTER_MOTION_HINT_MASK | \ GDK_ENTER_NOTIFY_MASK | \ GDK_BUTTON_PRESS_MASK | \ GDK_BUTTON_RELEASE_MASK | \ GDK_BUTTON1_MOTION_MASK) enum { ARG_0, ARG_CURVE_TYPE, ARG_MIN_X, ARG_MAX_X, ARG_MIN_Y, ARG_MAX_Y }; static GtkDrawingAreaClass *parent_class = NULL; /* forward declarations: */ static void xs_curve_class_init(XSCurveClass * class); static void xs_curve_init(XSCurve * curve); static void xs_curve_set_arg(GtkObject * object, GtkArg * arg, guint arg_id); static void xs_curve_get_arg(GtkObject * object, GtkArg * arg, guint arg_id); static void xs_curve_finalize(GtkObject * object); static gint xs_curve_graph_events(GtkWidget * widget, GdkEvent * event, XSCurve * c); static void xs_curve_size_graph(XSCurve * curve); GtkType xs_curve_get_type(void) { static GtkType curve_type = 0; if (!curve_type) { static const GtkTypeInfo curve_info = { "XSCurve", sizeof(XSCurve), sizeof(XSCurveClass), (GtkClassInitFunc) xs_curve_class_init, (GtkObjectInitFunc) xs_curve_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; curve_type = gtk_type_unique(GTK_TYPE_DRAWING_AREA, &curve_info); } return curve_type; } static void xs_curve_class_init(XSCurveClass * class) { GtkObjectClass *object_class; parent_class = gtk_type_class(GTK_TYPE_DRAWING_AREA); object_class = (GtkObjectClass *) class; object_class->set_arg = xs_curve_set_arg; object_class->get_arg = xs_curve_get_arg; object_class->finalize = xs_curve_finalize; gtk_object_add_arg_type("XSCurve::min_x", GTK_TYPE_FLOAT, GTK_ARG_READWRITE, ARG_MIN_X); gtk_object_add_arg_type("XSCurve::max_x", GTK_TYPE_FLOAT, GTK_ARG_READWRITE, ARG_MAX_X); gtk_object_add_arg_type("XSCurve::min_y", GTK_TYPE_FLOAT, GTK_ARG_READWRITE, ARG_MIN_Y); gtk_object_add_arg_type("XSCurve::max_y", GTK_TYPE_FLOAT, GTK_ARG_READWRITE, ARG_MAX_Y); } static void xs_curve_init(XSCurve * curve) { gint old_mask; curve->pixmap = NULL; curve->height = 0; curve->grab_point = -1; curve->num_points = 0; curve->point = 0; curve->num_ctlpoints = 0; curve->ctlpoint = NULL; curve->min_x = 0.0; curve->max_x = 1.0; curve->min_y = 0.0; curve->max_y = 1.0; old_mask = gtk_widget_get_events(GTK_WIDGET(curve)); gtk_widget_set_events(GTK_WIDGET(curve), old_mask | GRAPH_MASK); gtk_signal_connect(GTK_OBJECT(curve), "event", (GtkSignalFunc) xs_curve_graph_events, curve); xs_curve_size_graph(curve); } static void xs_curve_set_arg(GtkObject * object, GtkArg * arg, guint arg_id) { XSCurve *curve = XS_CURVE(object); switch (arg_id) { case ARG_MIN_X: xs_curve_set_range(curve, GTK_VALUE_FLOAT(*arg), curve->max_x, curve->min_y, curve->max_y); break; case ARG_MAX_X: xs_curve_set_range(curve, curve->min_x, GTK_VALUE_FLOAT(*arg), curve->min_y, curve->max_y); break; case ARG_MIN_Y: xs_curve_set_range(curve, curve->min_x, curve->max_x, GTK_VALUE_FLOAT(*arg), curve->max_y); break; case ARG_MAX_Y: xs_curve_set_range(curve, curve->min_x, curve->max_x, curve->min_y, GTK_VALUE_FLOAT(*arg)); break; } } static void xs_curve_get_arg(GtkObject * object, GtkArg * arg, guint arg_id) { XSCurve *curve = XS_CURVE(object); switch (arg_id) { case ARG_MIN_X: GTK_VALUE_FLOAT(*arg) = curve->min_x; break; case ARG_MAX_X: GTK_VALUE_FLOAT(*arg) = curve->max_x; break; case ARG_MIN_Y: GTK_VALUE_FLOAT(*arg) = curve->min_y; break; case ARG_MAX_Y: GTK_VALUE_FLOAT(*arg) = curve->max_y; break; default: arg->type = GTK_TYPE_INVALID; break; } } static int xs_project(gfloat value, gfloat min, gfloat max, int norm) { return (norm - 1) * ((value - min) / (max - min)) + 0.5; } static gfloat xs_unproject(gint value, gfloat min, gfloat max, int norm) { return value / (gfloat) (norm - 1) * (max - min) + min; } /* Solve the tridiagonal equation system that determines the second derivatives for the interpolation points. (Based on Numerical Recipies 2nd Edition.) */ static void spline_solve(int n, gfloat x[], gfloat y[], gfloat y2[]) { gfloat p, sig, *u; gint i, k; u = g_malloc((n - 1) * sizeof(u[0])); y2[0] = u[0] = 0.0; /* set lower boundary condition to "natural" */ for (i = 1; i < n - 1; ++i) { sig = (x[i] - x[i - 1]) / (x[i + 1] - x[i - 1]); p = sig * y2[i - 1] + 2.0; y2[i] = (sig - 1.0) / p; u[i] = ((y[i + 1] - y[i]) / (x[i + 1] - x[i]) - (y[i] - y[i - 1]) / (x[i] - x[i - 1])); u[i] = (6.0 * u[i] / (x[i + 1] - x[i - 1]) - sig * u[i - 1]) / p; } y2[n - 1] = 0.0; for (k = n - 2; k >= 0; --k) y2[k] = y2[k] * y2[k + 1] + u[k]; g_free(u); } static gfloat spline_eval(int n, gfloat x[], gfloat y[], gfloat y2[], gfloat val) { gint k_lo, k_hi, k; gfloat h, b, a; /* do a binary search for the right interval: */ k_lo = 0; k_hi = n - 1; while (k_hi - k_lo > 1) { k = (k_hi + k_lo) / 2; if (x[k] > val) k_hi = k; else k_lo = k; } h = x[k_hi] - x[k_lo]; g_assert(h > 0.0); a = (x[k_hi] - val) / h; b = (val - x[k_lo]) / h; return a * y[k_lo] + b * y[k_hi] + ((a * a * a - a) * y2[k_lo] + (b * b * b - b) * y2[k_hi]) * (h * h) / 6.0; } static void xs_curve_interpolate(XSCurve * c, gint width, gint height) { gfloat *vector; gint i; vector = g_malloc(width * sizeof(vector[0])); xs_curve_get_vector(c, width, vector); c->height = height; if (c->num_points != width) { c->num_points = width; if (c->point) g_free(c->point); c->point = g_malloc(c->num_points * sizeof(c->point[0])); } for (i = 0; i < width; ++i) { c->point[i].x = RADIUS + i; c->point[i].y = RADIUS + height - project(vector[i], c->min_y, c->max_y, height); } g_free(vector); } static void xs_curve_draw(XSCurve * c, gint width, gint height) { GtkStateType state; GtkStyle *style; gint i; if (!c->pixmap) return; if (c->height != height || c->num_points != width) xs_curve_interpolate(c, width, height); state = GTK_STATE_NORMAL; if (!GTK_WIDGET_IS_SENSITIVE(GTK_WIDGET(c))) state = GTK_STATE_INSENSITIVE; style = GTK_WIDGET(c)->style; /* clear the pixmap: */ gtk_paint_flat_box(style, c->pixmap, GTK_STATE_NORMAL, GTK_SHADOW_NONE, NULL, GTK_WIDGET(c), "curve_bg", 0, 0, width + RADIUS * 2, height + RADIUS * 2); /* draw the grid lines: (XXX make more meaningful) */ for (i = 0; i < 5; i++) { gdk_draw_line(c->pixmap, style->dark_gc[state], RADIUS, i * (height / 4.0) + RADIUS, width + RADIUS, i * (height / 4.0) + RADIUS); gdk_draw_line(c->pixmap, style->dark_gc[state], i * (width / 4.0) + RADIUS, RADIUS, i * (width / 4.0) + RADIUS, height + RADIUS); } gdk_draw_points(c->pixmap, style->fg_gc[state], c->point, c->num_points); gdk_draw_pixmap(GTK_WIDGET(c)->window, style->fg_gc[state], c->pixmap, 0, 0, 0, 0, width + RADIUS * 2, height + RADIUS * 2); } static gint xs_curve_graph_events(GtkWidget * widget, GdkEvent * event, XSCurve * c) { gint i, src, dst, leftbound, rightbound; GdkEventButton *bevent; GdkEventMotion *mevent; GtkWidget *w; gint tx, ty; gint cx, x, y, width, height; gint closest_point = 0; gfloat rx, ry, min_x; guint distance; gint x1, x2, y1, y2; w = GTK_WIDGET(c); width = w->allocation.width - RADIUS * 2; height = w->allocation.height - RADIUS * 2; if ((width < 0) || (height < 0)) return FALSE; return FALSE; } static void xs_curve_size_graph(XSCurve * curve) { gint width, height; gfloat aspect; width = (curve->max_x - curve->min_x) + 1; height = (curve->max_y - curve->min_y) + 1; aspect = width / (gfloat) height; if (width > gdk_screen_width() / 4) width = gdk_screen_width() / 4; if (height > gdk_screen_height() / 4) height = gdk_screen_height() / 4; if (aspect < 1.0) width = height * aspect; else height = width / aspect; gtk_drawing_area_size(GTK_DRAWING_AREA(curve), width + RADIUS * 2, height + RADIUS * 2); } static void xs_curve_reset_vector(XSCurve * curve) { } void xs_curve_reset(XSCurve * c) { } void xs_curve_set_range(XSCurve * curve, gfloat min_x, gfloat max_x, gfloat min_y, gfloat max_y) { curve->min_x = min_x; curve->max_x = max_x; curve->min_y = min_y; curve->max_y = max_y; xs_curve_size_graph(curve); xs_curve_reset_vector(curve); } GtkWidget *xs_curve_new(void) { return gtk_type_new(xs_curve_get_type()); } static void xs_curve_finalize(GtkObject * object) { XSCurve *curve; g_return_if_fail(object != NULL); g_return_if_fail(XS_IS_CURVE(object)); curve = XS_CURVE(object); if (curve->pixmap) gdk_pixmap_unref(curve->pixmap); if (curve->point) g_free(curve->point); if (curve->ctlpoint) g_free(curve->ctlpoint); (*GTK_OBJECT_CLASS(parent_class)->finalize) (object); } void xs_curve_get_vector(XSCurve *curve, int veclen, gfloat vector[]) { } void xs_curve_set_vector(XSCurve *curve, int veclen, gfloat vector[]) { }