gtk2/gtk/gtkcurve.c
Benjamin Otte 3d506df234 gdk: Deprecate all drawing functions
These functions will be gone in Gtk 3.0 and be replaced by Cairo
functions.

Includes not disabling deprecated functions in old widgets that aren't
going to be ported.
2010-08-07 15:35:51 +02:00

1041 lines
26 KiB
C

/* GTK - The GIMP Toolkit
* Copyright (C) 1997 David Mosberger
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/*
* Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GTK+ Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
#undef GDK_DISABLE_DEPRECATED
#undef GTK_DISABLE_DEPRECATED
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "gtkcurve.h"
#include "gtkdrawingarea.h"
#include "gtkmain.h"
#include "gtkmarshalers.h"
#include "gtkradiobutton.h"
#include "gtktable.h"
#include "gtkprivate.h"
#include "gtkintl.h"
#include "gtkalias.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 {
PROP_0,
PROP_CURVE_TYPE,
PROP_MIN_X,
PROP_MAX_X,
PROP_MIN_Y,
PROP_MAX_Y
};
static GtkDrawingAreaClass *parent_class = NULL;
static guint curve_type_changed_signal = 0;
/* forward declarations: */
static void gtk_curve_class_init (GtkCurveClass *class);
static void gtk_curve_init (GtkCurve *curve);
static void gtk_curve_get_property (GObject *object,
guint param_id,
GValue *value,
GParamSpec *pspec);
static void gtk_curve_set_property (GObject *object,
guint param_id,
const GValue *value,
GParamSpec *pspec);
static void gtk_curve_finalize (GObject *object);
static gint gtk_curve_graph_events (GtkWidget *widget,
GdkEvent *event,
GtkCurve *c);
static void gtk_curve_size_graph (GtkCurve *curve);
GType
gtk_curve_get_type (void)
{
static GType curve_type = 0;
if (!curve_type)
{
const GTypeInfo curve_info =
{
sizeof (GtkCurveClass),
NULL, /* base_init */
NULL, /* base_finalize */
(GClassInitFunc) gtk_curve_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (GtkCurve),
0, /* n_preallocs */
(GInstanceInitFunc) gtk_curve_init,
};
curve_type = g_type_register_static (GTK_TYPE_DRAWING_AREA, I_("GtkCurve"),
&curve_info, 0);
}
return curve_type;
}
static void
gtk_curve_class_init (GtkCurveClass *class)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
parent_class = g_type_class_peek_parent (class);
gobject_class->finalize = gtk_curve_finalize;
gobject_class->set_property = gtk_curve_set_property;
gobject_class->get_property = gtk_curve_get_property;
g_object_class_install_property (gobject_class,
PROP_CURVE_TYPE,
g_param_spec_enum ("curve-type",
P_("Curve type"),
P_("Is this curve linear, spline interpolated, or free-form"),
GTK_TYPE_CURVE_TYPE,
GTK_CURVE_TYPE_SPLINE,
GTK_PARAM_READWRITE));
g_object_class_install_property (gobject_class,
PROP_MIN_X,
g_param_spec_float ("min-x",
P_("Minimum X"),
P_("Minimum possible value for X"),
-G_MAXFLOAT,
G_MAXFLOAT,
0.0,
GTK_PARAM_READWRITE));
g_object_class_install_property (gobject_class,
PROP_MAX_X,
g_param_spec_float ("max-x",
P_("Maximum X"),
P_("Maximum possible X value"),
-G_MAXFLOAT,
G_MAXFLOAT,
1.0,
GTK_PARAM_READWRITE));
g_object_class_install_property (gobject_class,
PROP_MIN_Y,
g_param_spec_float ("min-y",
P_("Minimum Y"),
P_("Minimum possible value for Y"),
-G_MAXFLOAT,
G_MAXFLOAT,
0.0,
GTK_PARAM_READWRITE));
g_object_class_install_property (gobject_class,
PROP_MAX_Y,
g_param_spec_float ("max-y",
P_("Maximum Y"),
P_("Maximum possible value for Y"),
-G_MAXFLOAT,
G_MAXFLOAT,
1.0,
GTK_PARAM_READWRITE));
curve_type_changed_signal =
g_signal_new (I_("curve-type-changed"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GtkCurveClass, curve_type_changed),
NULL, NULL,
_gtk_marshal_VOID__VOID,
G_TYPE_NONE, 0);
}
static void
gtk_curve_init (GtkCurve *curve)
{
gint old_mask;
curve->cursor_type = GDK_TOP_LEFT_ARROW;
curve->pixmap = NULL;
curve->curve_type = GTK_CURVE_TYPE_SPLINE;
curve->height = 0;
curve->grab_point = -1;
curve->num_points = 0;
curve->point = NULL;
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);
g_signal_connect (curve, "event",
G_CALLBACK (gtk_curve_graph_events), curve);
gtk_curve_size_graph (curve);
}
static void
gtk_curve_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkCurve *curve = GTK_CURVE (object);
switch (prop_id)
{
case PROP_CURVE_TYPE:
gtk_curve_set_curve_type (curve, g_value_get_enum (value));
break;
case PROP_MIN_X:
gtk_curve_set_range (curve, g_value_get_float (value), curve->max_x,
curve->min_y, curve->max_y);
break;
case PROP_MAX_X:
gtk_curve_set_range (curve, curve->min_x, g_value_get_float (value),
curve->min_y, curve->max_y);
break;
case PROP_MIN_Y:
gtk_curve_set_range (curve, curve->min_x, curve->max_x,
g_value_get_float (value), curve->max_y);
break;
case PROP_MAX_Y:
gtk_curve_set_range (curve, curve->min_x, curve->max_x,
curve->min_y, g_value_get_float (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_curve_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkCurve *curve = GTK_CURVE (object);
switch (prop_id)
{
case PROP_CURVE_TYPE:
g_value_set_enum (value, curve->curve_type);
break;
case PROP_MIN_X:
g_value_set_float (value, curve->min_x);
break;
case PROP_MAX_X:
g_value_set_float (value, curve->max_x);
break;
case PROP_MIN_Y:
g_value_set_float (value, curve->min_y);
break;
case PROP_MAX_Y:
g_value_set_float (value, curve->max_y);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static int
project (gfloat value, gfloat min, gfloat max, int norm)
{
return (norm - 1) * ((value - min) / (max - min)) + 0.5;
}
static gfloat
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
gtk_curve_interpolate (GtkCurve *c, gint width, gint height)
{
gfloat *vector;
int i;
vector = g_malloc (width * sizeof (vector[0]));
gtk_curve_get_vector (c, width, vector);
c->height = height;
if (c->num_points != width)
{
c->num_points = width;
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
gtk_curve_draw (GtkCurve *c, gint width, gint height)
{
GtkStateType state;
GtkStyle *style;
gint i;
if (!c->pixmap)
return;
if (c->height != height || c->num_points != width)
gtk_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);
if (c->curve_type != GTK_CURVE_TYPE_FREE)
for (i = 0; i < c->num_ctlpoints; ++i)
{
gint x, y;
if (c->ctlpoint[i][0] < c->min_x)
continue;
x = project (c->ctlpoint[i][0], c->min_x, c->max_x,
width);
y = height -
project (c->ctlpoint[i][1], c->min_y, c->max_y,
height);
/* draw a bullet: */
gdk_draw_arc (c->pixmap, style->fg_gc[state], TRUE, x, y,
RADIUS * 2, RADIUS*2, 0, 360*64);
}
gdk_draw_drawable (GTK_WIDGET (c)->window, style->fg_gc[state], c->pixmap,
0, 0, 0, 0, width + RADIUS * 2, height + RADIUS * 2);
}
static gint
gtk_curve_graph_events (GtkWidget *widget, GdkEvent *event, GtkCurve *c)
{
GdkCursorType new_type = c->cursor_type;
gint i, src, dst, leftbound, rightbound;
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;
gint retval = FALSE;
w = GTK_WIDGET (c);
width = w->allocation.width - RADIUS * 2;
height = w->allocation.height - RADIUS * 2;
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 = c->min_x;
distance = ~0U;
for (i = 0; i < c->num_ctlpoints; ++i)
{
cx = project (c->ctlpoint[i][0], min_x, c->max_x, width);
if ((guint) abs (x - cx) < distance)
{
distance = abs (x - cx);
closest_point = i;
}
}
switch (event->type)
{
case GDK_CONFIGURE:
if (c->pixmap)
g_object_unref (c->pixmap);
c->pixmap = NULL;
/* fall through */
case GDK_EXPOSE:
if (!c->pixmap)
c->pixmap = gdk_pixmap_new (w->window,
w->allocation.width,
w->allocation.height, -1);
gtk_curve_draw (c, width, height);
break;
case GDK_BUTTON_PRESS:
gtk_grab_add (widget);
new_type = GDK_TCROSS;
switch (c->curve_type)
{
case GTK_CURVE_TYPE_LINEAR:
case GTK_CURVE_TYPE_SPLINE:
if (distance > MIN_DISTANCE)
{
/* insert a new control point */
if (c->num_ctlpoints > 0)
{
cx = project (c->ctlpoint[closest_point][0], min_x,
c->max_x, width);
if (x > cx)
++closest_point;
}
++c->num_ctlpoints;
c->ctlpoint =
g_realloc (c->ctlpoint,
c->num_ctlpoints * sizeof (*c->ctlpoint));
for (i = c->num_ctlpoints - 1; i > closest_point; --i)
memcpy (c->ctlpoint + i, c->ctlpoint + i - 1,
sizeof (*c->ctlpoint));
}
c->grab_point = closest_point;
c->ctlpoint[c->grab_point][0] =
unproject (x, min_x, c->max_x, width);
c->ctlpoint[c->grab_point][1] =
unproject (height - y, c->min_y, c->max_y, height);
gtk_curve_interpolate (c, width, height);
break;
case GTK_CURVE_TYPE_FREE:
c->point[x].x = RADIUS + x;
c->point[x].y = RADIUS + y;
c->grab_point = x;
c->last = y;
break;
}
gtk_curve_draw (c, width, height);
retval = TRUE;
break;
case GDK_BUTTON_RELEASE:
gtk_grab_remove (widget);
/* delete inactive points: */
if (c->curve_type != GTK_CURVE_TYPE_FREE)
{
for (src = dst = 0; src < c->num_ctlpoints; ++src)
{
if (c->ctlpoint[src][0] >= min_x)
{
memcpy (c->ctlpoint + dst, c->ctlpoint + src,
sizeof (*c->ctlpoint));
++dst;
}
}
if (dst < src)
{
c->num_ctlpoints -= (src - dst);
if (c->num_ctlpoints <= 0)
{
c->num_ctlpoints = 1;
c->ctlpoint[0][0] = min_x;
c->ctlpoint[0][1] = c->min_y;
gtk_curve_interpolate (c, width, height);
gtk_curve_draw (c, width, height);
}
c->ctlpoint =
g_realloc (c->ctlpoint,
c->num_ctlpoints * sizeof (*c->ctlpoint));
}
}
new_type = GDK_FLEUR;
c->grab_point = -1;
retval = TRUE;
break;
case GDK_MOTION_NOTIFY:
mevent = (GdkEventMotion *) event;
switch (c->curve_type)
{
case GTK_CURVE_TYPE_LINEAR:
case GTK_CURVE_TYPE_SPLINE:
if (c->grab_point == -1)
{
/* if no point is grabbed... */
if (distance <= MIN_DISTANCE)
new_type = GDK_FLEUR;
else
new_type = GDK_TCROSS;
}
else
{
/* drag the grabbed point */
new_type = GDK_TCROSS;
leftbound = -MIN_DISTANCE;
if (c->grab_point > 0)
leftbound = project (c->ctlpoint[c->grab_point - 1][0],
min_x, c->max_x, width);
rightbound = width + RADIUS * 2 + MIN_DISTANCE;
if (c->grab_point + 1 < c->num_ctlpoints)
rightbound = project (c->ctlpoint[c->grab_point + 1][0],
min_x, c->max_x, width);
if (tx <= leftbound || tx >= rightbound
|| ty > height + RADIUS * 2 + MIN_DISTANCE
|| ty < -MIN_DISTANCE)
c->ctlpoint[c->grab_point][0] = min_x - 1.0;
else
{
rx = unproject (x, min_x, c->max_x, width);
ry = unproject (height - y, c->min_y, c->max_y, height);
c->ctlpoint[c->grab_point][0] = rx;
c->ctlpoint[c->grab_point][1] = ry;
}
gtk_curve_interpolate (c, width, height);
gtk_curve_draw (c, width, height);
}
break;
case GTK_CURVE_TYPE_FREE:
if (c->grab_point != -1)
{
if (c->grab_point > x)
{
x1 = x;
x2 = c->grab_point;
y1 = y;
y2 = c->last;
}
else
{
x1 = c->grab_point;
x2 = x;
y1 = c->last;
y2 = y;
}
if (x2 != x1)
for (i = x1; i <= x2; i++)
{
c->point[i].x = RADIUS + i;
c->point[i].y = RADIUS +
(y1 + ((y2 - y1) * (i - x1)) / (x2 - x1));
}
else
{
c->point[x].x = RADIUS + x;
c->point[x].y = RADIUS + y;
}
c->grab_point = x;
c->last = y;
gtk_curve_draw (c, width, height);
}
if (mevent->state & GDK_BUTTON1_MASK)
new_type = GDK_TCROSS;
else
new_type = GDK_PENCIL;
break;
}
if (new_type != (GdkCursorType) c->cursor_type)
{
GdkCursor *cursor;
c->cursor_type = new_type;
cursor = gdk_cursor_new_for_display (gtk_widget_get_display (w),
c->cursor_type);
gdk_window_set_cursor (w->window, cursor);
gdk_cursor_unref (cursor);
}
retval = TRUE;
break;
default:
break;
}
return retval;
}
void
gtk_curve_set_curve_type (GtkCurve *c, GtkCurveType new_type)
{
gfloat rx, dx;
gint x, i;
if (new_type != c->curve_type)
{
gint width, height;
width = GTK_WIDGET (c)->allocation.width - RADIUS * 2;
height = GTK_WIDGET (c)->allocation.height - RADIUS * 2;
if (new_type == GTK_CURVE_TYPE_FREE)
{
gtk_curve_interpolate (c, width, height);
c->curve_type = new_type;
}
else if (c->curve_type == GTK_CURVE_TYPE_FREE)
{
g_free (c->ctlpoint);
c->num_ctlpoints = 9;
c->ctlpoint = g_malloc (c->num_ctlpoints * sizeof (*c->ctlpoint));
rx = 0.0;
dx = (width - 1) / (gfloat) (c->num_ctlpoints - 1);
for (i = 0; i < c->num_ctlpoints; ++i, rx += dx)
{
x = (int) (rx + 0.5);
c->ctlpoint[i][0] =
unproject (x, c->min_x, c->max_x, width);
c->ctlpoint[i][1] =
unproject (RADIUS + height - c->point[x].y,
c->min_y, c->max_y, height);
}
c->curve_type = new_type;
gtk_curve_interpolate (c, width, height);
}
else
{
c->curve_type = new_type;
gtk_curve_interpolate (c, width, height);
}
g_signal_emit (c, curve_type_changed_signal, 0);
g_object_notify (G_OBJECT (c), "curve-type");
gtk_curve_draw (c, width, height);
}
}
static void
gtk_curve_size_graph (GtkCurve *curve)
{
gint width, height;
gfloat aspect;
GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (curve));
width = (curve->max_x - curve->min_x) + 1;
height = (curve->max_y - curve->min_y) + 1;
aspect = width / (gfloat) height;
if (width > gdk_screen_get_width (screen) / 4)
width = gdk_screen_get_width (screen) / 4;
if (height > gdk_screen_get_height (screen) / 4)
height = gdk_screen_get_height (screen) / 4;
if (aspect < 1.0)
width = height * aspect;
else
height = width / aspect;
gtk_widget_set_size_request (GTK_WIDGET (curve),
width + RADIUS * 2,
height + RADIUS * 2);
}
static void
gtk_curve_reset_vector (GtkCurve *curve)
{
g_free (curve->ctlpoint);
curve->num_ctlpoints = 2;
curve->ctlpoint = g_malloc (2 * sizeof (curve->ctlpoint[0]));
curve->ctlpoint[0][0] = curve->min_x;
curve->ctlpoint[0][1] = curve->min_y;
curve->ctlpoint[1][0] = curve->max_x;
curve->ctlpoint[1][1] = curve->max_y;
if (curve->pixmap)
{
gint width, height;
width = GTK_WIDGET (curve)->allocation.width - RADIUS * 2;
height = GTK_WIDGET (curve)->allocation.height - RADIUS * 2;
if (curve->curve_type == GTK_CURVE_TYPE_FREE)
{
curve->curve_type = GTK_CURVE_TYPE_LINEAR;
gtk_curve_interpolate (curve, width, height);
curve->curve_type = GTK_CURVE_TYPE_FREE;
}
else
gtk_curve_interpolate (curve, width, height);
gtk_curve_draw (curve, width, height);
}
}
void
gtk_curve_reset (GtkCurve *c)
{
GtkCurveType old_type;
old_type = c->curve_type;
c->curve_type = GTK_CURVE_TYPE_SPLINE;
gtk_curve_reset_vector (c);
if (old_type != GTK_CURVE_TYPE_SPLINE)
{
g_signal_emit (c, curve_type_changed_signal, 0);
g_object_notify (G_OBJECT (c), "curve-type");
}
}
void
gtk_curve_set_gamma (GtkCurve *c, gfloat gamma)
{
gfloat x, one_over_gamma, height;
GtkCurveType old_type;
gint i;
if (c->num_points < 2)
return;
old_type = c->curve_type;
c->curve_type = GTK_CURVE_TYPE_FREE;
if (gamma <= 0)
one_over_gamma = 1.0;
else
one_over_gamma = 1.0 / gamma;
height = c->height;
for (i = 0; i < c->num_points; ++i)
{
x = (gfloat) i / (c->num_points - 1);
c->point[i].x = RADIUS + i;
c->point[i].y =
RADIUS + (height * (1.0 - pow (x, one_over_gamma)) + 0.5);
}
if (old_type != GTK_CURVE_TYPE_FREE)
g_signal_emit (c, curve_type_changed_signal, 0);
gtk_curve_draw (c, c->num_points, c->height);
}
void
gtk_curve_set_range (GtkCurve *curve,
gfloat min_x,
gfloat max_x,
gfloat min_y,
gfloat max_y)
{
g_object_freeze_notify (G_OBJECT (curve));
if (curve->min_x != min_x) {
curve->min_x = min_x;
g_object_notify (G_OBJECT (curve), "min-x");
}
if (curve->max_x != max_x) {
curve->max_x = max_x;
g_object_notify (G_OBJECT (curve), "max-x");
}
if (curve->min_y != min_y) {
curve->min_y = min_y;
g_object_notify (G_OBJECT (curve), "min-y");
}
if (curve->max_y != max_y) {
curve->max_y = max_y;
g_object_notify (G_OBJECT (curve), "max-y");
}
g_object_thaw_notify (G_OBJECT (curve));
gtk_curve_size_graph (curve);
gtk_curve_reset_vector (curve);
}
void
gtk_curve_set_vector (GtkCurve *c, int veclen, gfloat vector[])
{
GtkCurveType old_type;
gfloat rx, dx, ry;
gint i, height;
GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (c));
old_type = c->curve_type;
c->curve_type = GTK_CURVE_TYPE_FREE;
if (c->point)
height = GTK_WIDGET (c)->allocation.height - RADIUS * 2;
else
{
height = (c->max_y - c->min_y);
if (height > gdk_screen_get_height (screen) / 4)
height = gdk_screen_get_height (screen) / 4;
c->height = height;
c->num_points = veclen;
c->point = g_malloc (c->num_points * sizeof (c->point[0]));
}
rx = 0;
dx = (veclen - 1.0) / (c->num_points - 1.0);
for (i = 0; i < c->num_points; ++i, rx += dx)
{
ry = vector[(int) (rx + 0.5)];
if (ry > c->max_y) ry = c->max_y;
if (ry < c->min_y) ry = c->min_y;
c->point[i].x = RADIUS + i;
c->point[i].y =
RADIUS + height - project (ry, c->min_y, c->max_y, height);
}
if (old_type != GTK_CURVE_TYPE_FREE)
{
g_signal_emit (c, curve_type_changed_signal, 0);
g_object_notify (G_OBJECT (c), "curve-type");
}
gtk_curve_draw (c, c->num_points, height);
}
void
gtk_curve_get_vector (GtkCurve *c, int veclen, gfloat vector[])
{
gfloat rx, ry, dx, dy, min_x, delta_x, *mem, *xv, *yv, *y2v, prev;
gint dst, i, x, next, num_active_ctlpoints = 0, first_active = -1;
min_x = c->min_x;
if (c->curve_type != GTK_CURVE_TYPE_FREE)
{
/* count active points: */
prev = min_x - 1.0;
for (i = num_active_ctlpoints = 0; i < c->num_ctlpoints; ++i)
if (c->ctlpoint[i][0] > prev)
{
if (first_active < 0)
first_active = i;
prev = c->ctlpoint[i][0];
++num_active_ctlpoints;
}
/* handle degenerate case: */
if (num_active_ctlpoints < 2)
{
if (num_active_ctlpoints > 0)
ry = c->ctlpoint[first_active][1];
else
ry = c->min_y;
if (ry < c->min_y) ry = c->min_y;
if (ry > c->max_y) ry = c->max_y;
for (x = 0; x < veclen; ++x)
vector[x] = ry;
return;
}
}
switch (c->curve_type)
{
case GTK_CURVE_TYPE_SPLINE:
mem = g_malloc (3 * num_active_ctlpoints * sizeof (gfloat));
xv = mem;
yv = mem + num_active_ctlpoints;
y2v = mem + 2*num_active_ctlpoints;
prev = min_x - 1.0;
for (i = dst = 0; i < c->num_ctlpoints; ++i)
if (c->ctlpoint[i][0] > prev)
{
prev = c->ctlpoint[i][0];
xv[dst] = c->ctlpoint[i][0];
yv[dst] = c->ctlpoint[i][1];
++dst;
}
spline_solve (num_active_ctlpoints, xv, yv, y2v);
rx = min_x;
dx = (c->max_x - min_x) / (veclen - 1);
for (x = 0; x < veclen; ++x, rx += dx)
{
ry = spline_eval (num_active_ctlpoints, xv, yv, y2v, rx);
if (ry < c->min_y) ry = c->min_y;
if (ry > c->max_y) ry = c->max_y;
vector[x] = ry;
}
g_free (mem);
break;
case GTK_CURVE_TYPE_LINEAR:
dx = (c->max_x - min_x) / (veclen - 1);
rx = min_x;
ry = c->min_y;
dy = 0.0;
i = first_active;
for (x = 0; x < veclen; ++x, rx += dx)
{
if (rx >= c->ctlpoint[i][0])
{
if (rx > c->ctlpoint[i][0])
ry = c->min_y;
dy = 0.0;
next = i + 1;
while (next < c->num_ctlpoints
&& c->ctlpoint[next][0] <= c->ctlpoint[i][0])
++next;
if (next < c->num_ctlpoints)
{
delta_x = c->ctlpoint[next][0] - c->ctlpoint[i][0];
dy = ((c->ctlpoint[next][1] - c->ctlpoint[i][1])
/ delta_x);
dy *= dx;
ry = c->ctlpoint[i][1];
i = next;
}
}
vector[x] = ry;
ry += dy;
}
break;
case GTK_CURVE_TYPE_FREE:
if (c->point)
{
rx = 0.0;
dx = c->num_points / (double) veclen;
for (x = 0; x < veclen; ++x, rx += dx)
vector[x] = unproject (RADIUS + c->height - c->point[(int) rx].y,
c->min_y, c->max_y,
c->height);
}
else
memset (vector, 0, veclen * sizeof (vector[0]));
break;
}
}
GtkWidget*
gtk_curve_new (void)
{
return g_object_new (GTK_TYPE_CURVE, NULL);
}
static void
gtk_curve_finalize (GObject *object)
{
GtkCurve *curve;
g_return_if_fail (GTK_IS_CURVE (object));
curve = GTK_CURVE (object);
if (curve->pixmap)
g_object_unref (curve->pixmap);
g_free (curve->point);
g_free (curve->ctlpoint);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
#define __GTK_CURVE_C__
#include "gtkaliasdef.c"