Bézier Curve

在产品零件设计中,许多*曲面是通过*曲线来构造的。对于*曲线的设计,设计人员经常需要大致勾画出曲线的形状,用户希望有一种方法能不再采用一般的代数描述,而采用直观的具有明显几何意义的操作,使得设计的曲线能够逼近曲线的形状。采用插值方法,用户设计的曲线形状不但受曲线上型值点的约束,而且受到边界条件影响,用户不能灵活调整曲线形状。但在产品设计中,曲线的设计是经过多次修改和调整来完成的,已有的方法完成这样的功能并不容易。Bézier方法的出现改善了上述设计方法的不足,使用户能方便地实现曲线形状的修改。

一、Bézier曲线定义

1. 定义:用基函数表示的Bézier曲线为

Bézier Curve Bézier Curve

Bernstein基函数:

Bézier Curve

N=3时,即特征多边形有四个顶点,由上式可得到三次Bernstein基函数:

Bézier Curve

因此根据定义可把三次Bézier曲线表示为:

Bézier Curve

也可根据上式写成矩阵形式,有点类似二次型:

Bézier Curve

由上可知,只要给定特征多边形的四个顶点矢量P0P1P2P3,并得用上式即可构造一条三次Bézier曲线。只要改变t值即可计算曲线上的点。对于曲线设计来说,采用这种特征多边形顶点的方法比较方便、直观。

二、编程实现

利用OpenGL的glut库根据Bézier曲线定义来实现曲线的绘制,源程序如下:

// Bezier Curve Program

#include <gl\glut.h>

#define POINTS_NUM	100

// point structure
typedef struct SPoint{
	float	x;
	float	y;
}Point;

Point	ctrlPoint[4];

void	Initialize(void);
void	DrawScene(void);
void	myReshape(GLsizei w, GLsizei h);
void	PlotBezierCurveDirectly(void);
Point	PointOnBezierCurve(int i, int k, float t);
void	PlotBezierCurve(void);

void main(int argc, char* argv[]) {
	glutInit(&argc, argv);				// Initialize GLUT
	glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);	// Set display mode
	glutInitWindowPosition(50,100);			// Set top-left display window position
	glutInitWindowSize(400, 300);			// set display window width and height
	glutCreateWindow("Bézier Curve");		 // Create display window

	Initialize();					// Execute initialization procedure
	glutDisplayFunc(DrawScene);			// Send graphics to display window
	glutReshapeFunc(myReshape);			// 

	glutMainLoop();					// Display everything and wait
}

/*
*/
void	Initialize(void) {
	//glClearColor(1.0, 1.0, 1.0, 0.0);		// Set Display-window color to white
	glMatrixMode(GL_PROJECTION);			// Set projection parameters
	glLoadIdentity();
	gluOrtho2D(0.0, 200, 0.0, 150);			// 

} // Initialize

