view src/xs_curve.c @ 378:0f255a575720

Fix ending newline.
author Matti Hamalainen <ccr@tnsp.org>
date Wed, 09 Nov 2005 19:13:00 +0000
parents 58079c6180a0
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[])
{
}