Original: N-order Bezier curve drawing (c/c#)
Bezier is a classic thing, and the wheel should have a lot of it. Find the N-order Bezier curve using the? Decastrio algorithm (De Casteljau's algorithm)
need to copy code please use the final routine of this article , Most of the code in front of the article is not the best practice , is in the programming process of groping (through the detours), but these demonstrations on the author of the future writing algorithm is very enlightening.
The function to be completed is to draw an n-order Bezier curve based on the starting point, end point and control point
First, we look at the formula of the N-order Bezier curve
The formula uses the combination number, the large number combination number computation also has the algorithm:
In short, multiply the large number by the large number divided by the large number? The process is converted to simple accumulation.
Here is a description of the optimization process for this combinatorial number calculation:
100000/100 =? 1000
500 + 500 = 1000
The above two formulas result in equal results, but if the program is implemented,
The first formula must use at least one Uint32 to store 100000;
The second formula, however, requires only the Uint16 type to complete the calculation.
By means of the transformation calculation, the largest possible result can be calculated by the computer's limited data size.
Bezier curves are also an interpolation algorithm, which calculates the entire path through the middle control points, based on the start and end points.
Modern x86, hardware calculates floating point is converted to double first, so double will be faster than float, of course, this is only for the Intel family complex instruction set products.
Why use float instead of double is because pointf is C # comes with the struct {float x;float Y;}, to Double is also very simple, the global replacement of the data type can be
The C # array comes with the length property, but in order to facilitate porting to C, the array + array lengths are used as arguments, so it is easy to rewrite the array pointer + array length below C.
The direct implementation of the mathematical formula is relatively simple, direct code:
public static class bezier{//<summary>///Draw N-order Bezier paths///</summary>//<param name= "point S "> Input points </param>//<param name=" Count "> Points (n+1) </param>///<param Name=" Step "> Step, the smaller the step, the locus point More dense </param>///<returns></returns> public static pointf[] Draw_bezier_curves (pointf[] points, int count, float Step) {list<pointf> bezier_curves_points = new list<pointf> (); float T = 0F; do {PointF temp_point = Bezier_interpolation_func (t, points, count); Calculate interpolation point T + = step; Bezier_curves_points. ADD (Temp_point); } while (t <= 1 && count > 1); A point of the situation jumps directly. Return bezier_curves_points. ToArray (); All the coordinate points on the curve trajectory}///<summary>//N-order Bezier interpolation calculation function///////////////////////////// <param name= "T" > current interpolation position 0~1, 0 as start, 1 for end point </param>//<param name= "pointS "> Starting point, n-1 control point, End point </param>//<param name=" Count ">n+1 points </param>//<returns></returns > Private static PointF bezier_interpolation_func (float T, pointf[] points, int count) {PointF PointF = n EW PointF (); Float[] part = new Float[count]; Float sum_x = 0, sum_y = 0; for (int i = 0; i < count; i++) {ULONG tmp; int n_order = count-1; Order TMP = Calc_combination_number (N_order, i); Sum_x + = (float) (TMP * points[i]. X * MATH.POW ((1-t), n_order-i) * MATH.POW (T, i)); Sum_y + = (float) (TMP * points[i]. Y * MATH.POW ((1-t), n_order-i) * MATH.POW (T, i)); } pointf.x = sum_x; POINTF.Y = sum_y; return PointF; }///<summary>///Calculation of combination formula///</summary>//<param name= "n" ></param>//<par Am Name= "k" ></param>///<returns></returns> private static ULONG CALc_combination_number (int n, int k) {ulong[] result = new Ulong[n + 1]; for (int i = 1; I <= n; i++) {result[i] = 1; for (int j = i-1; J >= 1; j--) result[j] + = result[j-1]; Result[0] = 1; } return result[k]; }}
How to use:
// 第一个是起点,最后一个是终点,中间的都是控制点,贝赛尔曲线阶数 = 总点数-1PointF[] pointList = new PointF[] { new PointF(1.3F, 2.4F), new PointF(2, 3), new PointF(12.3F, 13.2F) };PointF[] aa = Bezier.draw_bezier_curves(pointList, pointList.Length, 0.001F); // 在起点和终点之间画1/0.001=1000个点foreach (var item in aa){? ? // 绘制曲线点? ? // 下面是C#绘制到Panel画板控件上的代码? ? // panel1.CreateGraphics().DrawEllipse(new Pen(Color.Green), new RectangleF(item, new SizeF(2, 2)));}
It's easy to rewrite it into a C + + version,
PointF is just a struct, {float x;float Y};
The array part of C/C + + does not need new
Math.pow () corresponding to the Pow () in the C language math.h header file
List<pointf> can be implemented in C + + by vector
In C can be replaced with malloc allocation array, size recommended (1/STEP) + 1.
So far, just a mathematical formula to implement the program, what is the problem of this program?
The following two graphs, respectively, are calculated by the above code 4 order and some n (n very Large) Order of the Basel curve,
You can see that the order is too high when the calculation error, the reason is to calculate the number of combinations of functions there, when the order is too large, the result of the calculation of factorial in the combination of numbers overflow.
In fact, careful observation will find that factorial calculation results in the Bezier_interpolation_func function multiplied by a decimal,
This is where improvements can be achieved, since the result of factorial calculation is only the median, then you can get rid of this intermediate value by some algorithms, or by conversion to accumulate the way to solve,
Like the beginning of the article, the Formula n!/(k!* (n-k)) is simplified to (n-1)!/(k!* (n-k-1)!) + (n-1)!/((k-1)!* (n-k+1)!), the same as the recursive addition, Bezier_interpolation_ The Func function can also be optimized in a recursive manner.
Enables it to calculate a higher order without overflow (if there is enough memory space ...).
Let's look at an improved version of the program
public static PointF bezier_interpolation_func(float t, PointF[] points, int count) { if (points.Length < 1) // 一个点都没有 throw new ArgumentOutOfRangeException(); if (count == 1) return points[0]; else { PointF[] tmp_points = new PointF[count]; for (int i = 1; i < count; i++) { tmp_points[i - 1].X = (float)(points[i - 1].X * t + points[i].X * (1 - t)); tmp_points[i - 1].Y = (float)(points[i - 1].Y * t + points[i].Y * (1 - t)); } return bezier_interpolation_func(t, tmp_points, count - 1); } }
You can see that the Bezier_interpolation_func is modified to a recursive form, and the process of finding the combined number is removed.
Look at the results.
Yes, the function is realized.
It then changes the recursion to a loop, which has better compatibility and efficiency, especially if some low-end embedded compilers do not support recursion.
/// 参考: http://blog.csdn.net/Fioman/article/details/2578895public static PointF bezier_interpolation_func(float t, PointF[] points, int count){? ? if (points.Length < 1) ?// 一个点都没有? ? ? ? throw new ArgumentOutOfRangeException();? ? PointF[] tmp_points = new PointF[count];? ? for (int i = 1; i < count; ++i)? ? {? ? ? ? for (int j = 0; j < count - i; ++j)? ? ? ? {? ? ? ? ? ? if (i == 1) // 计算+搬运数据,在计算的时候不要污染源数据? ? ? ? ? ? {? ? ? ? ? ? ? ? tmp_points[j].X = (float)(points[j].X * (1 - t) + points[j + 1].X * t);? ? ? ? ? ? ? ? tmp_points[j].Y = (float)(points[j].Y * (1 - t) + points[j + 1].Y * t);? ? ? ? ? ? ? ? continue;? ? ? ? ? ? }? ? ? ? ? ? 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];}
The following is a complete program of C language, but the coordinates of the printing curve point, not too intuitive.
#include "stdio.h" #include "math.h" #include "assert.h" typedef struct{float x;float Y; PointF; PointF Bezier_interpolation_func (float T, pointf* points, int count) {assert (count>0); PointF tmp_points[count];for (int i = 1; i < count; ++i) {for (int j = 0; j < count-i; ++j) {if (i = = 1) {tmp_points[ J]. X = (float) (Points[j]. X * (1-T) + points[j + 1]. X * t); Tmp_points[j]. Y = (float) (Points[j]. Y * (1-T) + points[j + 1]. Y * t); continue;} 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<out_count; i++) {PointF temp_point = Bezier_interpolation_func (t, points, count); Compute interpolation point T + = step;out_points[i] = temp_point;}} int main (int argc, char **argv) {PointF In[3] = {{100,100},{200,200},{300,100}};//input point int num = 1000; Output points pointf Out[num]; Output Point group draw_bezier_curves (in,3,out,num);//quadratic Bezier curve for (int j=0; j<num; j + +)//output path point {printf ("%d\t x=%f \ t y=%f \ r \ n") , J,out[j]. X,OUT[J]. Y);} return 0;}
Reference Connection http://blog.csdn.net/Fioman/article/details/2578895
Introduction To reference Connection http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/Bezier/de-casteljau.html
N-Order Bezier curve rendering (c/c#)