[f-spot] new FSpot.Widgets.Curve widget.
- From: Stephane Delcroix <sdelcroix src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [f-spot] new FSpot.Widgets.Curve widget.
- Date: Wed, 23 Sep 2009 09:22:13 +0000 (UTC)
commit a843e3d3d311b0b2669a6439c000ac277710afa3
Author: Stephane Delcroix <stephane delcroix org>
Date: Wed Sep 23 10:02:59 2009 +0200
new FSpot.Widgets.Curve widget.
this widget is a port of Gtk.Curve to managed in order to add a CurveChanged event (and some more stuffs, like histogram) in the future.
src/Makefile.am | 2 +
src/Widgets/Curve.cs | 438 ++++++++++++++++++++++++++++++++++++++++++++++
src/Widgets/CurveType.cs | 23 +++
3 files changed, 463 insertions(+), 0 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index 00e2255..3785f0d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -86,6 +86,8 @@ WIDGETS_CSDISTFILES = \
$(srcdir)/Widgets/BuilderWindow.cs \
$(srcdir)/Widgets/CheckPattern.cs \
$(srcdir)/Widgets/ComplexMenuItem.cs \
+ $(srcdir)/Widgets/Curve.cs \
+ $(srcdir)/Widgets/CurveType.cs \
$(srcdir)/Widgets/CustomPrintWidget.cs \
$(srcdir)/Widgets/DateEdit.cs \
$(srcdir)/Widgets/DateEditFlags.cs \
diff --git a/src/Widgets/Curve.cs b/src/Widgets/Curve.cs
new file mode 100644
index 0000000..730b135
--- /dev/null
+++ b/src/Widgets/Curve.cs
@@ -0,0 +1,438 @@
+//
+// FSpot.Widgets.Curve.cs
+//
+// Author(s):
+// Stephane Delcroix <stephane delcroix org>
+//
+// Copyright (c) 2009 Novell, Inc.
+//
+// This is open source software. See COPYING for details.
+//
+// Ported from Gtk+, where this widget and is no longer really supported.
+//
+
+using System;
+using System.Collections.Generic;
+using Gtk;
+using Gdk;
+
+namespace FSpot.Widgets
+{
+ public class Curve : DrawingArea
+ {
+#region public API
+ public Curve () : base ()
+ {
+ Events |= EventMask.ExposureMask
+ | EventMask.PointerMotionMask
+ | EventMask.PointerMotionHintMask
+ | EventMask.EnterNotifyMask
+ | EventMask.ButtonPressMask
+ | EventMask.ButtonReleaseMask
+ | EventMask.Button1MotionMask;
+ ResetVector ();
+ }
+
+ public void Reset ()
+ {
+ CurveType old_type = CurveType;
+ CurveType = CurveType.Spline;
+ ResetVector ();
+ EventHandler eh;
+ if (old_type != CurveType.Spline && (eh = CurveTypeChanged) != null)
+ eh (this, EventArgs.Empty);
+ }
+
+ float min_x = 0f;
+ public float MinX {
+ get { return min_x; }
+ set { SetRange (value, max_x, min_y, max_y); }
+ }
+
+ float max_x = 1.0f;
+ public float MaxX {
+ get { return max_x; }
+ set { SetRange (min_x, value, min_y, max_y); }
+ }
+
+ float min_y = 0f;
+ public float MinY {
+ get { return min_y; }
+ set { SetRange (min_x, max_x, value, max_y); }
+ }
+
+ float max_y = 1.0f;
+ public float MaxY {
+ get { return max_y; }
+ set { SetRange (min_x, max_x, min_y, value); }
+ }
+
+ public void SetRange (float min_x, float max_x, float min_y, float max_y)
+ {
+ this.min_x = min_x;
+ this.max_x = max_x;
+ this.min_y = min_y;
+ this.max_y = max_y;
+
+ ResetVector ();
+ QueueDraw ();
+ }
+
+ CurveType curve_type = CurveType.Spline;
+ public CurveType CurveType {
+ get { return curve_type; }
+ set {
+ curve_type = value;
+ QueueDraw ();
+ }
+ }
+
+ public float [] GetVector (int len)
+ {
+ if (len <= 0)
+ return null;
+
+ var vector = new float [len];
+
+ var xv = new float [points.Count];
+ var yv = new float [points.Count];
+ int i = 0;
+ foreach (var keyval in points) {
+ xv[i] = keyval.Key;
+ yv[i] = keyval.Value;
+ i++;
+ }
+ float rx = MinX;
+ float dx = (MaxX - MinX) / (len - 1.0f);
+
+ switch (CurveType) {
+ case CurveType.Spline:
+ var y2v = SplineSolve (xv, yv);
+
+ for (int x = 0; x < len; x++, rx += dx) {
+ float ry = SplineEval (xv, yv, y2v, rx);
+ if (ry < MinY)
+ ry = MinY;
+ if (ry > MaxY)
+ ry = MaxY;
+ vector[x] = ry;
+ }
+ break;;
+ case CurveType.Linear:
+ for (int x = 0; x < len; x++, rx += dx) {
+ float ry = LinearEval (xv, yv, rx);
+ if (ry < MinY)
+ ry = MinY;
+ if (ry > MaxY)
+ ry = MaxY;
+ vector[x] = ry;
+ }
+ break;
+ case CurveType.Free:
+ throw new NotImplementedException ();
+ break;
+ }
+
+ return vector;
+ }
+
+ public void SetVector (float[] vector)
+ {
+ }
+
+ public void AddPoint (float x, float y)
+ {
+ points.Add (x, y);
+ EventHandler eh = CurveChanged;
+ if (eh != null)
+ eh (this, EventArgs.Empty);
+ }
+
+ public event EventHandler CurveTypeChanged;
+ public event EventHandler CurveChanged;
+#endregion
+
+#region vector handling
+ SortedDictionary<float, float> points;
+ void ResetVector ()
+ {
+ points = new SortedDictionary<float, float> ();
+ points.Add (min_x, min_y);
+ points.Add (max_x, max_y);
+ points.Add (.2f, .1f);
+ points.Add (.5f, .5f);
+ points.Add (.8f, .9f);
+ }
+#endregion
+
+#region math helpers
+ /* Solve the tridiagonal equation system that determines the second
+ derivatives for the interpolation points. (Based on Numerical
+ Recipies 2nd Edition) */
+ static float [] SplineSolve (float[] x, float[] y)
+ {
+ var y2 = new float [x.Length];
+ var u = new float [x.Length - 1];
+
+ y2[0] = u[0] = 0.0f; //Set lower boundary condition to "natural"
+
+ for (int i = 1; i < x.Length - 1; ++i) {
+ float sig = (x[i] - x[i - 1]) / (x[i + 1] - x[i - 1]);
+ float p = sig * y2[i - 1] + 2.0f;
+ y2[i] = (sig - 1.0f) / 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.0f * u[i] / (x[i + 1] - x[i - 1]) - sig * u[i - 1]) / p;
+ }
+
+ y2[x.Length - 1] = 0.0f;
+ for (int k = x.Length - 2; k >= 0; --k)
+ y2[k] = y2[k] * y2[k + 1] + u[k];
+
+ return y2;
+ }
+
+ /* Returns a y value for val, given x[], y[] and y2[] */
+ static float SplineEval (float[] x, float[] y, float[] y2, float val)
+ {
+ //binary search for the right interval
+ int k_lo = 0;
+ int k_hi = x.Length - 1;
+ while (k_hi - k_lo > 1) {
+ int k = (k_hi + k_lo) / 2;
+ if (x[k] > val)
+ k_hi = k;
+ else
+ k_lo = k;
+ }
+ float h = x[k_hi] - x[k_lo];
+ float a = (x[k_hi] - val) / h;
+ float 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.0f;
+ }
+
+ static float LinearEval (float[] x, float[] y, float val)
+ {
+ //binary search for the right interval
+ int k_lo = 0;
+ int k_hi = x.Length - 1;
+ while (k_hi - k_lo > 1) {
+ int k = (k_hi + k_lo) / 2;
+ if (x[k] > val)
+ k_hi = k;
+ else
+ k_lo = k;
+ }
+ float dx = x[k_hi] - x[k_lo];
+ float dy = y[k_hi] - y[k_lo];
+ return val*dy/dx + y[k_lo] - dy/dx*x[k_lo];
+ }
+
+ static int Project (float val, float min, float max, int norm)
+ {
+ return (int)((norm - 1) * ((val - min) / (max - min)) + .5f);
+ }
+
+ static float Unproject (int val, float min, float max, int norm)
+ {
+ return val / (float) (norm - 1) * (max - min) + min;
+ }
+#endregion
+
+#region Gtk widgetry
+ const int radius = 3; //radius of the control points
+ const int min_distance = 8; //min distance between control points
+ int x_offset = radius;
+ int y_offset = radius;
+ int width, height; //the real graph
+
+ Pixmap pixmap = null;
+
+ protected override bool OnConfigureEvent (EventConfigure evnt)
+ {
+ pixmap = null;
+ return base.OnConfigureEvent (evnt);
+ }
+
+ protected override bool OnExposeEvent (EventExpose evnt)
+ {
+ pixmap = new Pixmap (GdkWindow, Allocation.Width, Allocation.Height);
+ Draw ();
+ return base.OnExposeEvent (evnt);
+ }
+
+ Gdk.Point [] Interpolate (int width, int height)
+ {
+ var vector = GetVector (width);
+ var retval = new Gdk.Point [width];
+ for (int i = 0; i < width; i++) {
+ retval[i].X = x_offset + i;
+ retval[i].Y = y_offset + height - Project (vector[i], MinY, MaxY, height);
+ }
+ return retval;
+ }
+
+ void Draw ()
+ {
+ if (pixmap == null)
+ return;
+
+ Style style = Style;
+ StateType state = Sensitive ? StateType.Normal : StateType.Insensitive;
+
+ if (width <= 0 || height <= 0)
+ return;
+
+ //clear the pixmap
+ GtkBeans.Style.PaintFlatBox (style, pixmap, StateType.Normal, ShadowType.None, null, this, "curve_bg", 0, 0, Allocation.Width, Allocation.Height);
+
+ //draw the grid lines
+ for (int i = 0; i < 5; i++) {
+ pixmap.DrawLine (style.DarkGC (state),
+ x_offset,
+ i * (int)(height / 4.0) + y_offset,
+ width + x_offset,
+ i * (int)(height / 4.0) + y_offset);
+ pixmap.DrawLine (style.DarkGC (state),
+ i * (int)(width / 4.0) + x_offset,
+ y_offset,
+ i * (int)(width / 4.0) + x_offset,
+ height + y_offset);
+ }
+
+ //draw the curve
+ pixmap.DrawPoints (style.ForegroundGC (state), Interpolate (width, height));
+
+ //draw the bullets
+ if (CurveType != CurveType.Free)
+ foreach (var keyval in points) {
+ if (keyval.Key < MinX)
+ continue;
+ int x = Project (keyval.Key, MinX, MaxX, width);
+ int y = height - Project (keyval.Value, MinY, MaxY, height);
+ pixmap.DrawArc (style.ForegroundGC (state), true, x, y, radius * 2, radius * 2, 0, 360*64);
+ }
+ GdkWindow.DrawDrawable (style.ForegroundGC (state), pixmap, 0, 0, 0, 0, Allocation.Width, Allocation.Height);
+ }
+
+ protected override void OnSizeAllocated (Rectangle allocation)
+ {
+ width = allocation.Width - 2 * radius;
+ height = allocation.Height - 2 * radius;
+ base.OnSizeAllocated (allocation);
+ }
+
+ protected override void OnSizeRequested (ref Requisition requisition)
+ {
+ requisition.Width = 128 + 2 * x_offset;
+ requisition.Height = 128 + 2 * y_offset;
+ }
+
+ float? grab_point = null;
+ protected override bool OnButtonPressEvent (EventButton evnt)
+ {
+ int px = (int)evnt.X - x_offset;
+ int py = (int)evnt.Y - y_offset;
+ if (px < 0) px = 0;
+ if (px > width - 1) px = width - 1;
+ if (py < 0) py = 0;
+ if (py > height - 1) py = height - 1;
+
+ //find the closest point
+ float closest_x = MinX - 1;
+ var distance = Int32.MaxValue;
+ foreach (var point in points) {
+ int cx = Project (point.Key, MinX, MaxX, width);
+ if (Math.Abs (px - cx) < distance) {
+ distance = Math.Abs (px - cx);
+ closest_x = point.Key;
+ }
+ }
+
+ Grab.Add (this);
+ CursorType = CursorType.Tcross;
+ switch (CurveType) {
+ case CurveType.Linear:
+ case CurveType.Spline:
+ if (distance > min_distance) {
+ //insert a new control point
+ AddPoint ((closest_x = Unproject (px, MinX, MaxX, width)), MaxY - Unproject (py, MinY, MaxY, height));
+ QueueDraw ();
+ }
+ grab_point = closest_x;
+ break;
+ case CurveType.Free:
+ throw new NotImplementedException ();
+ break;
+ }
+
+ return true;
+ }
+
+ protected override bool OnButtonReleaseEvent (EventButton evnt)
+ {
+ Grab.Remove (this);
+ //FIXME: remove inactive points
+
+ CursorType = CursorType.Fleur;
+ grab_point = null;
+ return true;
+ }
+
+ protected override bool OnMotionNotifyEvent (EventMotion evnt)
+ {
+ int px = (int)evnt.X - x_offset;
+ int py = (int)evnt.Y - y_offset;
+ if (px < 0) px = 0;
+ if (px > width - 1) px = width - 1;
+ if (py < 0) py = 0;
+ if (py > height - 1) py = height - 1;
+
+ //find the closest point
+ float closest_x = MinX - 1;
+ var distance = Int32.MaxValue;
+ foreach (var point in points) {
+ int cx = Project (point.Key, MinX, MaxX, width);
+ if (Math.Abs (px - cx) < distance) {
+ distance = Math.Abs (px - cx);
+ closest_x = point.Key;
+ }
+ }
+
+ switch (CurveType) {
+ case CurveType.Spline:
+ case CurveType.Linear:
+ if (grab_point == null) { //No grabbed point
+ if (distance <= min_distance)
+ CursorType = CursorType.Fleur;
+ else
+ CursorType = CursorType.Tcross;
+ return true;
+ }
+
+ CursorType = CursorType.Tcross;
+ points.Remove (grab_point.Value);
+ AddPoint ((closest_x = Unproject (px, MinX, MaxX, width)), MaxY - Unproject (py, MinY, MaxY, height));
+ QueueDraw ();
+ grab_point = closest_x;
+
+ break;
+ case CurveType.Free:
+ throw new NotImplementedException ();
+ break;
+ }
+ return true;
+ }
+
+ Gdk.CursorType cursor_type = Gdk.CursorType.TopLeftArrow;
+ CursorType CursorType {
+ get { return cursor_type; }
+ set {
+ if (value == cursor_type)
+ return;
+ cursor_type = value;
+ GdkWindow.Cursor = new Cursor (CursorType);
+ }
+ }
+#endregion
+ }
+}
diff --git a/src/Widgets/CurveType.cs b/src/Widgets/CurveType.cs
new file mode 100644
index 0000000..046d72f
--- /dev/null
+++ b/src/Widgets/CurveType.cs
@@ -0,0 +1,23 @@
+//
+// FSpot.Widgets.CurveType.cs
+//
+// Author(s):
+// Stephane Delcroix <stephane delcroix org>
+//
+// Copyright (c) 2009 Novell, Inc.
+//
+// This is open source software. See COPYING for details.
+//
+// Ported from Gtk+, where this widget and is no longer really supported.
+//
+
+namespace FSpot.Widgets
+{
+ public enum CurveType {
+ Linear,
+ Spline,
+ Free,
+ }
+}
+
+
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]