/*
*/
void	myReshape(GLsizei w, GLsizei h) {
	// Reset viewport and projection parameter
	glViewport(0, 0, w, h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D(0, w, 0, h);
	glMatrixMode(GL_MODELVIEW);
} // myReshape

/*
*/
void	DrawScene(void) {
	glClear(GL_COLOR_BUFFER_BIT);			// Clear display window

	PlotBezierCurveDirectly();

	PlotBezierCurve();

	glFlush();					// Process all OpenGL routines as quickly possible
} // DrawScene


/*
	plot the Bezier Curve according to Definition.
*/
void	PlotBezierCurveDirectly(void) {
	// Basis functions
	#define B0(t)	((1.0 - t) * (1.0 - t) * (1.0 - t))
	#define B1(t)	(3.0 * t * (1.0 - t) * (1.0 - t))
	#define B2(t)	(3.0 * t * t * (1.0 - t))
	#define B3(t)	(t * t * t)

	// initialize control points
	Point	controlPoint[4];
	Point	plotPoint[POINTS_NUM];

	controlPoint[0].x	=	100.0;
	controlPoint[0].y	=	100.0;
	controlPoint[1].x	=	300.0;
	controlPoint[1].y	=	200.0;
	controlPoint[2].x	=	400.0;
	controlPoint[2].y	=	200.0;
	controlPoint[3].x	=	600.0;
	controlPoint[3].y	=	100.0;

	// calculate the points on the Bezier curve
	glColor3f(1.0, 0.0, 0.0f);			// set line segment geometry color to red
	glBegin(GL_LINE_STRIP);
	for (int i = 0; i < POINTS_NUM; ++i) {
		float	t	=	1.0 * i / POINTS_NUM;

		plotPoint[i].x	=	(B0(t) * controlPoint[0].x) +
					(B1(t) * controlPoint[1].x) +
					(B2(t) * controlPoint[2].x) +
					(B3(t) * controlPoint[3].x);

		plotPoint[i].y	=	(B0(t) * controlPoint[0].y) +
					(B1(t) * controlPoint[1].y) +
					(B2(t) * controlPoint[2].y) +
					(B3(t) * controlPoint[3].y);

		glVertex2f(plotPoint[i].x, plotPoint[i].y);
	}
	glEnd();

	// draw control points
	glColor3f(1.0, 1.0, 0.0f);
	glPointSize(6.0);
	glBegin(GL_POINTS);
		glVertex2f(controlPoint[0].x, controlPoint[0].y);
		glVertex2f(controlPoint[1].x, controlPoint[1].y);
		glVertex2f(controlPoint[2].x, controlPoint[2].y);
		glVertex2f(controlPoint[3].x, controlPoint[3].y);
	glEnd();

	// draw control polygon
	glColor3f(1.0, 0.0, 1.0f);
	glBegin(GL_LINE_STRIP);
		glVertex2f(controlPoint[0].x, controlPoint[0].y);
		glVertex2f(controlPoint[1].x, controlPoint[1].y);
		glVertex2f(controlPoint[2].x, controlPoint[2].y);
		glVertex2f(controlPoint[3].x, controlPoint[3].y);
	glEnd();

} // PlotBezierCurveDirectly

/*
*/
Point	PointOnBezierCurve(int i, int k, float t) {
	if (k == 0) {
		return ctrlPoint[i];
	}
	else {
		Point	point;
		Point	iPrev	=	PointOnBezierCurve(i, k-1, t);
		Point	iNext	=	PointOnBezierCurve(i+1, k-1, t);

		point.x	=	(1.0 - t) * iPrev.x + t * iNext.x;
		point.y	=	(1.0 - t) * iPrev.y + t * iNext.y;

		return point;
	}
} // PointOnBezierCurve

/*
	Use recursive algorithm to plot Bezier Curve.
*/
void	PlotBezierCurve(void) {

	// initialize control points
	ctrlPoint[0].x	=	100.0;
	ctrlPoint[0].y	=	200.0;
	ctrlPoint[1].x	=	300.0;
	ctrlPoint[1].y	=	300.0;
	ctrlPoint[2].x	=	400.0;
	ctrlPoint[2].y	=	500.0;
	ctrlPoint[3].x	=	600.0;
	ctrlPoint[3].y	=	300.0;

	Point	plotPoint[POINTS_NUM];

	// calculate the points on the Bezier curve
	glColor3f(1.0, 0.0, 0.0f);			// set line segment geometry color to red
	glBegin(GL_LINE_STRIP);
	for (int i = 0; i < POINTS_NUM; ++i) {
		float	t	=	1.0 * i / POINTS_NUM;

		plotPoint[i]	=	PointOnBezierCurve(0,3,t);
		
		glVertex2f(plotPoint[i].x, plotPoint[i].y);
	}
	glEnd();

	// draw control points
	glColor3f(1.0, 1.0, 1.0f);
	glPointSize(6.0);
	glBegin(GL_POINTS);
		glVertex2f(ctrlPoint[0].x, ctrlPoint[0].y);
		glVertex2f(ctrlPoint[1].x, ctrlPoint[1].y);
		glVertex2f(ctrlPoint[2].x, ctrlPoint[2].y);
		glVertex2f(ctrlPoint[3].x, ctrlPoint[3].y);
	glEnd();

	// draw control polygon
	glColor3f(0.0, 0.0, 1.0f);
	glBegin(GL_LINE_STRIP);
		glVertex2f(ctrlPoint[0].x, ctrlPoint[0].y);
		glVertex2f(ctrlPoint[1].x, ctrlPoint[1].y);
		glVertex2f(ctrlPoint[2].x, ctrlPoint[2].y);
		glVertex2f(ctrlPoint[3].x, ctrlPoint[3].y);
	glEnd();

} // PlotBezierCurve

程序运行结果如图1所示:

B&eacute;zier Curve

图1 根据定义实现的Bézier曲线

可修改特征多边形顶点变量controlPoint来看看相应的Bézier曲线的变化。

三、Bézier曲线的递推算法

计算Bézier曲线上的点,可用Bézier曲线方程直接计算,但使用de Casteljau提出的递推算法则要简单得多。

Bézier曲线的Bernstein基函数的递推性质

B&eacute;zier Curve

由于组合数的递推性:

B&eacute;zier Curve

因此有

B&eacute;zier Curve

为了保证公式可计算,我们约定:在以上公式中,凡当指标超出范围,以至记号不具有意义时,都应理解为零,如:

B&eacute;zier Curve

根据Bézier曲线定义可知:

B&eacute;zier Curve

这说明一条n次Bézier曲线可表示为分别由前后n个控制顶点决定的两条n-1次Bézier曲线的线性组合。由此可得Bézier曲线上某一点递归求值的de Casteljau算法:

B&eacute;zier Curve

其中:

——B&eacute;zier Curve, 是定义Bézier曲线的控制点;

——B&eacute;zier Curve,即是曲线P(t)上具有参数t的点;

用这一递推公式在给定参数下,求Bézier曲线上一点P(t)非常有效。de Casteljau算法稳定可靠,直观简便,可以编写出十分简捷的程序,是计算Bézier曲线的基本算法和标准算法。

四、de Casteljau算法的程序实现

根据递推公式可写出Bézier曲线的递归算法,如下:

/*
*/
Point    PointOnBezierCurve(int i, int k, float t) {
    if (k == 0) {
        return ctrlPoint[i];
    }
    else {
        Point    point;
        Point    iPrev    =    PointOnBezierCurve(i, k-1, t);
        Point    iNext    =    PointOnBezierCurve(i+1, k-1, t);

        point.x    =    (1.0 - t) * iPrev.x + t * iNext.x;
        point.y    =    (1.0 - t) * iPrev.y + t * iNext.y;

        return point;
    }
} // PointOnBezierCurve
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

其中,为了保存数据的方便,控制顶点数组变量ctrlPoint为全局变量。

根据递推算法实现Bézier曲线与根据定义实现的程序效果如下图2所示:

B&eacute;zier Curve

图2 图中上面部分为用递推算法实现,下面部分为用定义实现。

源程序如下:

// Bezier Curve Program

// File : BezierCurve.cpp

#include <gl\glut.h>

#define POINTS_NUM 100

// point structure

typedef struct SPoint{

float x;

float y;

}Point;

Point ctrlPoint[4]; // control point for recursive algorithm Bezier Curve

void Initialize(void);

void DrawScene(void);

void myReshape(GLsizei w, GLsizei h);

void PlotBezierCurveDirectly(void);

Point PointOnBezierCurve(int i, int k, float t);

void PlotBezierCurve(void);

void main(int argc, char* argv[]) {

glutInit(&argc, argv); // Initialize GLUT

glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB); // Set display mode

glutInitWindowPosition(50,100); // Set top-left display window position

glutInitWindowSize(400, 300); // set display window width and height

glutCreateWindow("Bézier Curve"); // Create display window

Initialize(); // Execute initialization procedure

glutDisplayFunc(DrawScene); // Send graphics to display window

glutReshapeFunc(myReshape); //

glutMainLoop(); // Display everything and wait

}

