雖說是任意階數,可是嘞,算法原理是能夠到任意階數,計算機大概到100多階就會溢出了
c++
Bezier曲線介紹】git
【本文代碼】github
在windows的OpenGL
環境中,使用鼠標在屏幕上選點,並以點爲基礎畫出Bezier曲線算法
建立窗口,初始化大小、顯示模式、添加顯示和鼠標等回調函數,設置背景顏色等。windows
完成以後,定義兩個全局的int
類型的vector
用於存儲鼠標在窗口中選擇的點。同時定義窗口的高度和寬度。函數
vector<int> x_loc = {}; vector<int> y_loc = {}; int height = 600; int width = 600;
void drawPixel(double x, double y, int point_size) { glViewport(0, 0, (GLsizei)width, (GLsizei)height); glEnable(GL_POINT_SMOOTH); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glPointSize(point_size); glBegin(GL_POINTS); glVertex2d(x, y); glEnd(); }
其中point_size
爲點的大小。指針
OpenGL
中存在鼠標點擊、拖動等操做的回調函數,使用十分方便,調用便可。code
咱們定義在鼠標左鍵
按下擡起後爲一次屏幕選點,並將所選的點的座標壓入存儲存儲點的座標的容器中。blog
void Mouse_hit(int button, int state, int x, int y) { /// state == 1 mean button up /// state == 0 mean button down /// button == 0 mean left button /// button == 1 mean middle button /// button == 2 mean right button /// [x, y] is the location of mouse pointer if (button == 0 && state == 1) { x_loc.push_back(x); y_loc.push_back(y); cout << "point location: " << x_loc[x_loc.size() - 1] << " " << y_loc[y_loc.size() - 1] << endl; } if (button == 2 && state == 1){ x_loc.clear(); y_loc.clear(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glutSwapBuffers(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glutSwapBuffers(); glutPostRedisplay(); cout<<" Clear Screen!"<<endl; } glutSwapBuffers(); glutPostRedisplay(); }
回調函數使用爲get
glutMouseFunc(Mouse_hit);
Mouse_hit
函數中state表明當前鼠標的狀態是按下仍是擡起
button爲按下的是左、中、右三鍵中的哪個
[x, y]爲當前鼠標指針的座標。次座標不是世界座標系,使用時得進行轉換,看後面
拿一張圖簡單說明一下。因爲鼠標獲取的是世界座標系下的位置,而在屏幕上繪製點與線是使用的是當前繪圖座標系,因此要進行簡單的座標變換。
可在顯示回調函數中使用以下代碼重設OpenGL窗口。
glViewport(0, 0, (GLsizei)width, (GLsizei)height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0, width, height, 0);
好了,設置一下前景色和點的大小形狀等,來看看畫點的效果。
對於3階之內的Bezier曲線,直接將Bezier曲線的定義公式展開,求解係數便可。
void drawBezier_1(vector<int> x, vector<int> y, int num_of_points) { float ax, bx; float ay, by; int temp_loc = x.size() - 2; glColor3f(0.0f, 0.0f, 1.0f); drawPixel(x[temp_loc + 0], y[temp_loc + 0], 7); drawPixel(x[temp_loc + 1], y[temp_loc + 1], 7); ax = x[temp_loc + 0]; ay = y[temp_loc + 0]; bx = x[temp_loc + 1]; by = y[temp_loc + 1]; float t; t = 0.0; float dt = 0.002; while (t <= 1) { float x_temp = (1 - t) * ax + t * bx; float y_temp = (1 - t) * ay + t * by; drawPixel(x_temp, y_temp, 1); t += dt; } } void drawBezier_2(vector<int> x, vector<int> y, int num_of_points) { float ax, bx; float ay, by; float tSquared; int temp_loc = x.size() - 3; ax = x[temp_loc + 0] - 2 * x[temp_loc + 1] + x[temp_loc + 2]; ay = y[temp_loc + 0] - 2 * y[temp_loc + 1] + y[temp_loc + 2]; bx = x[temp_loc + 0] * (-2) + x[temp_loc + 1] * 2; by = y[temp_loc + 0] * (-2) + y[temp_loc + 1] * 2; glColor3f(0.0f, 0.0f, 1.0f); drawPixel(x[temp_loc + 0], y[temp_loc + 0], 7); drawPixel(x[temp_loc + 1], y[temp_loc + 1], 7); drawPixel(x[temp_loc + 2], y[temp_loc + 2], 7); float t; t = 0.0; float dt = 0.002; while (t <= 1) { tSquared = t * t; float x_temp = ax * tSquared + bx * t + x[temp_loc + 0]; float y_temp = ay * tSquared + by * t + y[temp_loc + 0]; drawPixel(x_temp, y_temp, 1); t += dt; } } void drawBezier_3(vector<int> x, vector<int> y, int num_of_points) { float ax, bx, cx; float ay, by, cy; float tSquared, tCubed; int temp_loc = x.size() - 4; cx = 3.0 * (x[temp_loc + 1] - x[temp_loc + 0]); bx = 3.0 * (x[temp_loc + 2] - x[temp_loc + 1]) - cx; ax = x[temp_loc + 3] - x[temp_loc + 0] - cx - bx; cy = 3.0 * (y[temp_loc + 1] - y[temp_loc + 0]); by = 3.0 * (y[temp_loc + 2] - y[temp_loc + 1]) - cy; ay = y[temp_loc + 3] - y[temp_loc + 0] - cy - by; glColor3f(0.0f, 0.0f, 1.0f); drawPixel(x[temp_loc + 0], y[temp_loc + 0], 7); drawPixel(x[temp_loc + 1], y[temp_loc + 1], 7); drawPixel(x[temp_loc + 2], y[temp_loc + 2], 7); drawPixel(x[temp_loc + 3], y[temp_loc + 3], 7); float t; t = 0.0; float dt = 0.002; while (t <= 1) { tSquared = t * t; tCubed = tSquared * t; float x_temp = (ax * tCubed) + (bx * tSquared) + (cx * t) + x[temp_loc + 0]; float y_temp = (ay * tCubed) + (by * tSquared) + (cy * t) + y[temp_loc + 0]; drawPixel(x_temp, y_temp, 1); t += dt; } }
階數+1
個點畫一次曲線。由Bezier的定義公式咱們能夠發現,畫Bezier曲線須要求組合數 ,求組合數須要求階乘,而後還須要求冪。由於c++
中有求冪的函數,因此實現階乘和組合數便可。
階乘
double fac(int n) { double result = 1; if (n == 0) return result; for (int i = 1; i <= n; i++){ result *= i; } return result; }
爲了擴大計算範圍,使用了double
類型
組合數
double combinate(int n, int k) { if (k == 0) return 1; double result = 0; result = fac(n) / (fac(k)*(fac(n - k))); return result; }
爲了擴大計算範圍,也使用了double
類型,其中k <= n
n階Bezier曲線
void drawBezier(vector<int> x, vector<int> y, int num_of_points) { float px = 0.0, py = 0.0; //point current should draw int n; //number of points -1 float t = 0.0, dt = 0.0005; //t in [0, 1], dt is changes each time in t n = x.size() - 1; while (t <= 1) { for (int i = 0; i <= n; i++) { double temp = combinate(n, i)*powf(t, i)*powf(1 - t, n - i); px += temp * x[i]; py += temp * y[i]; } drawPixel(px, py, 1); t += dt; px = 0.0; py = 0.0; } }
1階
2階
3階
n階曲線畫的❤
簡單而粗暴。。。