Mercurial > hg > xmms-sid
changeset 502:54d86ee98b98
Alpha/preliminary curve widget code merged.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Sat, 27 Jan 2007 02:52:02 +0000 |
parents | d753e545f9fa |
children | 070516a21ef4 |
files | src/xs_curve.c src/xs_curve.h |
diffstat | 2 files changed, 348 insertions(+), 164 deletions(-) [+] |
line wrap: on
line diff
--- a/src/xs_curve.c Sat Jan 27 02:51:02 2007 +0000 +++ b/src/xs_curve.c Sat Jan 27 02:52:02 2007 +0000 @@ -2,25 +2,32 @@ #include <string.h> #include <math.h> +#include <stdio.h> + #include "xs_curve.h" #include <gtk/gtkdrawingarea.h> #include <gtk/gtkmain.h> #include <gtk/gtksignal.h> +#define GET_X(i) curve->ctlpoints[i].x +#define GET_Y(i) curve->ctlpoints[i].y + + #define RADIUS 3 /* radius of the control points */ +#define RADIUS2 (RADIUS * 2) + #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) + 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 -{ +enum { ARG_0, ARG_CURVE_TYPE, ARG_MIN_X, @@ -31,8 +38,6 @@ 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); @@ -41,6 +46,7 @@ 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; @@ -62,7 +68,8 @@ return curve_type; } -static void xs_curve_class_init(XSCurveClass * class) + +static void xs_curve_class_init(XSCurveClass *class) { GtkObjectClass *object_class; @@ -80,7 +87,8 @@ gtk_object_add_arg_type("XSCurve::max_y", GTK_TYPE_FLOAT, GTK_ARG_READWRITE, ARG_MAX_Y); } -static void xs_curve_init(XSCurve * curve) + +static void xs_curve_init(XSCurve *curve) { gint old_mask; @@ -88,16 +96,13 @@ curve->height = 0; curve->grab_point = -1; - curve->num_points = 0; - curve->point = 0; - curve->num_ctlpoints = 0; - curve->ctlpoint = NULL; + curve->ctlpoints = NULL; curve->min_x = 0.0; - curve->max_x = 1.0; + curve->max_x = 2048.0; curve->min_y = 0.0; - curve->max_y = 1.0; + curve->max_y = 22000.0; old_mask = gtk_widget_get_events(GTK_WIDGET(curve)); gtk_widget_set_events(GTK_WIDGET(curve), old_mask | GRAPH_MASK); @@ -105,7 +110,8 @@ xs_curve_size_graph(curve); } -static void xs_curve_set_arg(GtkObject * object, GtkArg * arg, guint arg_id) + +static void xs_curve_set_arg(GtkObject *object, GtkArg *arg, guint arg_id) { XSCurve *curve = XS_CURVE(object); @@ -125,7 +131,8 @@ } } -static void xs_curve_get_arg(GtkObject * object, GtkArg * arg, guint arg_id) + +static void xs_curve_get_arg(GtkObject *object, GtkArg *arg, guint arg_id) { XSCurve *curve = XS_CURVE(object); @@ -148,158 +155,327 @@ } } + 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; - } +static inline void xs_cubic_coeff(gfloat x1, gfloat y1, + gfloat x2, gfloat y2, + gfloat k1, gfloat k2, + gfloat *a, gfloat *b, + gfloat *c, gfloat *d) +{ + gfloat dx = x2 - x1, dy = y2 - y1; - y2[n - 1] = 0.0; - for (k = n - 2; k >= 0; --k) - y2[k] = y2[k] * y2[k + 1] + u[k]; - - g_free(u); + *a = ((k1 + k2) - 2 * dy / dx) / (dx * dx); + *b = ((k2 - k1) / dx - 3 * (x1 + x2) * (*a)) / 2; + *c = k1 - (3 * x1 * (*a) + 2 * (*b)) * x1; + *d = y1 - ((x1 * (*a) + (*b)) * x1 + (*c)) * x1; } -static gfloat spline_eval(int n, gfloat x[], gfloat y[], gfloat y2[], gfloat val) -{ - gint k_lo, k_hi, k; - gfloat h, b, a; +#define x(val) val->x +#define y(val) val->y - /* 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) +static void xs_curve_draw(XSCurve *curve, 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) -{ + gfloat res = 10.0f; GtkStateType state; GtkStyle *style; gint i; + t_xs_point *p0, *p1, *p2, *p3; - if (!c->pixmap) + if (!curve->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))) + if (!GTK_WIDGET_IS_SENSITIVE(GTK_WIDGET(curve))) state = GTK_STATE_INSENSITIVE; - style = GTK_WIDGET(c)->style; + style = GTK_WIDGET(curve)->style; - /* clear the pixmap: */ - gtk_paint_flat_box(style, c->pixmap, + /* Clear the pixmap */ + gtk_paint_flat_box(style, curve->pixmap, GTK_STATE_NORMAL, GTK_SHADOW_NONE, - NULL, GTK_WIDGET(c), "curve_bg", - 0, 0, width + RADIUS * 2, height + RADIUS * 2); + NULL, GTK_WIDGET(curve), "curve_bg", + 0, 0, + width + RADIUS2, + height + RADIUS2); - /* draw the grid lines: (XXX make more meaningful) */ + + /* Draw the grid */ for (i = 0; i < 5; i++) { - gdk_draw_line(c->pixmap, style->dark_gc[state], + gdk_draw_line(curve->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], + gdk_draw_line(curve->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); + + /* Draw the spline/curve itself */ + p0 = curve->ctlpoints; + p1 = p0; p1++; + p2 = p1; p2++; + p3 = p2; p3++; + + /* Draw each curve segment */ + fprintf(stderr, "npoints = %d\n", curve->num_ctlpoints); + for (i = 0; i < curve->num_ctlpoints; i++, ++p0, ++p1, ++p2, ++p3) { + gfloat k1, k2, a, b, c, d, x; + + /* p1 and p2 equal; single point */ + if (x(p1) == x(p2)) + continue; + + /* Both end points repeated; straight line */ + if (x(p0) == x(p1) && x(p2) == x(p3)) { + k1 = k2 = (y(p2) - y(p1)) / (x(p2) - x(p1)); + } + /* p0 and p1 equal; use f''(x1) = 0 */ + else if (x(p0) == x(p1)) { + k2 = (y(p3) - y(p1)) / (x(p3) - x(p1)); + k1 = (3 * (y(p2) - y(p1)) / (x(p2) - x(p1)) - k2) / 2; + } + /* p2 and p3 equal; use f''(x2) = 0 */ + else if (x(p2) == x(p3)) { + k1 = (y(p2) - y(p0)) / (x(p2) - x(p0)); + k2 = (3 * (y(p2) - y(p1)) / (x(p2) - x(p1)) - k1) / 2; + } + /* Normal curve */ + else { + k1 = (y(p2) - y(p0)) / (x(p2) - x(p0)); + k2 = (y(p3) - y(p1)) / (x(p3) - x(p1)); + } - gdk_draw_pixmap(GTK_WIDGET(c)->window, style->fg_gc[state], c->pixmap, - 0, 0, 0, 0, width + RADIUS * 2, height + RADIUS * 2); + xs_cubic_coeff(x(p1), y(p1), x(p2), y(p2), k1, k2, &a, &b, &c, &d); + + fprintf(stderr, "--seg[%1.3f, %1.3f]=>[%1.3f, %1.3f]--\n", + x(p1), y(p1), x(p2), y(p2)); + + for (x = x(p1); x <= x(p2); x += res) { + gfloat y = ((a * x + b) * x + c) * x + d; + gint qx, qy; + qx = RADIUS + xs_project(x, curve->min_x, curve->max_x, width); + qy = RADIUS + xs_project(y, curve->min_y, curve->max_y, height); + + fprintf(stderr, "[%1.3f, %1.3f] -> %d, %d\n", x, y, qx, qy); + + gdk_draw_point(curve->pixmap, style->fg_gc[state], + RADIUS + xs_project(x, curve->min_x, curve->max_x, width), + RADIUS + xs_project(y, curve->min_y, curve->max_y, height) + ); + + } + + fprintf(stderr, "-------\n"); + } + + /* Draw control points */ + for (i = 0; i < curve->num_ctlpoints; ++i) { + gint x, y; + + if (GET_X(i) < curve->min_x || GET_Y(i) < curve->min_y || + GET_X(i) >= curve->max_x || GET_Y(i) >= curve->max_y) + continue; + + x = xs_project(GET_X(i), curve->min_x, curve->max_x, width); + y = xs_project(GET_Y(i), curve->min_y, curve->max_y, height); + + gdk_draw_arc(curve->pixmap, style->fg_gc[state], TRUE, + x, y, RADIUS2, RADIUS2, 0, 360 * 64); + } + + /* Draw pixmap in the widget */ + gdk_draw_pixmap(GTK_WIDGET(curve)->window, + style->fg_gc[state], curve->pixmap, + 0, 0, 0, 0, + width + RADIUS2, + height + RADIUS2); } -static gint xs_curve_graph_events(GtkWidget * widget, GdkEvent * event, XSCurve * c) +#undef x +#undef y + + +static gint xs_curve_graph_events(GtkWidget *widget, GdkEvent *event, XSCurve *curve) { - gint i, src, dst, leftbound, rightbound; + GdkCursorType new_type = curve->cursor_type; GdkEventButton *bevent; GdkEventMotion *mevent; GtkWidget *w; - gint tx, ty; - gint cx, x, y, width, height; - gint closest_point = 0; - gfloat rx, ry, min_x; + gint i, width, height, x, y, tx, ty, cx, closest_point = 0, 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; + w = GTK_WIDGET(curve); + width = w->allocation.width - RADIUS2; + height = w->allocation.height - RADIUS2; if ((width < 0) || (height < 0)) return FALSE; + + /* get the pointer position */ + gdk_window_get_pointer(w->window, &tx, &ty, NULL); + x = CLAMP((tx - RADIUS), 0, width - 1); + y = CLAMP((ty - RADIUS), 0, height - 1); + + min_x = curve->min_x; + + distance = ~0U; + for (i = 0; i < curve->num_ctlpoints; ++i) { + cx = xs_project(GET_X(i), min_x, curve->max_x, width); + if ((guint) abs(x - cx) < distance) { + distance = abs(x - cx); + closest_point = i; + } + } + + /* Act based on event type */ + switch (event->type) { + case GDK_CONFIGURE: + if (curve->pixmap) + gdk_pixmap_unref(curve->pixmap); + curve->pixmap = 0; + + /* fall through */ + + case GDK_EXPOSE: + if (!curve->pixmap) { + curve->pixmap = gdk_pixmap_new(w->window, + w->allocation.width, w->allocation.height, -1); + } + xs_curve_draw(curve, width, height); + break; + + case GDK_BUTTON_PRESS: + gtk_grab_add(widget); + + bevent = (GdkEventButton *) event; + new_type = GDK_TCROSS; + + if (distance > MIN_DISTANCE) { + /* insert a new control point */ + if (curve->num_ctlpoints > 0) { + cx = xs_project(GET_X(closest_point), min_x, curve->max_x, width); + if (x > cx) closest_point++; + } + + curve->num_ctlpoints++; + + curve->ctlpoints = g_realloc(curve->ctlpoints, curve->num_ctlpoints * sizeof(*curve->ctlpoints)); + for (i = curve->num_ctlpoints - 1; i > closest_point; --i) { + memcpy(curve->ctlpoints + i, curve->ctlpoints + i - 1, sizeof(*curve->ctlpoints)); + } + } + + curve->grab_point = closest_point; + GET_X(curve->grab_point) = xs_unproject(x, min_x, curve->max_x, width); + GET_Y(curve->grab_point) = xs_unproject(y, curve->min_y, curve->max_y, height); + + xs_curve_draw(curve, width, height); + break; + + case GDK_BUTTON_RELEASE: + { + gint src, dst; + + gtk_grab_remove(widget); + + /* delete inactive points: */ + for (src = dst = 0; src < curve->num_ctlpoints; ++src) + if (GET_X(src) >= min_x) { + memcpy(curve->ctlpoints + dst, curve->ctlpoints + src, sizeof(*curve->ctlpoints)); + dst++; + } + + if (dst < src) { + curve->num_ctlpoints -= (src - dst); + if (curve->num_ctlpoints <= 0) { + curve->num_ctlpoints = 1; + GET_X(0) = min_x; + GET_Y(0) = curve->min_y; + xs_curve_draw(curve, width, height); + } + curve->ctlpoints = g_realloc(curve->ctlpoints, curve->num_ctlpoints * sizeof(*curve->ctlpoints)); + } + + new_type = GDK_FLEUR; + curve->grab_point = -1; + } + break; + + case GDK_MOTION_NOTIFY: + mevent = (GdkEventMotion *) event; + + if (curve->grab_point == -1) { + /* if no point is grabbed... */ + if (distance <= MIN_DISTANCE) + new_type = GDK_FLEUR; + else + new_type = GDK_TCROSS; + } else { + gint leftbound, rightbound; + + /* drag the grabbed point */ + new_type = GDK_TCROSS; + + leftbound = -MIN_DISTANCE; + if (curve->grab_point > 0) { + leftbound = xs_project( + GET_X(curve->grab_point-1), + min_x, curve->max_x, width); + } + + rightbound = width + RADIUS2 + MIN_DISTANCE; + if (curve->grab_point + 1 < curve->num_ctlpoints) { + rightbound = xs_project( + GET_X(curve->grab_point+1), + min_x, curve->max_x, width); + } + + if ((tx <= leftbound) || (tx >= rightbound) || + (ty > height + RADIUS2 + MIN_DISTANCE) || (ty < -MIN_DISTANCE)) { + GET_X(curve->grab_point) = min_x - 1.0; + } else { + GET_X(curve->grab_point) = + xs_unproject(x, min_x, curve->max_x, width); + GET_Y(curve->grab_point) = + xs_unproject(y, curve->min_y, curve->max_y, height); + } + + xs_curve_draw(curve, width, height); + } + + if (new_type != (GdkCursorType) curve->cursor_type) { + GdkCursor *cursor; + + curve->cursor_type = new_type; + + cursor = gdk_cursor_new(curve->cursor_type); + gdk_window_set_cursor(w->window, cursor); + gdk_cursor_destroy(cursor); + } + break; + + default: + break; + } return FALSE; } -static void xs_curve_size_graph(XSCurve * curve) + +static void xs_curve_size_graph(XSCurve *curve) { gint width, height; gfloat aspect; @@ -307,8 +483,10 @@ 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; @@ -317,19 +495,41 @@ 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) -{ + gtk_drawing_area_size(GTK_DRAWING_AREA(curve), width + RADIUS2, height + RADIUS2); } -void xs_curve_set_range(XSCurve * curve, gfloat min_x, gfloat max_x, gfloat min_y, gfloat max_y) +void xs_curve_reset(XSCurve *curve) +{ + if (curve->ctlpoints) + g_free(curve->ctlpoints); + + curve->num_ctlpoints = 4; + curve->ctlpoints = g_malloc(curve->num_ctlpoints * sizeof(curve->ctlpoints[0])); + + GET_X(0) = curve->min_x; + GET_Y(0) = curve->min_y; + + GET_X(1) = curve->min_x; + GET_Y(1) = curve->min_y; + + GET_X(2) = curve->max_x; + GET_Y(2) = curve->max_y; + + GET_X(3) = curve->max_x; + GET_Y(3) = curve->max_y; + + if (curve->pixmap) { + gint width, height; + + width = GTK_WIDGET(curve)->allocation.width - RADIUS2; + height = GTK_WIDGET(curve)->allocation.height - RADIUS2; + xs_curve_draw(curve, width, height); + } +} + + +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; @@ -337,7 +537,7 @@ curve->max_y = max_y; xs_curve_size_graph(curve); - xs_curve_reset_vector(curve); + xs_curve_reset(curve); } @@ -347,7 +547,7 @@ } -static void xs_curve_finalize(GtkObject * object) +static void xs_curve_finalize(GtkObject *object) { XSCurve *curve; @@ -355,22 +555,13 @@ 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); + + if (curve->ctlpoints) + g_free(curve->ctlpoints); (*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[]) -{ -} -
--- a/src/xs_curve.h Sat Jan 27 02:51:02 2007 +0000 +++ b/src/xs_curve.h Sat Jan 27 02:52:02 2007 +0000 @@ -19,31 +19,30 @@ typedef struct _XSCurve XSCurve; typedef struct _XSCurveClass XSCurveClass; +typedef struct { + gfloat x,y; +} t_xs_point; -struct _XSCurve -{ + +struct _XSCurve { GtkDrawingArea graph; + gint cursor_type; gfloat min_x; gfloat max_x; gfloat min_y; gfloat max_y; GdkPixmap *pixmap; - gint height; /* (cached) graph height in pixels */ - gint grab_point; /* point currently grabbed */ + gint height; /* (cached) graph height in pixels */ + gint grab_point; /* point currently grabbed */ gint last; - /* (cached) curve points: */ - gint num_points; - GdkPoint *point; - - /* control points: */ - gint num_ctlpoints; /* number of control points */ - gfloat (*ctlpoint)[2]; /* array of control points */ + /* control points */ + gint num_ctlpoints; /* number of control points */ + t_xs_point *ctlpoints; /* array of control points */ }; -struct _XSCurveClass -{ +struct _XSCurveClass { GtkDrawingAreaClass parent_class; }; @@ -51,15 +50,9 @@ GtkType xs_curve_get_type (void); GtkWidget* xs_curve_new (void); void xs_curve_reset (XSCurve *curve); -void xs_curve_set_gamma (XSCurve *curve, gfloat gamma); void xs_curve_set_range (XSCurve *curve, gfloat min_x, gfloat max_x, gfloat min_y, gfloat max_y); -void xs_curve_get_vector (XSCurve *curve, - int veclen, gfloat vector[]); -void xs_curve_set_vector (XSCurve *curve, - int veclen, gfloat vector[]); - #ifdef __cplusplus }