/*

*/

void Initialize(void) {

//glClearColor(1.0, 1.0, 1.0, 0.0); // Set Display-window color to white

glMatrixMode(GL_PROJECTION); // Set projection parameters

glLoadIdentity();

gluOrtho2D(0.0, 200, 0.0, 150); //

} // Initialize

/*

*/

void myReshape(GLsizei w, GLsizei h) {

// Reset viewport and projection parameter

glViewport(0, 0, w, h);

glMatrixMode(GL_PROJECTION);

glLoadIdentity();

gluOrtho2D(0, w, 0, h);

glMatrixMode(GL_MODELVIEW);

} // myReshape

/*

*/

void DrawScene(void) {

glClear(GL_COLOR_BUFFER_BIT); // Clear display window

PlotBezierCurveDirectly();

PlotBezierCurve();

glFlush(); // Process all OpenGL routines as quickly possible

} // DrawScene

/*

plot the Bezier Curve according to Definition.

*/

void PlotBezierCurveDirectly(void) {

// Basis functions

#define B0(t) ((1.0 - t) * (1.0 - t) * (1.0 - t))

#define B1(t) (3.0 * t * (1.0 - t) * (1.0 - t))

#define B2(t) (3.0 * t * t * (1.0 - t))

#define B3(t) (t * t * t)

// initialize control points

Point controlPoint[4];

Point plotPoint[POINTS_NUM];

controlPoint[0].x = 100.0;

controlPoint[0].y = 100.0;

controlPoint[1].x = 300.0;

controlPoint[1].y = 200.0;

controlPoint[2].x = 400.0;

controlPoint[2].y = 200.0;

controlPoint[3].x = 600.0;

controlPoint[3].y = 100.0;

// calculate the points on the Bezier curve

glColor3f(1.0, 0.0, 0.0f); // set line segment geometry color to red

glBegin(GL_LINE_STRIP);

for (int i = 0; i < POINTS_NUM; ++i) {

float t = 1.0 * i / POINTS_NUM;

plotPoint[i].x = (B0(t) * controlPoint[0].x) +

(B1(t) * controlPoint[1].x) +

(B2(t) * controlPoint[2].x) +

(B3(t) * controlPoint[3].x);

plotPoint[i].y = (B0(t) * controlPoint[0].y) +

(B1(t) * controlPoint[1].y) +

(B2(t) * controlPoint[2].y) +

(B3(t) * controlPoint[3].y);

glVertex2f(plotPoint[i].x, plotPoint[i].y);

}

glEnd();

// draw control points

glColor3f(1.0, 1.0, 0.0f);

glPointSize(6.0);

glBegin(GL_POINTS);

glVertex2f(controlPoint[0].x, controlPoint[0].y);

glVertex2f(controlPoint[1].x, controlPoint[1].y);

glVertex2f(controlPoint[2].x, controlPoint[2].y);

glVertex2f(controlPoint[3].x, controlPoint[3].y);

glEnd();

// draw control polygon

glColor3f(1.0, 0.0, 1.0f);

glBegin(GL_LINE_STRIP);

glVertex2f(controlPoint[0].x, controlPoint[0].y);

glVertex2f(controlPoint[1].x, controlPoint[1].y);

glVertex2f(controlPoint[2].x, controlPoint[2].y);

glVertex2f(controlPoint[3].x, controlPoint[3].y);

glEnd();

} // PlotBezierCurveDirectly

