canvas.bezier() jak zrobic zeby przechodzila przez punky?

0

witam.
nie wiem za bardzo jak sie obchodzic z krzywymi. nie wiem jak to sie wlasciwe rysuje.
co zrobic, zeby linia przechodzila przez wybrane przeze nie punkty?
zeby wygladalo jak LineTo tylko okraglejsze.

0

Krzywe Beziera to aproksymacja punktow kontrolnych wiec przez nie nie przechodzi :) Ale mozesz to osiagnac laczac kilka krzywych tak, zeby poczatki i konce mialy w punktach ktore chcesz zeby przecinaly.

0

ok, el z tego co widze, krzywe potrzebuja min. 3punktow zeby byly.... krzywe.
poczatek, koniec ale gdzie powinien byc ten srodkowy punkt?

0

Krzywa beziera nie przechodzi przez punkty kontrolne.
Najprostszy przykład (krzywa sinusoidalna):

const LIMIT=255;

//Linia między punktami (X1, Y1) oraz (X1, Y2)
procedure Line(Canvas:TCanvas; X1, Y1, X2, Y2:double);
begin
Canvas.MoveTo(Round(X1), Round(Y1));
Canvas.LineTo(Round(X2), Round(Y2));
end;

//Zwraca współrzędne punktu między Start a Stop w Miejsce <0..1>
function PointBetween(Start, Stop:TPoint; Miejsce:double):TPoint;
begin
result.X:=Start.X+Round((Stop.X - Start.X) * Miejsce);
result.Y:=Start.Y+Round((Stop.Y - Start.Y) * Miejsce);
end;

//Rysuje krzywą Beziera o punktach Start i Stop, z uwzględnieniem punktów kontrolnych
//  StartHelper i StopHelper
procedure Bezier(Canvas:TCanvas; Start, StartHelper, Stop, StopHelper:TPoint);
var i:integer;
    p1, p2, p3:TPoint;
    Color:Tcolor;
    Pts:array[0..LIMIT] of TPoint;
const PI2=PI/2;
begin
Canvas.Pen.Color:=clBlue;
Line(Canvas, Start.X, Start.Y, StartHelper.X, StartHelper.Y);
Line(Canvas, Stop.X, Stop.Y, StopHelper.X, StopHelper.Y);
Canvas.Pen.Color:=clRed;
for i:=0 to LIMIT do
  begin
  p1:=PointBetween(Start, StartHelper, sin(PI2*i/LIMIT));
  p2:=PointBetween(Stop, StopHelper, sin(PI2*(LIMIT-i)/LIMIT));
  p3:=PointBetween(p1, p2, i/LIMIT);
  Pts[i]:=p3;
  end;
Canvas.Polyline(Pts);
end;

DOPISANE:

Pełna implementacja pełnych krzywych Beziera i sinusoidalnych krzywych Beziera:

unit Unit1;

interface

uses
  Windows, Types, Classes, Graphics, Controls, Forms;

