Simple Bezier Curve Implementation
/* GTK - GIMP工具包
* 版权 (C) 1995-1997 Peter Mattis, Spencer Kimball 和 Josh MacDonald 所有
*
* 本程序是自由软件。你可以在自由软件基金发布的 GNU GPL 的条款下重新分发
* 或修改它。GPL 可以使用版本 2 或(由你选择)任何随后的版本。
*
* 本程序分发的目的是它可能对其他人有用,但不提供任何的担保,包括隐含的
* 和适合特定用途的保证。请查阅GNU通用公共许可证获得详细的信息。
*
* 你应该已经随该软件一起收到一份GNU通用公共许可。如果还没有,请写信给
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <gtk/gtk.h>
#include <math.h>
static GdkPixmap *pixmap = NULL;
double ConstFactorial[33] =
{
1.0,
1.0,
2.0,
6.0,
24.0,
120.0,
720.0,
5040.0,
40320.0,
362880.0,
3628800.0,
39916800.0,
479001600.0,
6227020800.0,
87178291200.0,
1307674368000.0,
20922789888000.0,
355687428096000.0,
6402373705728000.0,
121645100408832000.0,
2432902008176640000.0,
51090942171709440000.0,
1124000727777607680000.0,
25852016738884976640000.0,
620448401733239439360000.0,
15511210043330985984000000.0,
403291461126605635584000000.0,
10888869450418352160768000000.0,
304888344611713860501504000000.0,
8841761993739701954543616000000.0,
265252859812191058636308480000000.0,
8222838654177922817725562880000000.0,
263130836933693530167218012160000000.0,
};
double Combinatorial(int n, int i)
{
if (n > 32 || i > 32 || n < i)
{
return 1.0; //error
}
return ConstFactorial[n]/ConstFactorial[i]/ConstFactorial[n-i];
}
double Bernstein(int n, int i, double t)
{
double ti; /* t^i */
double tni; /* (1 - t)^i */
/* Prevent problems with pow */
if (t == 0.0 && i == 0)
{
ti = 1.0;
}
else
{
ti = pow(t, i);
}
if (n == i && t == 1.0)
{
tni = 1.0;
}
else
{
tni = pow((1 - t), (n - i));
}
return Combinatorial(n, i) * ti * tni;
}
void BezierExponent(int ctrlPtrCount, GdkPoint* ctrlPointArray, double t, GdkPoint* plotPoint)
{
int i;
double x = 0.0, y = 0.0;
for ( i = 0; i < ctrlPtrCount; i ++)
{
double basis = Bernstein(ctrlPtrCount - 1, i, t);
x += basis * ctrlPointArray[i].x;
y += basis * ctrlPointArray[i].y;
}
plotPoint->x = x;
plotPoint->y = y;
}
void BezierRecursion(int ctrlPtrCount, GdkPoint* ctrlPointArray, double t, GdkPoint* plotPoint)
{
GdkPoint* pointArray = (GdkPoint*) malloc(sizeof(GdkPoint) * ctrlPtrCount);
int i, j;
if (pointArray)
{
for (i = 0; i < ctrlPtrCount; i ++)
{
pointArray[i] = ctrlPointArray[i];
}
for (j = 1; j < ctrlPtrCount; j ++)
{
for (i = 0; i < ctrlPtrCount - j; i ++)
{
pointArray[i].x = pointArray[i].x * (1 - t) + pointArray[i + 1].x * t;
pointArray[i].y = pointArray[i].y * (1 - t) + pointArray[i + 1].y * t;
}
}
plotPoint->x = pointArray[0].x;
plotPoint->y = pointArray[0].y;
free(pointArray);
}
}
static int bUseRecursion = FALSE;
void Bezier2D(int ctrlPtrCount, GdkPoint* ctrlPointArray, int plotPtrCount, GdkPoint* plotPointArray)
{
double step, t;
int counter, i;
if (ctrlPtrCount < 2 || plotPtrCount < 2)
{
return;
}
step = (double)1.0 / (plotPtrCount - 1);
for (counter = 0, t = 0.0; counter < plotPtrCount; counter ++, t += step)
{
if ((1.0 - t) < 0.0000001)
{
t = 1.0;
}
if (!bUseRecursion)
{
BezierExponent(ctrlPtrCount, ctrlPointArray, t, plotPointArray +
counter);
}
else
{
BezierRecursion(ctrlPtrCount, ctrlPointArray, t, plotPointArray +
counter);
}
}
}
static void drawBezier(GtkWidget* widget)
{
int i;
GdkPoint ControlPtrArray[] =
{
{100, 500},
{200, 100},
{300, 300},
{400, 450},
{500, 550},
{600, 250},
{700, 200},
{750, 550},
};
const int CtrlPtrCount = sizeof(ControlPtrArray)/sizeof(GdkPoint);
const int PlotPtrCount = 80;
GdkPoint plotPtrArray[PlotPtrCount];
Bezier2D(CtrlPtrCount, ControlPtrArray, PlotPtrCount, plotPtrArray);
gdk_draw_lines(pixmap, widget->style->black_gc, plotPtrArray, PlotPtrCount);
GdkGC* pointGC = gdk_gc_new(widget->window);
GdkColor pointColor;
if (bUseRecursion)
{
bUseRecursion = FALSE;
pointColor.red = 65535;
pointColor.green = 0;
pointColor.blue = 0;
}
else
{
bUseRecursion = TRUE;
pointColor.red = 0;
pointColor.green = 65535;
pointColor.blue = 0;
}
gdk_gc_set_rgb_fg_color(pointGC, &pointColor);
for ( i = 0; i < PlotPtrCount; i ++)
{
gdk_draw_rectangle(pixmap,
pointGC, TRUE, plotPtrArray[i].x - 5, plotPtrArray[i].y - 5, 10, 10);
}
GdkRectangle update_rect;
update_rect.x = 0;
update_rect.y = 0;
update_rect.width = 800;
update_rect.height = 600;
gtk_widget_queue_draw_area (widget,
update_rect.x,
update_rect.y,
update_rect.width,
update_rect.height);
}
static gint configure_event( GtkWidget *widget,
GdkEventConfigure
*event )
{
if (pixmap)
g_object_unref (pixmap);
pixmap = gdk_pixmap_new (widget->window,
widget->allocation.width,
widget->allocation.height,
-1);
gdk_draw_rectangle (pixmap,
widget->style->white_gc,
TRUE,
0,
0,
widget->allocation.width,
widget->allocation.height);
return TRUE;
}
static gint expose_event( GtkWidget *widget,
GdkEventExpose
*event )
{
gdk_draw_drawable (widget->window,
widget->style->fg_gc[GTK_WIDGET_STATE
(widget)],
pixmap,
event->area.x,
event->area.y,
event->area.x,
event->area.y,
event->area.width,
event->area.height);
return FALSE;
}
static void quit()
{
exit(0);
}
int main( int argc,
char *argv[] )
{
GtkWidget *window;
GtkWidget *drawing_area;
GtkWidget *vbox;
GtkWidget *drawButton;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_widget_set_name (window, "Test Input");
vbox = gtk_vbox_new (FALSE, 0);
gtk_container_add (GTK_CONTAINER (window), vbox);
gtk_widget_show (vbox);
g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (quit), NULL);
drawing_area = gtk_drawing_area_new ();
gtk_widget_set_size_request (GTK_WIDGET (drawing_area), 800, 600);
gtk_box_pack_start (GTK_BOX (vbox), drawing_area, TRUE, TRUE, 0);
gtk_widget_show (drawing_area);
g_signal_connect (G_OBJECT (drawing_area), "expose_event", G_CALLBACK (expose_event), NULL);
g_signal_connect (G_OBJECT (drawing_area),"configure_event", G_CALLBACK (configure_event), NULL);
drawButton = gtk_button_new_with_label("Draw");
gtk_box_pack_start (GTK_BOX (vbox), drawButton, FALSE, FALSE, 0);
g_signal_connect_swapped (G_OBJECT (drawButton), "clicked",
G_CALLBACK
(drawBezier),
drawing_area);
gtk_widget_show (drawButton);
gtk_widget_show (window);
gtk_main ();
return 0;
}