de Castel jau的递推算法
(1)原理
(2)这么说很难看懂,举个例子结合理解:
首先画出控制点:
然后取两条曲线的1/3处的点并连线:
p01 中1表示第一层,也就是第一次取等比例的点,0表示这一层的第0个点,其他同理
再取连线上的1/3处的点:
显然p02 已经是最后一个可以取等比例处的曲线的点了,所以曲线上t=1/3处的点就是p02 ,同理可以求t=1/2、1/4.。。。处的点进而绘制出一条抛物线:
(3)根据上面的例子推导计算公式:
这样t=1/3处的点坐标就可以求出来:
把1式2式代入3式即可得出:
进而我们可以推出:
k=0的时候也就是控制点
这就是著名的de Castel jau的递推算法,很便于求Bezier曲线上的一个点P(t);,这个算法稳定可靠,编程简单,是计算Bezier曲线的基本算法和标准算法。
下面是编程实现
效果:
一定要把递推式改成坐标分量式,也就是把P拆成x,y,z分别乘进去。
先给出这个算法的灵魂,也就是递推的实现:
//用来获得参数t处对应曲线上点的坐标
void GetPointPr(GLfloat t,Pt3D *Pt,GLint ControlN,Pt3D *ControlP)
{//t是参数值,Pt是存某点坐标,ControlN是控制点个数,ControlP中存有控制点坐标
GLint i,k,n=ControlN;
Pt3D Pki[4][4];//存递推的各项的值
for(i=0;i<n;i++){//K=0的情况,也就是控制点
Pki[0][i].x=ControlP[i].x;
Pki[0][i].y=ControlP[i].y;
}
//开始递推
for(k=1;k<n;k++){
for(i=0;i<n-k;i++){
Pki[k][i].x=(1-t)*Pki[k-1][i].x+t*Pki[k-1][i+1].x;
Pki[k][i].y=(1-t)*Pki[k-1][i].y+t*Pki[k-1][i+1].y;
}
}
//对应曲线上的点的坐标就是k=n-1时的坐标
Pt->x=Pki[n-1][0].x;
Pt->y=Pki[n-1][0].y;
}
完整代码:
#include <GL/freeglut.h>
#include <math.h>
class Pt3D{
public:
GLfloat x,y,z;
};//坐标类
void myinit(void)
{
glClearColor(1.0f,1.0f,1.0f,1.0f);
}
void myReshape(GLsizei w, GLsizei h)
{
//设定视区
glViewport(0, 0, w, h);
//设定透视方式
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(-100.0,100.0,-100.0,100.0);
}
//计算曲线上某点的坐标
void GetPointPr(GLfloat t,Pt3D *Pt,GLint ControlN,Pt3D *ControlP)
{//t是参数值,Pt是存某点坐标,ControlN是控制点个数,ControlP中存有控制点坐标
GLint i,k,n=ControlN;
Pt3D Pki[4][4];//存递推的各项的值
for(i=0;i<n;i++){//K=0的情况,也就是控制点
Pki[0][i].x=ControlP[i].x;
Pki[0][i].y=ControlP[i].y;
}
//开始递推
for(k=1;k<n;k++){
for(i=0;i<n-k;i++){
Pki[k][i].x=(1-t)*Pki[k-1][i].x+t*Pki[k-1][i+1].x;
Pki[k][i].y=(1-t)*Pki[k-1][i].y+t*Pki[k-1][i+1].y;
}
}
Pt->x=Pki[n-1][0].x;
Pt->y=Pki[n-1][0].y;
}
//根据控制点,画曲线上的500个点
void Bezierdraw(GLint m,GLint ControlN,Pt3D *ControlP)
{
Pt3D CurvePt;//保存当前点坐标
glColor3f(1.0,0.0,0.0);
glPointSize(3);//曲线画粗点和控制图形区分
glBegin(GL_POINTS);
for(int i=0;i<=m;i++){
GetPointPr((GLfloat)i/m,&CurvePt,ControlN,ControlP);
glVertex2f(CurvePt.x,CurvePt.y);
}
glEnd();
}
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT);
GLint ControlN=4;//4个控制点
GLint m=500;//曲线由500个点绘制而成
Pt3D ControlP[4]={{-80.0,-40.0,0.0},{-10.0,90.0,0.0},{10.0,-90.0,0.0},{80.0,40.0,0.0}};//控制点坐标
//画曲线
Bezierdraw(m,ControlN,ControlP);
//画控制图形
glColor3f(0.0,0.0,0.0);
glBegin(GL_LINE_STRIP);
for(int i=0;i<ControlN;i++){
glVertex3f(ControlP[i].x,ControlP[i].y,ControlP[i].z);
}
glEnd();
//交换前后缓冲区
glutSwapBuffers();
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
//初始化OPENGL显示方式
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGBA);
//设定OPENGL窗口位置和大小
glutInitWindowSize (500, 500);
glutInitWindowPosition (100, 100);
//打开窗口
glutCreateWindow ("递推算法");
//调用初始化函数
myinit();
//设定窗口大小变化的回调函数
glutReshapeFunc(myReshape);
//开始OPENGL的循环
glutDisplayFunc(display);
glutMainLoop();
return 0;
}