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