Delphi - база знаний

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


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



Cтатья Даниила Карапетяна ( )

как нарисовать кривую
Безье. Именно она применяется для построения гладких кривых во всех графических
программах - от 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. Все
узлы можно перемещать по полю мышью. Для создания нового узла нужно нажать мышью
на пустое место на поле или увеличить порядок кривой. Скачать необходимые для компиляции файлы проекта можно на .

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  
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;  
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

Взято с Vingrad.ru



Содержание раздела