![]() |
![]() ![]() ![]() ![]() ![]() |
![]() |
Как нарисовать кривую БезьеDelphi , Графика и Игры , Графика
Автор: Даниил Карапетян Как нарисовать кривую Безье. Именно она применяется для построения гладких кривых во всех графических программах - от 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 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; Как нарисовать кривую Безье: статья описывает алгоритм и функции для создания гладких кривых Безье в программном обеспечении. Комментарии и вопросыПолучайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.
|
||||
©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007 |