/*

*/

Point PointOnBezierCurve(int i, int k, float t) {

if (k == 0) {

return ctrlPoint[i];

}

else {

Point point;

Point iPrev = PointOnBezierCurve(i, k-1, t);

Point iNext = PointOnBezierCurve(i+1, k-1, t);

point.x = (1.0 - t) * iPrev.x + t * iNext.x;

point.y = (1.0 - t) * iPrev.y + t * iNext.y;

return point;

}

} // PointOnBezierCurve

/*

Use recursive algorithm to plot Bezier Curve.

*/

void PlotBezierCurve(void) {

// initialize control points

ctrlPoint[0].x = 100.0;

ctrlPoint[0].y = 300.0;

ctrlPoint[1].x = 300.0;

ctrlPoint[1].y = 400.0;

ctrlPoint[2].x = 400.0;

ctrlPoint[2].y = 400.0;

ctrlPoint[3].x = 600.0;

ctrlPoint[3].y = 300.0;

Point plotPoint[POINTS_NUM];

// calculate the points on the Bezier curve

glColor3f(1.0, 0.0, 0.0f); // set line segment geometry color to red

glBegin(GL_LINE_STRIP);

for (int i = 0; i < POINTS_NUM; ++i) {

float t = 1.0 * i / POINTS_NUM;

plotPoint[i] = PointOnBezierCurve(0,3,t);

glVertex2f(plotPoint[i].x, plotPoint[i].y);

}

glEnd();

// draw control points

glColor3f(1.0, 1.0, 1.0f);

glPointSize(6.0);

glBegin(GL_POINTS);

glVertex2f(ctrlPoint[0].x, ctrlPoint[0].y);

glVertex2f(ctrlPoint[1].x, ctrlPoint[1].y);

glVertex2f(ctrlPoint[2].x, ctrlPoint[2].y);

glVertex2f(ctrlPoint[3].x, ctrlPoint[3].y);

glEnd();

// draw control polygon

glColor3f(0.0, 0.0, 1.0f);

glBegin(GL_LINE_STRIP);

glVertex2f(ctrlPoint[0].x, ctrlPoint[0].y);

glVertex2f(ctrlPoint[1].x, ctrlPoint[1].y);

glVertex2f(ctrlPoint[2].x, ctrlPoint[2].y);

glVertex2f(ctrlPoint[3].x, ctrlPoint[3].y);

glEnd();

} // PlotBezierCurve

因此,根据递归算法绘制Bézier曲线的程序简单直观,便于把数学公式与程序对应。

B&eacute;zier Curve

图3 修改控制顶点后效果图

上一篇:重学计算机组成原理(十二)- 加法器


下一篇:*上人气最旺的10个Java问题