OpenGL绘制Bezier曲线



项目要求:

– 使用鼠标在屏幕中任意设置控制点,并生成曲线

– 使用鼠标和键盘的交互操作实现对曲线的修改。

项目总体介绍

本项目利用Bezier曲线生成算法生成可由用户自定义的曲线。可实现核心功能如下:

  1. 用户用鼠标左击屏幕任意处产生记录点。

  2. 鼠标右击屏幕任意处由先前的任意个数记录点和其先后关系生成Bezier曲线。

另有辅助输入功能:

  1. 按键盘C键可清除所有记录点。

  2. 按键盘R键可清除上一个记录点。

  3. 按键盘Q键可推出程序。

    扫描二维码关注公众号,回复: 3803117 查看本文章

 

 

项目设计思路

1Bezier曲线介绍:

贝塞尔曲线就是这样的一条曲线,它是依据四个位置任意的点坐标绘制出的一条光滑曲线。在历史上,研究贝塞尔曲线的人最初是按照已知曲线参数方程来确定四个点的思路设计出这种矢量曲线绘制法。1962年,法国数学家Pierre Bézier第一个研究了这种矢量绘制曲线的方法,并给出了详细的计算公式,因此按照这样的公式绘制出来的曲线就用他的姓氏来命名是为贝塞尔曲线。

2、生成公式:

1线性公式(只有两个点情况)

给定点P0P1,线性贝兹曲线只是一条两点之间的直线。这条线由下式给出:

且其等同于线性插值。

2二次方公式(三个点组成)

二次方贝兹曲线的路径由给定点P0P1P2的函数Bt)追踪:

 

TrueType字型就运用了以贝兹样条组成的二次贝兹曲线。

3三次方公式(四个点)

P0P1P2P3四个点在平面或在三维空间中定义了三次方贝兹曲线。曲线起始于P0走向P1,并从P2的方向来到P3。一般不会经过P1P2;这两个点只是在那里提供方向资讯。P0P1之间的间距,决定了曲线在转而趋进P3之前,走向P2方向的“长度有多长”。

曲线的参数形式为:

现代的成象系统,如PostScriptAsymptoteMetafont,运用了以贝兹样条组成的三次贝兹曲线,用来描绘曲线轮廓。

 

 

4一般参数公式n个点)

阶贝兹曲线可如下推断。给定点P0P1、…、Pn,其贝兹曲线即:

 


N阶的贝兹曲线,即N-1阶贝兹曲线之间的插值。

#include<stdlib.h>
#include<stdio.h>
#include<math.h>
#include<GL/glut.h>
//定义控制点数目的最大值
#define MAX_CPTX 25  
int ncpts=0;//实际控制点个数
static int width=600,height=600;//窗口大小
typedef struct
{
	GLfloat x,y;
} POINT;
POINT cpts[MAX_CPTX];//存储控制点坐标
//求n!
int JieCheng(int n)
{
	if(n==1||n==0)
	{
		return 1;
	}
	else
	{
		return n*JieCheng(n-1);
	}
}
//求组合排列
double C(int n,int i)
{
	return ((double)JieCheng(n))/((double)(JieCheng(i)*JieCheng(n-i)));
}
//求一个数u的num次方
double N(double u,int n)
{
	double sum=1.0;
	if (n==0)
	{
		return 1;
	}
	for(int i=0;i<n;i++)
	{
		sum*=u;
	}
	return sum;
}

//绘制bezier曲线
void drawBezier(POINT *p)
{    
	void display();
	if(ncpts<=0) return;	

	POINT *p1;
	p1=new POINT[1000];
	GLfloat u=0,x,y;
	int i,num=1;
	p1[0]=p[0];
	for(u=0;u<=1;u=u+0.001)
	{   
		x=0;
		y=0;
		for(i=0;i<ncpts;i++)
		{	
	      x+=C(ncpts-1,i)*N(u,i)*N((1-u),(ncpts-1-i))*p[i].x;
	      y+=C(ncpts-1,i)*N(u,i)*N((1-u),(ncpts-1-i))*p[i].y;
		}
		p1[num].x=x;
		p1[num].y=y;
		num++;
	}	

	    glPointSize(4.0);
	    glColor3f(0.0,0.0,0.0);
	    glBegin(GL_LINE_STRIP);
	    for(int k=0;k<1000;k++)
		  glVertex2f(p1[k].x,p1[k].y);
	    glEnd();
	    glFlush();
		return;
}



//输入新的控制点
static void mouse(int button, int state,int x,int y)
{
void display();
float wx,wy;
//鼠标未按下左键,不做响应
if(state!=GLUT_DOWN)
   return;
else 
	{if(button==GLUT_LEFT_BUTTON)
	{
//转换坐标
wx=(2.0*x)/(float)(width-1)-1.0;
wy=(2.0*(height-1-y))/(float)(height-1)-1.0;
//判断控制点数目是否超过最大值
if(ncpts==MAX_CPTX)
	return;
//存储控制点
cpts[ncpts].x=wx;
cpts[ncpts].y=wy;
ncpts++;
//绘制控制点
glColor3f(0.0,0.0,0.0);
glPointSize(5.0);
glBegin(GL_POINTS);
glVertex2f(wx,wy);
glEnd();
glFlush();
}
if(button==GLUT_RIGHT_BUTTON)
{
display();
drawBezier(cpts);
}
}
}
void display(void)
{
	int i;
    glClear(GL_COLOR_BUFFER_BIT);
	glColor3f(0.0,0.0,0.0);
    glPointSize(5.0);
    glBegin(GL_POINTS);
    for (i = 0; i < ncpts; i++)
        glVertex2f(cpts[i].x,cpts[i].y);
    glEnd();
    glFlush();

}
//键盘回调函数
void keyboard(unsigned char key,int x,int y)
{
	switch (key)
    {
        case 'q': case 'Q':
            exit(0);
            break;
        case 'c': case 'C':
			ncpts = 0;
			glutPostRedisplay();
            break;
		case 'r': case 'R':
			ncpts--;
			glutPostRedisplay();
			break;
    }
}

//重绘函数
void reshape(int w,int h)
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-1.0,1.0,-1.0,1.0,-1.0,1.0);
glMatrixMode(GL_MODELVIEW);
glViewport(0,0,w,h);//调整视口
width=w;
height=h;
}
int main(int argc, char **argv)
{
//初始化
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_RGB);
glutInitWindowSize(width,height);
glutCreateWindow("zjc2012211763");
//注册回调函数
glutDisplayFunc(display);
glutMouseFunc(mouse);
glutKeyboardFunc(keyboard);
glutReshapeFunc(reshape);
glClearColor(1.0,1.0,1.0,1.0);
glColor3f(0.0,0.0,0.0);
glutMainLoop();
}






张竞成

www.zjc18.com

猜你喜欢

转载自blog.csdn.net/zjccoder/article/details/42807645