N階貝塞爾曲線繪製 C C

2021-08-31 19:11:34 字數 3670 閱讀 1870

貝塞爾是很經典的東西,輪子應該有很多的。求n階貝塞爾曲線用到了 德卡斯特里奧演算法(de casteljau』s algorithm)

要完成的功能是根據起點,終點和控制點,繪製n階貝塞爾曲線

首先看n階貝塞爾曲線的公式

公式中用了組合數,大數組合數計算也有演算法:

簡言之就是把  大數乘以大數除以大數  這個過程 轉為單純的累加。

下面來說明一下這個組合數計算的優化過程:

100000 / 100 =  1000

500 + 500 = 1000

上面兩個式子計算結果是相等的,但是如果程式設計實現,

第乙個式子就必須使用至少乙個uint32 來存放100000;

但是第二個式子只需要uint16型別就可以完成整個計算。

通過變換計算方式,可以通過計算機有限的資料大小計算盡可能大的結果。 

貝塞爾曲線也是一種插值演算法, 根據起點和終點,通過中間的控制點,插值計算出整條路徑

現代的x86,硬體計算浮點都是先轉換為double的,所以用double會比float會更快,當然這裡只針對英特爾家族複雜指令集產品。

為什麼用float不用double是因為pointf是c#自帶的結構體,要改為double也是很簡單的,全域性替換一下資料型別即可

c#陣列自帶length屬性,但是為了方便移植到c,這裡還是使用陣列+陣列長度作為入參,這樣可以很容易的改寫為c下面的陣列指標+陣列長度。

直接實現數學公式是比較簡單的,直接貼**:

public static class bezier

while (t <= 1 && count > 1);    // 乙個點的情況直接跳出.

return bezier_curves_points.toarray();  // 曲線軌跡上的所有座標點

}/// /// n階貝塞爾曲線插值計算函式

/// 根據起點,n個控制點,終點 計算貝塞爾曲線插值

///

/// 當前插值位置0~1 ,0為起點,1為終點

/// 起點,n-1個控制點,終點

/// n+1個點

///

private static pointf bezier_interpolation_func(float t, pointf points, int count)

pointf.x = sum_x;

pointf.y = sum_y;

return pointf;

}/// /// 計算組合數公式

///

///

///

///

private static ulong calc_combination_number(int n, int k)

return result[k];

}}

使用方法:

// 第乙個是起點,最後乙個是終點,中間的都是控制點,貝賽爾曲線階數 = 總點數-1

pointf pointlist = new pointf ;  

pointf aa = bezier.draw_bezier_curves(pointlist, pointlist.length, 0.001f); // 在起點和終點之間畫1/0.001=1000個點

foreach (var item in aa)

可以很容易的改寫成c/c++版,

pointf只是個結構體,;

c/c++中陣列部分不需要new

math.pow()對應於c語言math.h標頭檔案裡的 pow()

list在c++中可以通過vector實現

在c中可以換成malloc分配個陣列,大小推薦使用 (1/step) + 1。

目前為止,只是從數學公式上實現了程式,這個程式有什麼問題呢?

下面放兩張圖,分別是通過上面的**計算出來的 4階和 某n(n很大)階的巴賽爾曲線,

可以看到階數過多的時候計算出錯了,原因就在於計算組合數函式那裡,當階數過多的時候,組合數中階乘的計算結果溢位了。

其實仔細觀察會發現階乘計算結果在bezier_interpolation_func函式中又乘以 了乙個小數,

這裡就是可以改進的地方,階乘的計算結果既然只是中間值,那麼就可以通過某些演算法來除掉這個中間值或者通過轉化為累加的方式來解決,

就像文章開篇把組合數的 計算公式 n!/(k!*(n-k)!) 簡化為 (n-1)!/(k!*(n-k-1)!) + (n-1)!/((k-1)!*(n-k+1)!)  的遞迴加法一樣,bezier_interpolation_func函式也可以通過遞迴的方式來優化。

使它能夠計算更高的階數而不會溢位(如果有足夠的記憶體空間..)。

下面來看乙個改進版的程式

public static pointf bezier_interpolation_func(float t, pointf points, int count)

return bezier_interpolation_func(t, tmp_points, count - 1);

}}

可以看到將bezier_interpolation_func修改為遞迴的形式,同時去掉了求組合數的過程。

看一下結果

可以,實現了功能。

然後將遞迴改為迴圈,迴圈的方式有更好的相容性和效率,特別是某些低端的嵌入式編譯器不支援遞迴方式。

/// 參考: 

public static pointf bezier_interpolation_func(float t, pointf points, int count)

tmp_points[j].x = (float)(tmp_points[j].x * (1 - t) + tmp_points[j + 1].x * t);

tmp_points[j].y = (float)(tmp_points[j].y * (1 - t) + tmp_points[j + 1].y * t);}}

return tmp_points[0];

}

下面是c語言的乙個完整程式,不過是列印曲線點的座標,不太直觀。

#include "stdio.h"

#include "math.h"

#include "assert.h"

typedef struct

pointf;

pointf bezier_interpolation_func(float t, pointf* points, int count)

tmp_points[j].x = (float)(tmp_points[j].x * (1 - t) + tmp_points[j + 1].x * t);

tmp_points[j].y = (float)(tmp_points[j].y * (1 - t) + tmp_points[j + 1].y * t);}}

return tmp_points[0];} 

void draw_bezier_curves(pointf* points, int count, pointf* out_points,int out_count)

{    float step = 1.0 / out_count;

float t =0;

for(int i=0; i

參考連線

簡介引用連線

演算法 N階貝塞爾曲線程式設計

最近在研究捕魚的路徑點問題。上司要求路徑由幾個貝塞爾曲線的特徵點來生成魚的路徑。倒騰了一會 真沒想到自己寫出來了。也算是自己畢業後寫的第乙個有關遊戲的演算法。寫這篇日誌做乙個筆記。這裡只放出關鍵 首先它肯定需要乙個遞迴呼叫,因為每個比例都要生成乙個點,然而,每次計算都要從n個點計算為n 1個點,最後...

Python繪製三階貝塞爾曲線

作者本科畢業設計是做機械人軌跡跟蹤控制,軌跡由函式曲線來描述,本文選取三階貝塞爾曲線為代表進行講解,並展示部分基於python tkinter的實現 首先簡單了解一下什麼是貝塞爾曲線 余弦函式曲線我就不多說了哈!貝塞爾曲線又稱貝茲曲線,是法國工程師皮埃爾.貝塞爾於1962年發表。貝塞爾曲線廣泛應用於...

繪製貝塞爾Bezier曲線

trainingtools.cpp 定義控制台應用程式的入口點。include include include include include include using namespace std const int ww max mark count 40 最大40個控制點 int mark c...