一段话总结
贝塞尔曲线 给出n+1个控制点,画出n阶曲线。一直按某个固定比例,在控制点的连线和其衍生连线上取点,直到最后一阶用该比例取到将绘制到曲线上的点。
de Casteljau算法作图的原理就是,在0~1范围内选取比例,每一比例都会产出不同的点。相邻的控制点连线,然后在连线的t比例位置取点再连线,在连线的t比例上取得绘制点,这个绘制点就是本曲线在t比例的点啦。不过二阶Bezier曲线(三个控制点的)的取点才结束的这么快。如果说是三阶,在连线上按照t比例取点,此时还会有另一条同级别的衍生连线,同样的按t比例取点,两点相连,此时没有其他同级别的衍生线,本级别只有一条了!可以在上面取点了,咋取?还是按t比例取。
更高阶,相邻控制点之间的连线,衍生连线,衍生连线又衍生出衍生连线的衍生连线,生就完事了,每相邻的两条生一条,每一阶段衍生出来的线,会比上一阶段的线少一条,最后只衍生出一条线的阶段,就是取点的阶段了。四点生三条,三条生两条,两条生一条,画!五点生四条,四条生三条,三条生…哦哟,递推计算公式就是这个道理吗?
用一下网上的图!非常感谢制作者啦!
画法
为了绘制一条线,我们要得到线上各点的坐标。
计算Bezier曲线每个点,需要的数据有:
1.比例t
2.Cxx多项式系数。项的数量取决于阶数,二阶有三项。 Cmn=n!/m!(n-m)!
3.BEN基函数{多项式系数Cin × ti× (1-t)n-i }
4.各控制点坐标()。
1和2是用在3里的,3.×4.,即控制点坐标×基函数,得一项,各项的数据都加上,就是把诸多后续衍生的连线计算进去,就是把每个控制点的影响算进去。得到本比例t的点P。
然后呢,只需要改变t,t分的越细,越微,则取得的点越多,线越光滑。两点之间当然是用直线拟合啦。
代码
计算部分的代码是这里的
图形算法:贝塞尔曲线
#include<gl/glut.h>
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
typedef struct {
float x, y, z;
}point3D;
typedef struct {
int length;
int * arr;
}intArray;
typedef struct {
int length;
point3D * arr;
}pointArray;
//该方法接受经过计算的贝塞尔点,并对相邻两个点进行连线处理
void drawLine(pointArray &bezPts);
//此方法用来计算二项式系数
//接受一个数组对象的引用
void binomialCoefficient(intArray &C) {
int j, n = C.length - 1;
for (int k = 0; k <= n; k++) {
C.arr[k] = 1; /* C上m下n=n!/m!(n-m)! */
for (j = n; j > k; j--) /* j!/k! */
C.arr[k] *= j;
for (j = n - k; j > 1; j--) /* (n-k)! */
C.arr[k] /= j;
}
}
//此方法计算在比例t时点的坐标位置
//接受的第一个参数为规律u,第二个参数为将要存放位置点的指针,第三个参数为所有控制点坐标数组,第四个参数为系数数组)
void computeBezPt(float t, point3D &bezPt, pointArray &ctrlPts, intArray &C) {
int n = ctrlPts.length - 1;
float bezFcn;//保存贝塞尔方程计算结果
bezPt.x = bezPt.y = bezPt.z = 0;//初始化
for (int k = 0; k <= n; k++) { /* Cxx 多项式系数 * t^i* (1-t)^( n-i) */
bezFcn = C.arr[k] * pow(t, k)*pow(1 - t, n - k);
bezPt.x += ctrlPts.arr[k].x*bezFcn;
bezPt.y += ctrlPts.arr[k].y*bezFcn;
bezPt.z += ctrlPts.arr[k].z*bezFcn;
}
}
//此方法为调用方法,用来产生贝塞尔曲线
//第一个参数用来接受控制点数组,第二个参数为精度,精度越高两个点之间的距离越小,但是需要的计算时间也就越长
void bezier(pointArray &ctrlPts, int precision) {
intArray C;//阶数+1
C.length = ctrlPts.length;
C.arr = new int[C.length];
binomialCoefficient(C);//计算系数
pointArray bezPts;//保存计算点的参数
bezPts.length = precision + 1;
bezPts.arr = new point3D[bezPts.length];
float t;//规律参数
for (int k = 0; k <= precision; k++) {
t = float(k) / float(precision);
computeBezPt(t, bezPts.arr[k], ctrlPts, C);//每次计算一个点。t从0-1,把所有占比用上,曲线就完成啦
}
drawLine(bezPts);
delete[] bezPts.arr;
delete[] C.arr;
}
void drawLine(pointArray &bezPts) {
glBegin(GL_LINE_STRIP);
for (int i = 0; i < bezPts.length; i++) {
glVertex3f(bezPts.arr[i].x, bezPts.arr[i].y, bezPts.arr[i].z);
}
glEnd();
}
/***************************************/
static pointArray ctrlPts_;
static bool readyToDraw = false;
bool bInput, accept, bDraw;
const int MAX_CPTS = 13;
GLfloat clickPts[MAX_CPTS][3];
int ncpts = 0;
static int width = 500, height = 500;
void PointGL(int i) {
glPointSize(2);
glBegin(GL_POINTS);
glColor3f(0.0f, 0.0f, 0.0f);
glVertex2f(clickPts[i][0], clickPts[i][1]);
glEnd();
}
void coolTransform() {
int length = ncpts;
point3D *ctrlPts;
ctrlPts = (point3D *)malloc(length * sizeof(point3D));
for (int i = 0; i < ncpts; i++) {
ctrlPts[i].x = clickPts[i][0];
ctrlPts[i].y = clickPts[i][1];
ctrlPts[i].z = clickPts[i][2];
}
ctrlPts_.arr = ctrlPts;
ctrlPts_.length = ncpts;
}
void mouse(int button, int state, int x, int y) {
float wx, wy;
if (button != GLUT_LEFT_BUTTON || state != GLUT_DOWN)
return;
wx = x;
wy = height-y;
if (ncpts == MAX_CPTS)
return;
clickPts[ncpts][0] = wx;
clickPts[ncpts][1] = wy;
clickPts[ncpts][2] = 0.0;
ncpts++;
glutPostRedisplay();
}
void keyboard(unsigned char key, int x, int y) {
switch (key) {
case 'q': case'Q':
exit(1);
break;
case 'c':case'C':
ncpts = 0;
readyToDraw = false;
glutPostRedisplay();
break;
case 'd':case'D':
readyToDraw = true;
coolTransform();
glutPostRedisplay();
break;
default:
break;
}
}
void displayFcn(void) {
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1, 0, 0);
if (readyToDraw)
bezier(ctrlPts_, 100);
if (ncpts > 0)
for (int i = 0; i < ncpts; i++) {
PointGL(i);
}
glFlush();
}
int main(int argc, char* argv[]) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowPosition(50, 50);
glutInitWindowSize(width, height);
glutCreateWindow("bezier curve_左键取点,按D绘制,按C清空,按Q退出");
glClearColor(1, 1, 1, 0);
glMatrixMode(GL_PROJECTION);
gluOrtho2D(0, width, 0, height);
glutDisplayFunc(displayFcn);
glutKeyboardFunc(keyboard);
glutMouseFunc(mouse);
glutMainLoop();
return 0;
}