source: trunk/third/gtk/gtk/gtkcurve.c @ 14482

Revision 14482, 23.4 KB checked in by ghudson, 24 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r14481, which included commits to RCS files with non-trunk default branches.
Line 
1/* GTK - The GIMP Toolkit
2 * Copyright (C) 1997 David Mosberger
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 */
19
20/*
21 * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
22 * file for a list of people on the GTK+ Team.  See the ChangeLog
23 * files for a list of changes.  These files are distributed with
24 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
25 */
26
27#include <stdlib.h>
28#include <string.h>
29#include <math.h>
30
31#include "gtkcurve.h"
32#include "gtkdrawingarea.h"
33#include "gtkmain.h"
34#include "gtkradiobutton.h"
35#include "gtksignal.h"
36#include "gtktable.h"
37
38#define RADIUS          3       /* radius of the control points */
39#define MIN_DISTANCE    8       /* min distance between control points */
40
41#define GRAPH_MASK      (GDK_EXPOSURE_MASK |            \
42                         GDK_POINTER_MOTION_MASK |      \
43                         GDK_POINTER_MOTION_HINT_MASK | \
44                         GDK_ENTER_NOTIFY_MASK |        \
45                         GDK_BUTTON_PRESS_MASK |        \
46                         GDK_BUTTON_RELEASE_MASK |      \
47                         GDK_BUTTON1_MOTION_MASK)
48
49enum {
50  ARG_0,
51  ARG_CURVE_TYPE,
52  ARG_MIN_X,
53  ARG_MAX_X,
54  ARG_MIN_Y,
55  ARG_MAX_Y
56};
57
58static GtkDrawingAreaClass *parent_class = NULL;
59static guint curve_type_changed_signal = 0;
60
61
62/* forward declarations: */
63static void gtk_curve_class_init   (GtkCurveClass *class);
64static void gtk_curve_init         (GtkCurve      *curve);
65static void gtk_curve_set_arg     (GtkObject      *object,
66                                   GtkArg         *arg,
67                                   guint           arg_id);
68static void gtk_curve_get_arg     (GtkObject      *object,
69                                   GtkArg         *arg,
70                                   guint           arg_id);
71static void gtk_curve_finalize     (GtkObject     *object);
72static gint gtk_curve_graph_events (GtkWidget     *widget,
73                                    GdkEvent      *event,
74                                    GtkCurve      *c);
75static void gtk_curve_size_graph   (GtkCurve      *curve);
76
77GtkType
78gtk_curve_get_type (void)
79{
80  static GtkType curve_type = 0;
81
82  if (!curve_type)
83    {
84      static const GtkTypeInfo curve_info =
85      {
86        "GtkCurve",
87        sizeof (GtkCurve),
88        sizeof (GtkCurveClass),
89        (GtkClassInitFunc) gtk_curve_class_init,
90        (GtkObjectInitFunc) gtk_curve_init,
91        /* reserved_1 */ NULL,
92        /* reserved_2 */ NULL,
93        (GtkClassInitFunc) NULL,
94      };
95
96      curve_type = gtk_type_unique (GTK_TYPE_DRAWING_AREA, &curve_info);
97    }
98  return curve_type;
99}
100
101static void
102gtk_curve_class_init (GtkCurveClass *class)
103{
104  GtkObjectClass *object_class;
105 
106  parent_class = gtk_type_class (GTK_TYPE_DRAWING_AREA);
107 
108  object_class = (GtkObjectClass *) class;
109 
110  object_class->set_arg = gtk_curve_set_arg;
111  object_class->get_arg = gtk_curve_get_arg;
112  object_class->finalize = gtk_curve_finalize;
113 
114  curve_type_changed_signal =
115    gtk_signal_new ("curve_type_changed", GTK_RUN_FIRST, object_class->type,
116                    GTK_SIGNAL_OFFSET (GtkCurveClass, curve_type_changed),
117                    gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0);
118  gtk_object_class_add_signals (object_class, &curve_type_changed_signal, 1);
119 
120  gtk_object_add_arg_type ("GtkCurve::curve_type", GTK_TYPE_CURVE_TYPE,
121                           GTK_ARG_READWRITE, ARG_CURVE_TYPE);
122  gtk_object_add_arg_type ("GtkCurve::min_x", GTK_TYPE_FLOAT,
123                           GTK_ARG_READWRITE, ARG_MIN_X);
124  gtk_object_add_arg_type ("GtkCurve::max_x", GTK_TYPE_FLOAT,
125                           GTK_ARG_READWRITE, ARG_MAX_X);
126  gtk_object_add_arg_type ("GtkCurve::min_y", GTK_TYPE_FLOAT,
127                           GTK_ARG_READWRITE, ARG_MIN_Y);
128  gtk_object_add_arg_type ("GtkCurve::max_y", GTK_TYPE_FLOAT,
129                           GTK_ARG_READWRITE, ARG_MAX_Y);
130}
131
132static void
133gtk_curve_init (GtkCurve *curve)
134{
135  gint old_mask;
136
137  curve->cursor_type = GDK_TOP_LEFT_ARROW;
138  curve->pixmap = NULL;
139  curve->curve_type = GTK_CURVE_TYPE_SPLINE;
140  curve->height = 0;
141  curve->grab_point = -1;
142
143  curve->num_points = 0;
144  curve->point = 0;
145
146  curve->num_ctlpoints = 0;
147  curve->ctlpoint = NULL;
148
149  curve->min_x = 0.0;
150  curve->max_x = 1.0;
151  curve->min_y = 0.0;
152  curve->max_y = 1.0;
153
154  old_mask = gtk_widget_get_events (GTK_WIDGET (curve));
155  gtk_widget_set_events (GTK_WIDGET (curve), old_mask | GRAPH_MASK);
156  gtk_signal_connect (GTK_OBJECT (curve), "event",
157                      (GtkSignalFunc) gtk_curve_graph_events, curve);
158  gtk_curve_size_graph (curve);
159}
160
161static void
162gtk_curve_set_arg (GtkObject *object,
163                   GtkArg    *arg,
164                   guint      arg_id)
165{
166  GtkCurve *curve = GTK_CURVE (object);
167 
168  switch (arg_id)
169    {
170    case ARG_CURVE_TYPE:
171      gtk_curve_set_curve_type (curve, GTK_VALUE_ENUM (*arg));
172      break;
173    case ARG_MIN_X:
174      gtk_curve_set_range (curve, GTK_VALUE_FLOAT (*arg), curve->max_x,
175                           curve->min_y, curve->max_y);
176      break;
177    case ARG_MAX_X:
178      gtk_curve_set_range (curve, curve->min_x, GTK_VALUE_FLOAT (*arg),
179                           curve->min_y, curve->max_y);
180      break;   
181    case ARG_MIN_Y:
182      gtk_curve_set_range (curve, curve->min_x, curve->max_x,
183                           GTK_VALUE_FLOAT (*arg), curve->max_y);
184      break;
185    case ARG_MAX_Y:
186      gtk_curve_set_range (curve, curve->min_x, curve->max_x,
187                           curve->min_y, GTK_VALUE_FLOAT (*arg));
188      break;
189    }
190}
191
192static void
193gtk_curve_get_arg (GtkObject *object,
194                   GtkArg    *arg,
195                   guint      arg_id)
196{
197  GtkCurve *curve = GTK_CURVE (object);
198
199  switch (arg_id)
200    {
201    case ARG_CURVE_TYPE:
202      GTK_VALUE_ENUM (*arg) = curve->curve_type;
203      break;
204    case ARG_MIN_X:
205      GTK_VALUE_FLOAT (*arg) = curve->min_x;
206      break;
207    case ARG_MAX_X:
208      GTK_VALUE_FLOAT (*arg) = curve->max_x;
209      break;
210    case ARG_MIN_Y:
211      GTK_VALUE_FLOAT (*arg) = curve->min_y;
212      break;
213    case ARG_MAX_Y:
214      GTK_VALUE_FLOAT (*arg) = curve->max_y;
215      break;
216    default:
217      arg->type = GTK_TYPE_INVALID;
218      break;
219    }
220}
221
222static int
223project (gfloat value, gfloat min, gfloat max, int norm)
224{
225  return (norm - 1) * ((value - min) / (max - min)) + 0.5;
226}
227
228static gfloat
229unproject (gint value, gfloat min, gfloat max, int norm)
230{
231  return value / (gfloat) (norm - 1) * (max - min) + min;
232}
233
234/* Solve the tridiagonal equation system that determines the second
235   derivatives for the interpolation points.  (Based on Numerical
236   Recipies 2nd Edition.) */
237static void
238spline_solve (int n, gfloat x[], gfloat y[], gfloat y2[])
239{
240  gfloat p, sig, *u;
241  gint i, k;
242
243  u = g_malloc ((n - 1) * sizeof (u[0]));
244
245  y2[0] = u[0] = 0.0;   /* set lower boundary condition to "natural" */
246
247  for (i = 1; i < n - 1; ++i)
248    {
249      sig = (x[i] - x[i - 1]) / (x[i + 1] - x[i - 1]);
250      p = sig * y2[i - 1] + 2.0;
251      y2[i] = (sig - 1.0) / p;
252      u[i] = ((y[i + 1] - y[i])
253              / (x[i + 1] - x[i]) - (y[i] - y[i - 1]) / (x[i] - x[i - 1]));
254      u[i] = (6.0 * u[i] / (x[i + 1] - x[i - 1]) - sig * u[i - 1]) / p;
255    }
256
257  y2[n - 1] = 0.0;
258  for (k = n - 2; k >= 0; --k)
259    y2[k] = y2[k] * y2[k + 1] + u[k];
260
261  g_free (u);
262}
263
264static gfloat
265spline_eval (int n, gfloat x[], gfloat y[], gfloat y2[], gfloat val)
266{
267  gint k_lo, k_hi, k;
268  gfloat h, b, a;
269
270  /* do a binary search for the right interval: */
271  k_lo = 0; k_hi = n - 1;
272  while (k_hi - k_lo > 1)
273    {
274      k = (k_hi + k_lo) / 2;
275      if (x[k] > val)
276        k_hi = k;
277      else
278        k_lo = k;
279    }
280
281  h = x[k_hi] - x[k_lo];
282  g_assert (h > 0.0);
283
284  a = (x[k_hi] - val) / h;
285  b = (val - x[k_lo]) / h;
286  return a*y[k_lo] + b*y[k_hi] +
287    ((a*a*a - a)*y2[k_lo] + (b*b*b - b)*y2[k_hi]) * (h*h)/6.0;
288}
289
290static void
291gtk_curve_interpolate (GtkCurve *c, gint width, gint height)
292{
293  gfloat *vector;
294  int i;
295
296  vector = g_malloc (width * sizeof (vector[0]));
297
298  gtk_curve_get_vector (c, width, vector);
299
300  c->height = height;
301  if (c->num_points != width)
302    {
303      c->num_points = width;
304      if (c->point)
305        g_free (c->point);
306      c->point = g_malloc (c->num_points * sizeof (c->point[0]));
307    }
308
309  for (i = 0; i < width; ++i)
310    {
311      c->point[i].x = RADIUS + i;
312      c->point[i].y = RADIUS + height
313        - project (vector[i], c->min_y, c->max_y, height);
314    }
315
316  g_free (vector);
317}
318
319static void
320gtk_curve_draw (GtkCurve *c, gint width, gint height)
321{
322  GtkStateType state;
323  GtkStyle *style;
324  gint i;
325
326  if (!c->pixmap)
327    return;
328
329  if (c->height != height || c->num_points != width)
330    gtk_curve_interpolate (c, width, height);
331
332  state = GTK_STATE_NORMAL;
333  if (!GTK_WIDGET_IS_SENSITIVE (GTK_WIDGET (c)))
334    state = GTK_STATE_INSENSITIVE;
335
336  style = GTK_WIDGET (c)->style;
337
338  /* clear the pixmap: */
339  gtk_paint_flat_box (style, c->pixmap, GTK_STATE_NORMAL, GTK_SHADOW_NONE,
340                      NULL, GTK_WIDGET(c), "curve_bg",
341                      0, 0, width + RADIUS * 2, height + RADIUS * 2);
342  /* draw the grid lines: (XXX make more meaningful) */
343  for (i = 0; i < 5; i++)
344    {
345      gdk_draw_line (c->pixmap, style->dark_gc[state],
346                     RADIUS, i * (height / 4.0) + RADIUS,
347                     width + RADIUS, i * (height / 4.0) + RADIUS);
348      gdk_draw_line (c->pixmap, style->dark_gc[state],
349                     i * (width / 4.0) + RADIUS, RADIUS,
350                     i * (width / 4.0) + RADIUS, height + RADIUS);
351    }
352
353  gdk_draw_points (c->pixmap, style->fg_gc[state], c->point, c->num_points);
354  if (c->curve_type != GTK_CURVE_TYPE_FREE)
355    for (i = 0; i < c->num_ctlpoints; ++i)
356      {
357        gint x, y;
358
359        if (c->ctlpoint[i][0] < c->min_x)
360          continue;
361
362        x = project (c->ctlpoint[i][0], c->min_x, c->max_x,
363                     width);
364        y = height -
365          project (c->ctlpoint[i][1], c->min_y, c->max_y,
366                   height);
367
368        /* draw a bullet: */
369        gdk_draw_arc (c->pixmap, style->fg_gc[state], TRUE, x, y,
370                      RADIUS * 2, RADIUS*2, 0, 360*64);
371      }
372  gdk_draw_pixmap (GTK_WIDGET (c)->window, style->fg_gc[state], c->pixmap,
373                   0, 0, 0, 0, width + RADIUS * 2, height + RADIUS * 2);
374}
375
376static gint
377gtk_curve_graph_events (GtkWidget *widget, GdkEvent *event, GtkCurve *c)
378{
379  GdkCursorType new_type = c->cursor_type;
380  gint i, src, dst, leftbound, rightbound;
381  GdkEventButton *bevent;
382  GdkEventMotion *mevent;
383  GtkWidget *w;
384  gint tx, ty;
385  gint cx, x, y, width, height;
386  gint closest_point = 0;
387  gfloat rx, ry, min_x;
388  guint distance;
389  gint x1, x2, y1, y2;
390
391  w = GTK_WIDGET (c);
392  width = w->allocation.width - RADIUS * 2;
393  height = w->allocation.height - RADIUS * 2;
394
395  if ((width < 0) || (height < 0))
396    return FALSE;
397
398  /*  get the pointer position  */
399  gdk_window_get_pointer (w->window, &tx, &ty, NULL);
400  x = CLAMP ((tx - RADIUS), 0, width-1);
401  y = CLAMP ((ty - RADIUS), 0, height-1);
402
403  min_x = c->min_x;
404
405  distance = ~0U;
406  for (i = 0; i < c->num_ctlpoints; ++i)
407    {
408      cx = project (c->ctlpoint[i][0], min_x, c->max_x, width);
409      if ((guint) abs (x - cx) < distance)
410        {
411          distance = abs (x - cx);
412          closest_point = i;
413        }
414    }
415
416  switch (event->type)
417    {
418    case GDK_CONFIGURE:
419      if (c->pixmap)
420        gdk_pixmap_unref (c->pixmap);
421      c->pixmap = 0;
422      /* fall through */
423    case GDK_EXPOSE:
424      if (!c->pixmap)
425        c->pixmap = gdk_pixmap_new (w->window,
426                                    w->allocation.width,
427                                    w->allocation.height, -1);
428      gtk_curve_draw (c, width, height);
429      break;
430
431    case GDK_BUTTON_PRESS:
432      gtk_grab_add (widget);
433
434      bevent = (GdkEventButton *) event;
435      new_type = GDK_TCROSS;
436
437      switch (c->curve_type)
438        {
439        case GTK_CURVE_TYPE_LINEAR:
440        case GTK_CURVE_TYPE_SPLINE:
441          if (distance > MIN_DISTANCE)
442            {
443              /* insert a new control point */
444              if (c->num_ctlpoints > 0)
445                {
446                  cx = project (c->ctlpoint[closest_point][0], min_x,
447                                c->max_x, width);
448                  if (x > cx)
449                    ++closest_point;
450                }
451              ++c->num_ctlpoints;
452              c->ctlpoint =
453                g_realloc (c->ctlpoint,
454                           c->num_ctlpoints * sizeof (*c->ctlpoint));
455              for (i = c->num_ctlpoints - 1; i > closest_point; --i)
456                memcpy (c->ctlpoint + i, c->ctlpoint + i - 1,
457                        sizeof (*c->ctlpoint));
458            }
459          c->grab_point = closest_point;
460          c->ctlpoint[c->grab_point][0] =
461            unproject (x, min_x, c->max_x, width);
462          c->ctlpoint[c->grab_point][1] =
463            unproject (height - y, c->min_y, c->max_y, height);
464
465          gtk_curve_interpolate (c, width, height);
466          break;
467
468        case GTK_CURVE_TYPE_FREE:
469          c->point[x].x = RADIUS + x;
470          c->point[x].y = RADIUS + y;
471          c->grab_point = x;
472          c->last = y;
473          break;
474        }
475      gtk_curve_draw (c, width, height);
476      break;
477
478    case GDK_BUTTON_RELEASE:
479      gtk_grab_remove (widget);
480
481      /* delete inactive points: */
482      if (c->curve_type != GTK_CURVE_TYPE_FREE)
483        {
484          for (src = dst = 0; src < c->num_ctlpoints; ++src)
485            {
486              if (c->ctlpoint[src][0] >= min_x)
487                {
488                  memcpy (c->ctlpoint + dst, c->ctlpoint + src,
489                          sizeof (*c->ctlpoint));
490                  ++dst;
491                }
492            }
493          if (dst < src)
494            {
495              c->num_ctlpoints -= (src - dst);
496              if (c->num_ctlpoints <= 0)
497                {
498                  c->num_ctlpoints = 1;
499                  c->ctlpoint[0][0] = min_x;
500                  c->ctlpoint[0][1] = c->min_y;
501                  gtk_curve_interpolate (c, width, height);
502                  gtk_curve_draw (c, width, height);
503                }
504              c->ctlpoint =
505                g_realloc (c->ctlpoint,
506                           c->num_ctlpoints * sizeof (*c->ctlpoint));
507            }
508        }
509      new_type = GDK_FLEUR;
510      c->grab_point = -1;
511      break;
512
513    case GDK_MOTION_NOTIFY:
514      mevent = (GdkEventMotion *) event;
515
516      switch (c->curve_type)
517        {
518        case GTK_CURVE_TYPE_LINEAR:
519        case GTK_CURVE_TYPE_SPLINE:
520          if (c->grab_point == -1)
521            {
522              /* if no point is grabbed...  */
523              if (distance <= MIN_DISTANCE)
524                new_type = GDK_FLEUR;
525              else
526                new_type = GDK_TCROSS;
527            }
528          else
529            {
530              /* drag the grabbed point  */
531              new_type = GDK_TCROSS;
532
533              leftbound = -MIN_DISTANCE;
534              if (c->grab_point > 0)
535                leftbound = project (c->ctlpoint[c->grab_point - 1][0],
536                                     min_x, c->max_x, width);
537
538              rightbound = width + RADIUS * 2 + MIN_DISTANCE;
539              if (c->grab_point + 1 < c->num_ctlpoints)
540                rightbound = project (c->ctlpoint[c->grab_point + 1][0],
541                                      min_x, c->max_x, width);
542
543              if (tx <= leftbound || tx >= rightbound
544                  || ty > height + RADIUS * 2 + MIN_DISTANCE
545                  || ty < -MIN_DISTANCE)
546                c->ctlpoint[c->grab_point][0] = min_x - 1.0;
547              else
548                {
549                  rx = unproject (x, min_x, c->max_x, width);
550                  ry = unproject (height - y, c->min_y, c->max_y, height);
551                  c->ctlpoint[c->grab_point][0] = rx;
552                  c->ctlpoint[c->grab_point][1] = ry;
553                }
554              gtk_curve_interpolate (c, width, height);
555              gtk_curve_draw (c, width, height);
556            }
557          break;
558
559        case GTK_CURVE_TYPE_FREE:
560          if (c->grab_point != -1)
561            {
562              if (c->grab_point > x)
563                {
564                  x1 = x;
565                  x2 = c->grab_point;
566                  y1 = y;
567                  y2 = c->last;
568                }
569              else
570                {
571                  x1 = c->grab_point;
572                  x2 = x;
573                  y1 = c->last;
574                  y2 = y;
575                }
576
577              if (x2 != x1)
578                for (i = x1; i <= x2; i++)
579                  {
580                    c->point[i].x = RADIUS + i;
581                    c->point[i].y = RADIUS +
582                      (y1 + ((y2 - y1) * (i - x1)) / (x2 - x1));
583                  }
584              else
585                {
586                  c->point[x].x = RADIUS + x;
587                  c->point[x].y = RADIUS + y;
588                }
589              c->grab_point = x;
590              c->last = y;
591              gtk_curve_draw (c, width, height);
592            }
593          if (mevent->state & GDK_BUTTON1_MASK)
594            new_type = GDK_TCROSS;
595          else
596            new_type = GDK_PENCIL;
597          break;
598        }
599      if (new_type != (GdkCursorType) c->cursor_type)
600        {
601          GdkCursor *cursor;
602
603          c->cursor_type = new_type;
604
605          cursor = gdk_cursor_new (c->cursor_type);
606          gdk_window_set_cursor (w->window, cursor);
607          gdk_cursor_destroy (cursor);
608        }
609      break;
610
611    default:
612      break;
613    }
614  return FALSE;
615}
616
617void
618gtk_curve_set_curve_type (GtkCurve *c, GtkCurveType new_type)
619{
620  gfloat rx, dx;
621  gint x, i;
622
623  if (new_type != c->curve_type)
624    {
625      gint width, height;
626
627      width  = GTK_WIDGET(c)->allocation.width - RADIUS * 2;
628      height = GTK_WIDGET(c)->allocation.height - RADIUS * 2;
629
630      if (new_type == GTK_CURVE_TYPE_FREE)
631        {
632          gtk_curve_interpolate (c, width, height);
633          c->curve_type = new_type;
634        }
635      else if (c->curve_type == GTK_CURVE_TYPE_FREE)
636        {
637          if (c->ctlpoint)
638            g_free (c->ctlpoint);
639          c->num_ctlpoints = 9;
640          c->ctlpoint = g_malloc (c->num_ctlpoints * sizeof (*c->ctlpoint));
641
642          rx = 0.0;
643          dx = (width - 1) / (gfloat) (c->num_ctlpoints - 1);
644
645          for (i = 0; i < c->num_ctlpoints; ++i, rx += dx)
646            {
647              x = (int) (rx + 0.5);
648              c->ctlpoint[i][0] =
649                unproject (x, c->min_x, c->max_x, width);
650              c->ctlpoint[i][1] =
651                unproject (RADIUS + height - c->point[x].y,
652                           c->min_y, c->max_y, height);
653            }
654          c->curve_type = new_type;
655          gtk_curve_interpolate (c, width, height);
656        }
657      else
658        {
659          c->curve_type = new_type;
660          gtk_curve_interpolate (c, width, height);
661        }
662      gtk_signal_emit (GTK_OBJECT (c), curve_type_changed_signal);
663      gtk_curve_draw (c, width, height);
664    }
665}
666
667static void
668gtk_curve_size_graph (GtkCurve *curve)
669{
670  gint width, height;
671  gfloat aspect;
672
673  width  = (curve->max_x - curve->min_x) + 1;
674  height = (curve->max_y - curve->min_y) + 1;
675  aspect = width / (gfloat) height;
676  if (width > gdk_screen_width () / 4)
677    width  = gdk_screen_width () / 4;
678  if (height > gdk_screen_height () / 4)
679    height = gdk_screen_height () / 4;
680
681  if (aspect < 1.0)
682    width  = height * aspect;
683  else
684    height = width / aspect;
685
686  gtk_drawing_area_size (GTK_DRAWING_AREA (curve),
687                         width + RADIUS * 2, height + RADIUS * 2);
688}
689
690static void
691gtk_curve_reset_vector (GtkCurve *curve)
692{
693  if (curve->ctlpoint)
694    g_free (curve->ctlpoint);
695
696  curve->num_ctlpoints = 2;
697  curve->ctlpoint = g_malloc (2 * sizeof (curve->ctlpoint[0]));
698  curve->ctlpoint[0][0] = curve->min_x;
699  curve->ctlpoint[0][1] = curve->min_y;
700  curve->ctlpoint[1][0] = curve->max_x;
701  curve->ctlpoint[1][1] = curve->max_y;
702
703  if (curve->pixmap)
704    {
705      gint width, height;
706
707      width = GTK_WIDGET (curve)->allocation.width - RADIUS * 2;
708      height = GTK_WIDGET (curve)->allocation.height - RADIUS * 2;
709
710      if (curve->curve_type == GTK_CURVE_TYPE_FREE)
711        {
712          curve->curve_type = GTK_CURVE_TYPE_LINEAR;
713          gtk_curve_interpolate (curve, width, height);
714          curve->curve_type = GTK_CURVE_TYPE_FREE;
715        }
716      else
717        gtk_curve_interpolate (curve, width, height);
718      gtk_curve_draw (curve, width, height);
719    }
720}
721
722void
723gtk_curve_reset (GtkCurve *c)
724{
725  GtkCurveType old_type;
726
727  old_type = c->curve_type;
728  c->curve_type = GTK_CURVE_TYPE_SPLINE;
729  gtk_curve_reset_vector (c);
730
731  if (old_type != GTK_CURVE_TYPE_SPLINE)
732    gtk_signal_emit (GTK_OBJECT (c), curve_type_changed_signal);
733}
734
735void
736gtk_curve_set_gamma (GtkCurve *c, gfloat gamma)
737{
738  gfloat x, one_over_gamma, height, one_over_width;
739  GtkCurveType old_type;
740  gint i;
741
742  if (c->num_points < 2)
743    return;
744
745  old_type = c->curve_type;
746  c->curve_type = GTK_CURVE_TYPE_FREE;
747
748  if (gamma <= 0)
749    one_over_gamma = 1.0;
750  else
751    one_over_gamma = 1.0 / gamma;
752  one_over_width = 1.0 / (c->num_points - 1);
753  height = c->height;
754  for (i = 0; i < c->num_points; ++i)
755    {
756      x = (gfloat) i / (c->num_points - 1);
757      c->point[i].x = RADIUS + i;
758      c->point[i].y =
759        RADIUS + (height * (1.0 - pow (x, one_over_gamma)) + 0.5);
760    }
761
762  if (old_type != GTK_CURVE_TYPE_FREE)
763    gtk_signal_emit (GTK_OBJECT (c), curve_type_changed_signal);
764
765  gtk_curve_draw (c, c->num_points, c->height);
766}
767
768void
769gtk_curve_set_range (GtkCurve *curve,
770                     gfloat min_x, gfloat max_x, gfloat min_y, gfloat max_y)
771{
772  curve->min_x = min_x;
773  curve->max_x = max_x;
774  curve->min_y = min_y;
775  curve->max_y = max_y;
776
777  gtk_curve_size_graph (curve);
778  gtk_curve_reset_vector (curve);
779}
780
781void
782gtk_curve_set_vector (GtkCurve *c, int veclen, gfloat vector[])
783{
784  GtkCurveType old_type;
785  gfloat rx, dx, ry;
786  gint i, height;
787
788  old_type = c->curve_type;
789  c->curve_type = GTK_CURVE_TYPE_FREE;
790
791  if (c->point)
792    height = GTK_WIDGET (c)->allocation.height - RADIUS * 2;
793  else
794    {
795      height = (c->max_y - c->min_y);
796      if (height > gdk_screen_height () / 4)
797        height = gdk_screen_height () / 4;
798
799      c->height = height;
800      c->num_points = veclen;
801      c->point = g_malloc (c->num_points * sizeof (c->point[0]));
802    }
803  rx = 0;
804  dx = (veclen - 1.0) / (c->num_points - 1.0);
805
806  for (i = 0; i < c->num_points; ++i, rx += dx)
807    {
808      ry = vector[(int) (rx + 0.5)];
809      if (ry > c->max_y) ry = c->max_y;
810      if (ry < c->min_y) ry = c->min_y;
811      c->point[i].x = RADIUS + i;
812      c->point[i].y =
813        RADIUS + height - project (ry, c->min_y, c->max_y, height);
814    }
815  if (old_type != GTK_CURVE_TYPE_FREE)
816    gtk_signal_emit (GTK_OBJECT (c), curve_type_changed_signal);
817
818  gtk_curve_draw (c, c->num_points, height);
819}
820
821void
822gtk_curve_get_vector (GtkCurve *c, int veclen, gfloat vector[])
823{
824  gfloat rx, ry, dx, dy, min_x, delta_x, *mem, *xv, *yv, *y2v, prev;
825  gint dst, i, x, next, num_active_ctlpoints = 0, first_active = -1;
826
827  min_x = c->min_x;
828
829  if (c->curve_type != GTK_CURVE_TYPE_FREE)
830    {
831      /* count active points: */
832      prev = min_x - 1.0;
833      for (i = num_active_ctlpoints = 0; i < c->num_ctlpoints; ++i)
834        if (c->ctlpoint[i][0] > prev)
835          {
836            if (first_active < 0)
837              first_active = i;
838            prev = c->ctlpoint[i][0];
839            ++num_active_ctlpoints;
840          }
841
842      /* handle degenerate case: */
843      if (num_active_ctlpoints < 2)
844        {
845          if (num_active_ctlpoints > 0)
846            ry = c->ctlpoint[first_active][1];
847          else
848            ry = c->min_y;
849          if (ry < c->min_y) ry = c->min_y;
850          if (ry > c->max_y) ry = c->max_y;
851          for (x = 0; x < veclen; ++x)
852            vector[x] = ry;
853          return;
854        }
855    }
856
857  switch (c->curve_type)
858    {
859    case GTK_CURVE_TYPE_SPLINE:
860      mem = g_malloc (3 * num_active_ctlpoints * sizeof (gfloat));
861      xv  = mem;
862      yv  = mem + num_active_ctlpoints;
863      y2v = mem + 2*num_active_ctlpoints;
864
865      prev = min_x - 1.0;
866      for (i = dst = 0; i < c->num_ctlpoints; ++i)
867        if (c->ctlpoint[i][0] > prev)
868          {
869            prev    = c->ctlpoint[i][0];
870            xv[dst] = c->ctlpoint[i][0];
871            yv[dst] = c->ctlpoint[i][1];
872            ++dst;
873          }
874
875      spline_solve (num_active_ctlpoints, xv, yv, y2v);
876
877      rx = min_x;
878      dx = (c->max_x - min_x) / (veclen - 1);
879      for (x = 0; x < veclen; ++x, rx += dx)
880        {
881          ry = spline_eval (num_active_ctlpoints, xv, yv, y2v, rx);
882          if (ry < c->min_y) ry = c->min_y;
883          if (ry > c->max_y) ry = c->max_y;
884          vector[x] = ry;
885        }
886
887      g_free (mem);
888      break;
889
890    case GTK_CURVE_TYPE_LINEAR:
891      dx = (c->max_x - min_x) / (veclen - 1);
892      rx = min_x;
893      ry = c->min_y;
894      dy = 0.0;
895      i  = first_active;
896      for (x = 0; x < veclen; ++x, rx += dx)
897        {
898          if (rx >= c->ctlpoint[i][0])
899            {
900              if (rx > c->ctlpoint[i][0])
901                ry = c->min_y;
902              dy = 0.0;
903              next = i + 1;
904              while (next < c->num_ctlpoints
905                     && c->ctlpoint[next][0] <= c->ctlpoint[i][0])
906                ++next;
907              if (next < c->num_ctlpoints)
908                {
909                  delta_x = c->ctlpoint[next][0] - c->ctlpoint[i][0];
910                  dy = ((c->ctlpoint[next][1] - c->ctlpoint[i][1])
911                        / delta_x);
912                  dy *= dx;
913                  ry = c->ctlpoint[i][1];
914                  i = next;
915                }
916            }
917          vector[x] = ry;
918          ry += dy;
919        }
920      break;
921
922    case GTK_CURVE_TYPE_FREE:
923      if (c->point)
924        {
925          rx = 0.0;
926          dx = c->num_points / (double) veclen;
927          for (x = 0; x < veclen; ++x, rx += dx)
928            vector[x] = unproject (RADIUS + c->height - c->point[(int) rx].y,
929                                   c->min_y, c->max_y,
930                                   c->height);
931        }
932      else
933        memset (vector, 0, veclen * sizeof (vector[0]));
934      break;
935    }
936}
937
938GtkWidget*
939gtk_curve_new (void)
940{
941  return gtk_type_new (gtk_curve_get_type ());
942}
943
944static void
945gtk_curve_finalize (GtkObject *object)
946{
947  GtkCurve *curve;
948
949  g_return_if_fail (object != NULL);
950  g_return_if_fail (GTK_IS_CURVE (object));
951
952  curve = GTK_CURVE (object);
953  if (curve->pixmap)
954    gdk_pixmap_unref (curve->pixmap);
955  if (curve->point)
956    g_free (curve->point);
957  if (curve->ctlpoint)
958    g_free (curve->ctlpoint);
959
960  (*GTK_OBJECT_CLASS (parent_class)->finalize) (object);
961}
Note: See TracBrowser for help on using the repository browser.