Как нарисовать кривую Безье?

Как нарисовать кривую Безье. Именно она применяется для построения гладких кривых во всех графических программах - от PaintBrush до CorelDraw и PhotoShop. 
Для задания кривой Безье n-ной степени (чем больше степень, тем более кривой может быть линия; кривая первой степени - отрезок) нужно указать n+1 точку. Первая и последняя точки будут началом и концом кривой, а остальные точки задаю ее поведение на других участках. В частности, первая и n-ая точки задают касательные и кривизну кривой на ее концах.
В большинстве программ используются кривые Безье третьего порядка. Начиная с Delphi5 такую кривую можно нарисовать при помощи функции PolyBezier. Кривая Безье задается параметрически (x=x(t), y=y(t)). Это позволяет ей вести себя абсолютно произвольно. Если бы она задавалась, как y(x), она не смогла бы даже сделать поворот на 180 градусов. Функции x(t) и y(t) выглядят так:
 x(t)= Cn0 * t0 * (1-t)n * x0 + Cn1 * t1 * (1-t)n-1 * x1 + Cn2 * t2 * (1-t)n-2 * x2 + ... + Cnn * tn * (1-t)0 * xn
 y(t)= Cn0 * t0 * (1-t)n * y0 + Cn1 * t1 * (1-t)n-1 * y1 + Cn2 * t2 * (1-t)n-2 * y2 + ... + Cnn * tn * (1-t)0 * yn
где 
* n - порядок кривой,
* Cni - коэффициенты в разложении бинома Ньютона,
* t - параметр, меняющийся от 0 до 1,
* xi, yi - координаты опорных точек.
Эта программа строит кривую Безье n-ного порядка. n задается в SpinEdit1. Все узлы можно перемещать по полю мышью. Для создания нового узла нужно нажать мышью на пустое место на поле или увеличить порядок кривой. Скачать необходимые для компиляции файлы проекта можно на http://program.dax.ru/.
uses Math;
const
 RectSize = 5;
 MaxN = 128;
var
 n: Integer = -1;
 pt: array [0..MaxN] of TPoint;
 C: array [0..MaxN] of Single;
 bm: TBitMap;
function GetBinomialCoefficient(m, i: integer): single;
 function Factorial(x: Integer): Double;
 var
 i: Integer;
 begin
 Result := 1;
 for i := 2 to x do
 Result := Result * i;
 end;
begin
 Result := Factorial(m) / (Factorial(i) * Factorial(m - i));
end;
procedure DrawBezier(Canvas: TCanvas; Count: integer);
type
 TPointArray = array [word] of TPoint;
 PPointArray = ^TPointArray;
var
 p: PPointArray;
 Step, qx, qy, t, q: Single;
 i, j: Integer;
begin
 GetMem(p, SizeOf(TPoint) * (Count + 1));
 Step := 1.0 / Count;
 for i := 0 to Count do
 begin
 t := i * Step;
 qx := 0; qy := 0;
 for j := 0 to n do
 begin
 q := C[j] * IntPower(1 - t, j) * IntPower(t, n - j);
 qx := qx + q * pt[j].x;
 qy := qy + q * pt[j].y;
 end;
 p[i] := Point(Round(qx), Round(qy));
 end;
 Canvas.Polyline(Slice(p^, Count + 1));
 FreeMem(p);
end;
procedure DrawLines(canvas: TCanvas; const pt: array of TPoint);
var
 i: Integer;
begin
 Canvas.Pen.Color := clGreen;
 Canvas.Pen.Width := 1;
 Canvas.MoveTo(pt[0].x, pt[0].y);
 for i := 0 to n do
 begin
 Canvas.Rectangle(Bounds(pt[i].x - RectSize, pt[i].y - RectSize,
 2 * RectSize, 2 * RectSize));
 Canvas.LineTo(pt[i].x, pt[i].y);
 end;
end;
procedure Redraw;
begin
 with Form1.PaintBox1 do
 begin
 bm.Canvas.FillRect(Bounds(0, 0, Width, Height));
 if Form1.CheckBox1.Checked then DrawLines(bm.Canvas, pt);
 bm.Canvas.PolyBezier(pt);
 bm.Canvas.Pen.Color := clRed;
 bm.Canvas.Pen.Width := Form1.SpinEdit3.Value;
 DrawBezier(bm.Canvas, Form1.SpinEdit2.Value);
 Canvas.Draw(0, 0, bm);
 end;
end;
var
 moving: Integer = -1;
 oldr: TRect;
procedure FillRandom(NewN: Integer);
var
 i: Integer;
begin
 Randomize;
 for i := n+1 to NewN do
 pt[i] := Point(Random(Form1.PaintBox1.Width - 20) + 10,
 Random(Form1.PaintBox1.Height - 20) + 10);
 n := NewN;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
 bm := TBitmap.Create;
 bm.Width := PaintBox1.Width;
 bm.Height := PaintBox1.Height;
 SpinEdit1.MinValue := 1;
 SpinEdit1.MaxValue := MaxN;
 SpinEdit1.Value := 3;
 SpinEdit2.MinValue := 6;
 SpinEdit2.MaxValue := MaxN * 4;
 SpinEdit2.Value := 50;
 SpinEdit2.OnChange := PaintBox1.OnPaint;
 SpinEdit3.MinValue := 1;
 SpinEdit3.MaxValue := 8;
 SpinEdit3.Value := 3;
 SpinEdit3.OnChange := PaintBox1.OnPaint;
 CheckBox1.Checked := true;
 CheckBox1.OnClick := PaintBox1.OnPaint;
end;
procedure TForm1.PaintBox1MouseDown(Sender: TObject; Button: TMouseButton;
 Shift: TShiftState; X, Y: Integer);
var
 i: Integer;
 r: TRect;
begin
 if Button <> mbLeft then Exit;
 for i := 0 to n do
 begin
 if (abs(X - pt[i].x) <= RectSize) and (abs(Y - pt[i].y) <= RectSize) then
 begin
 moving := i;
 r.TopLeft := Form1.ClientToScreen(PaintBox1.BoundsRect.TopLeft);
 r.BottomRight := Form1.ClientToScreen(PaintBox1.BoundsRect.BottomRight);
 GetClipCursor(oldr);
 ClipCursor(@r);
 Exit;
 end;
 end;
 if moving < 0 then
 begin
 SpinEdit1.Value := SpinEdit1.Value + 1;
 pt[n] := Point(X, Y);
 Redraw;
 end;
end;
procedure TForm1.PaintBox1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
 if moving < 0 then Exit;
 pt[moving] := Point(X, Y);
 Redraw;
end;
procedure TForm1.PaintBox1MouseUp(Sender: TObject; Button: TMouseButton;
 Shift: TShiftState; X, Y: Integer);
begin
 if (Button = mbLeft) and (moving >= 0) then
 begin
 moving := -1;
 ClipCursor(@oldr);
 end;
end;
procedure TForm1.SpinEdit1Change(Sender: TObject);
var
 i: Integer;
begin
 FillRandom(SpinEdit1.Value);
 SpinEdit2.MinValue := n * 2;
 for i := 0 to n do C[i] := GetBinomialCoefficient(n, i);
 Redraw;
end;
procedure TForm1.PaintBox1Paint(Sender: TObject);
begin
 Redraw;
end;
 На сайте http://delphi4all.narod.ru Вы найдете еще более 100 советов по Delphi.
 Email: delphi4all@narod.ru
Author: Даниил Карапетян
Source: http://forum.vingrad.ru
ID: 04097
Category:Алгоритмы
Category:Графика