type
  TForm1 = class(TForm)
    procedure FormPaint(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

var Points:array[0..7] of TPoint;
    Actives:integer = 0;
const LIMIT=255;

//Linia między punktami (X1, Y1) oraz (X1, Y2)
procedure Line(Canvas:TCanvas; P1, P2:TPoint); overload;
begin
Canvas.MoveTo(P1.X, P1.Y);
Canvas.LineTo(P2.X, P2.Y);
end;

//Linia między punktami (X1, Y1) oraz (X1, Y2)
procedure Line(Canvas:TCanvas; X1, Y1, X2, Y2:double); overload;
begin
Line(Canvas, Point(Round(X1), Round(Y1)), Point(Round(X2), Round(Y2)));
end;

////////////////////////////////////////////////////////////////////////////////
//Rysuje pełną krzywą Beziera o punktach Start i Stop, z uwzględnieniem prostych
//  StartHelper i StopHelper
////////////////////////////////////////////////////////////////////////////////
procedure FullBezier(Canvas:TCanvas; Start, StartHelper, StopHelper, Stop:TPoint);
var i:integer;
    Pts:array[0..LIMIT] of TPoint;

  //Wylicza punkt na krzywej w czasie t <0..1>
  function PointOnBezier(Start, StartHelper, Stop, StopHelper:TPoint; t:double):TPoint;
  var ax,bx,cx,ay,by,cy:double;
      t2,t3:double;
  begin
  cx := 3.0 * (StartHelper.x - Start.x);
  bx := 3.0 * (StopHelper.x - StartHelper.x) - cx;
  ax := Stop.x - Start.x - cx - bx;
  cy := 3.0 * (StartHelper.y - Start.y);
  by := 3.0 * (StopHelper.y - StartHelper.y) - cy;
  ay := Stop.y - Start.y - cy - by;
  t2:=t*t;
  t3:=t*t2;
  result.x := Round((ax * t3) + (bx * t2) + (cx * t) + Start.x);
  result.y := Round((ay * t3) + (by * t2) + (cy * t) + Start.y);
  end;

begin
for i:=0 to LIMIT do
  Pts[i]:=PointOnBezier(Start, StartHelper, StopHelper, Stop, i/LIMIT);
Canvas.Polyline(Pts);
end;

////////////////////////////////////////////////////////////////////////////////
//Rysuje sinusoidalną krzywą Beziera o punktach Start i Stop, z uwzględnieniem
//  prostych StartHelper i StopHelper
////////////////////////////////////////////////////////////////////////////////
procedure SimpleBezier(Canvas:TCanvas; Start, StartHelper, StopHelper, Stop:TPoint);
var i:integer;
    Pts:array[0..LIMIT] of TPoint;
const PI2=PI/2;

  //Wylicza punkt między punktami z czasie t <0..1>
  function PointBetween(Start, Stop:TPoint; t:double):TPoint;
  begin
  result.X:=Start.X+Round((Stop.X - Start.X) * t);
  result.Y:=Start.Y+Round((Stop.Y - Start.Y) * t);
  end;

  //Wylicza punkt na krzywej w czasie t <0..1>
  function PointOnBezier(Start, StartHelper, Stop, StopHelper:TPoint; t:double):TPoint;
  var p1, p2:TPoint;
  begin
  p1:=PointBetween(Start, StartHelper, sin(PI2*t));
  p2:=PointBetween(Stop, StopHelper, sin(PI2*(1-t)));
  result:=PointBetween(p1, p2, t);
  end;

begin
for i:=0 to LIMIT do
  Pts[i]:=PointOnBezier(Start, StartHelper, StopHelper, Stop, i/LIMIT);
Canvas.Polyline(Pts);
end;

procedure TForm1.FormPaint(Sender: TObject);
begin
FormMouseMove(sender, [], Points[Actives].X, Points[Actives].Y);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
Points[0]:=Point(50, 50);
Points[1]:=Point(50, 300);
Points[2]:=Point(300, 300);
Points[3]:=Point(300, 50);
Points[4]:=Point(50, 50);
Points[5]:=Point(50, 300);
Points[6]:=Point(300, 300);
Points[7]:=Point(300, 50);
end;

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var Cursor:TPoint;
begin
Actives:=(Actives+1) mod 8;
Cursor:=ClientToScreen(Points[Actives]);
SetCursorPos(Cursor.X, Cursor.Y);
end;

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
var i:integer;
begin
Points[Actives]:=Point(X,Y);
Canvas.Brush.Color:=clBtnFace;
Canvas.Pen.Color:=clBtnFace;
Canvas.Rectangle(0, 0, ClientWidth, ClientHeight);
Canvas.Pen.Color:=clBlue;
Line(Canvas, Points[0], Points[1]);
Line(Canvas, Points[2], Points[3]);
Line(Canvas, Points[4], Points[5]);
Line(Canvas, Points[6], Points[7]);
Canvas.Brush.Color:=clBtnFace;
Canvas.Pen.Color:=clGreen;
for i:=0 to 8 do
  begin
  if (i=Actives) then
    begin
    Canvas.Brush.Color:=clFuchsia;
    Canvas.FillRect(Rect(Points[i].X-2, Points[i].Y-2, Points[i].X+2, Points[i].Y+2));
    Canvas.Brush.Color:=clBtnFace;
    end
  else
    Canvas.Rectangle(Rect(Points[i].X-2, Points[i].Y-2, Points[i].X+2, Points[i].Y+2))
  end;
Canvas.Pen.Color:=clRed;
////////////////////////////////////////////////////////////////////////////////
SimpleBezier(Canvas, Points[0], Points[1], Points[2], Points[3]);
FullBezier(Canvas, Points[4], Points[5], Points[6], Points[7]);
////////////////////////////////////////////////////////////////////////////////
end;

end.

1 użytkowników online, w tym zalogowanych: 0, gości: 1