📄 gtkcurve.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 Library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library 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-1999. 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/. */#include <stdlib.h>#include <string.h>#include <math.h>#include "gtkcurve.h"#include "gtkdrawingarea.h"#include "gtkmain.h"#include "gtkradiobutton.h"#include "gtksignal.h"#include "gtktable.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;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_set_arg (GtkObject *object, GtkArg *arg, guint arg_id);static void gtk_curve_get_arg (GtkObject *object, GtkArg *arg, guint arg_id);static void gtk_curve_finalize (GtkObject *object);static gint gtk_curve_graph_events (GtkWidget *widget, GdkEvent *event, GtkCurve *c);static void gtk_curve_size_graph (GtkCurve *curve);GtkTypegtk_curve_get_type (void){ static GtkType curve_type = 0; if (!curve_type) { static const GtkTypeInfo curve_info = { "GtkCurve", sizeof (GtkCurve), sizeof (GtkCurveClass), (GtkClassInitFunc) gtk_curve_class_init, (GtkObjectInitFunc) gtk_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 voidgtk_curve_class_init (GtkCurveClass *class){ GtkObjectClass *object_class; parent_class = gtk_type_class (GTK_TYPE_DRAWING_AREA); object_class = (GtkObjectClass *) class; object_class->set_arg = gtk_curve_set_arg; object_class->get_arg = gtk_curve_get_arg; object_class->finalize = gtk_curve_finalize; curve_type_changed_signal = gtk_signal_new ("curve_type_changed", GTK_RUN_FIRST, object_class->type, GTK_SIGNAL_OFFSET (GtkCurveClass, curve_type_changed), gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0); gtk_object_class_add_signals (object_class, &curve_type_changed_signal, 1); gtk_object_add_arg_type ("GtkCurve::curve_type", GTK_TYPE_CURVE_TYPE, GTK_ARG_READWRITE, ARG_CURVE_TYPE); gtk_object_add_arg_type ("GtkCurve::min_x", GTK_TYPE_FLOAT, GTK_ARG_READWRITE, ARG_MIN_X); gtk_object_add_arg_type ("GtkCurve::max_x", GTK_TYPE_FLOAT, GTK_ARG_READWRITE, ARG_MAX_X); gtk_object_add_arg_type ("GtkCurve::min_y", GTK_TYPE_FLOAT, GTK_ARG_READWRITE, ARG_MIN_Y); gtk_object_add_arg_type ("GtkCurve::max_y", GTK_TYPE_FLOAT, GTK_ARG_READWRITE, ARG_MAX_Y);}static voidgtk_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 = 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) gtk_curve_graph_events, curve); gtk_curve_size_graph (curve);}static voidgtk_curve_set_arg (GtkObject *object, GtkArg *arg, guint arg_id){ GtkCurve *curve = GTK_CURVE (object); switch (arg_id) { case ARG_CURVE_TYPE: gtk_curve_set_curve_type (curve, GTK_VALUE_ENUM (*arg)); break; case ARG_MIN_X: gtk_curve_set_range (curve, GTK_VALUE_FLOAT (*arg), curve->max_x, curve->min_y, curve->max_y); break; case ARG_MAX_X: gtk_curve_set_range (curve, curve->min_x, GTK_VALUE_FLOAT (*arg), curve->min_y, curve->max_y); break; case ARG_MIN_Y: gtk_curve_set_range (curve, curve->min_x, curve->max_x, GTK_VALUE_FLOAT (*arg), curve->max_y); break; case ARG_MAX_Y: gtk_curve_set_range (curve, curve->min_x, curve->max_x, curve->min_y, GTK_VALUE_FLOAT (*arg)); break; }}static voidgtk_curve_get_arg (GtkObject *object, GtkArg *arg, guint arg_id){ GtkCurve *curve = GTK_CURVE (object); switch (arg_id) { case ARG_CURVE_TYPE: GTK_VALUE_ENUM (*arg) = curve->curve_type; break; 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 intproject (gfloat value, gfloat min, gfloat max, int norm){ return (norm - 1) * ((value - min) / (max - min)) + 0.5;}static gfloatunproject (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 voidspline_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 gfloatspline_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 voidgtk_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; 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 voidgtk_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_pixmap (GTK_WIDGET (c)->window, style->fg_gc[state], c->pixmap, 0, 0, 0, 0, width + RADIUS * 2, height + RADIUS * 2);}static gintgtk_curve_graph_events (GtkWidget *widget, GdkEvent *event, GtkCurve *c){ GdkCursorType new_type = c->cursor_type; 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; /* 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) gdk_pixmap_unref (c->pixmap); c->pixmap = 0; /* 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); bevent = (GdkEventButton *) event; 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); break; case GDK_BUTTON_RELEASE: gtk_grab_remove (widget); /* delete inactive points: */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -