I have fooled around with bezier a little bit.
Here is some code from a test project I made.
I do use LineTo, but that can easily be changed to pixels by using Bresenham
line algorithm.
A Bresenham Line algorithm for Delphi can be found at my web site:
http://finn.mobilixnet.dk/delphi/
Create a new project, make the Form events you can see and fill in the code
below.
With this you can grab the 4 points and move them, and the bezier curve
updates accordingly.
Enjoy!
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
procedure FormPaint(Sender: TObject);
private
{ Private declarations }
Punkter: array [1..4] of TPoint;
FDrawing: Boolean;
FMovePoint: Integer;
procedure DoDrawBezier;
procedure OpdaterBezier(X, Y: Integer);
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
uses
Math;
procedure DrawBezier(A, v1, v2, D: TPoint; Canvas: TCanvas);
var
B, C, point: TPoint;
t, aa, bb: Real;
begin
B := A; // B = A + v1
B.x := B.x + v1.x;
B.y := B.y + v1.y;
C := D; // C = D + v2
C.x := C.x + v2.x;
C.y := C.y + v2.y;
t := 0; // loop t from 0 to 1 in steps of .01
while t <= 1 do
begin
aa := t;
bb := 1 - t;
// point = A*b3 + 3*B*b2*a + 3C*b*a2 + D*a3
point.x := Round(A.x * Power(bb, 3) + 3 * B.x * Power(bb, 2) * aa + 3
* C.x * bb * Power(aa, 2) + D.x * Power(aa, 3));
point.y := Round(A.y * Power(bb, 3) + 3 * B.y * Power(bb, 2) * aa + 3
* C.y * bb * Power(aa, 2) + D.y * Power(aa, 3));
//Canvas.Pixels[point.x, point.y] := clBlack // drawpixel (point)
if t = 0 then
Canvas.MoveTo(point.x, point.y)
else
Canvas.LineTo(point.x, point.y);
t := t + 0.01;
end; {of loop}
end; {of draw_bezier}
procedure TForm1.DoDrawBezier;
var
A, D, B, C: TPoint;
v1, v2: TPoint;
i: Integer;
begin
A := Punkter[1]; // end point 1
D := Punkter[2]; // end point 2
B := Punkter[3]; // curve point 1
C := Punkter[4]; // curve point 2
v1 := Point(B.x-A.x, B.y-A.y); // vector 1
v2 := Point(C.x-D.x, C.y-D.y); // vector 2
// tegn først vektorerne
Canvas.Pen.Width := 1;
Canvas.Pen.Color := clBlue;
Canvas.MoveTo(A.x, A.y);
Canvas.LineTo(A.x + v1.x, A.y + v1.y);
Canvas.MoveTo(D.x, D.y);
Canvas.LineTo(D.x + v2.x, D.y + v2.y);
Canvas.Pen.Color := clBlack;
DrawBezier(A, v1, v2, D, Canvas);
Canvas.Pen.Color := clRed;
for i := 1 to 4 do
begin
Canvas.MoveTo(Punkter[i].x-1, Punkter[i].y-1);
Canvas.LineTo(Punkter[i].x+1, Punkter[i].y-1);
Canvas.LineTo(Punkter[i].x+1, Punkter[i].y+1);
Canvas.LineTo(Punkter[i].x-1, Punkter[i].y+1);
Canvas.LineTo(Punkter[i].x-1, Punkter[i].y-1);
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
FDrawing := False;
Punkter[1] := Point(020, 068); // end point 1
Punkter[2] := Point(298, 234); // end point 2
Punkter[3] := Point(128, 012); // curve point 1
Punkter[4] := Point(178, 234); // curve point 2
end;
procedure TForm1.FormPaint(Sender: TObject);
begin
DoDrawBezier;
end;
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
i: Integer;
begin
for i := 1 to 4 do
begin
if (x > Punkter[i].x - 3) and (x < Punkter[i].x + 3) and
(y > Punkter[i].y - 3) and (y < Punkter[i].y + 3) then
begin
FMovePoint := i;
FDrawing := True;
end;
end;
end;
procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if FDrawing then
OpdaterBezier(x, y);
FDrawing := False;
end;
procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
if FDrawing then
OpdaterBezier(x, y);
end;
procedure TForm1.OpdaterBezier(X, Y: Integer);
begin
if not FDrawing then
Exit;
if X < 0 then
X := 0;
if X > ClientWidth - 1 then
X := ClientWidth - 1;
if Y < 0 then
Y := 0;
if Y > ClientHeight - 1 then
Y := ClientHeight - 1;
Punkter[FMovePoint] := Point(X, Y);
Refresh;
DoDrawBezier;
end;
end.
--
Finn Tolderlund
Instead of creating a fixed stepsize of 0.01, I think it would be better to
use a "divide and conquer" recursive algorithm:
1) Set A = 0, B = 1
2) Find the distance D from P(A) to P(B). If this is bigger than a limit (a
few pixels), we will go to 3, otherwise go to 4.
3) Divide the bezier from [A, B] into two segments from [A, (A + B)/2] and
[(A + B)/2, B], for both segments go to 2.
4) We draw a straight line from A to B
It will warrant the same accuracy no matter what scale, and no matter what
kind of control points (even if they are way out).
Kind regards,
Nils Haeck
www.simdesign.nl
PS Approximating a user-drawn freehand line with bezier curves.. code can be
found here:
http://www.simdesign.nl/bezier.html
"Finn Tolderlund" wrote in message
news:3f46...@newsgroups.borland.com...
"Nils Haeck" <n.haeck...@chello.nl> skrev i en meddelelse
news:3f466d7b$1...@newsgroups.borland.com...
could it be possible to modify the code to have point.x incrementing by 1
for each value of t, or would this be an impossibility, how else could a
Bezier curve be implemented in a warp, my idea would be to plot a point.y
value for every point.x where x increments by 1 and represents the pixel
increments in a bitmap scanline
"Finn Tolderlund" <n...@spam.dk> wrote in message
news:3f46...@newsgroups.borland.com...
"Nils Haeck" <n.haeck...@chello.nl> wrote in message
news:3f466d7b$1...@newsgroups.borland.com...
"Joe Mbatha" <JoeM...@telkomsa.net> skrev i en meddelelse
news:3f47...@newsgroups.borland.com...
> I would like to try and implement this curve in a warp function, but
> cannot think whether this will be possible ?, the difficulty i am
> imagining is that point.x does not increment uniformily by 1 as t is
> incremented.
As I understand the Bezier function, the points will only increment in
equal intervals where the sections approximate linear behavior. Where
you have a high rate of change, as in a spiral, for example, the points
will be increasingly close as the spiral moves toward the center. The
behavior is to provide essentially constant smoothness.
--
Bill
--------
"To compel a man to furnish funds for the propagation of ideas he
disbelieves and abhors is sinful and tyrannical." -- Thomas Jefferson
It does increment the curve in some cases, yes.. Imagine you divide the
curve (like now) in 100 segments.. and you happen to have a curve going all
over your document. You will certainly see sharp corners, especially if the
bezier contains some short curves.
Of course you can use 1000 segments.. and you'll probably be fine. But then,
you're going to calculate a lot of points that are unneccesary.
The recursive algorithm always calculates/uses a quite optimal number of
points
to render the bezier curve, so with few points for small curves and many
points
for long curves.
I have created an example that implements the recursive bezier drawing. You
can download the source through the link below. It also contains an EXE for
the
readers that are too lazy to compile the demo :)
http://www.simdesign.nl/download/RecurseBezier.zip
Of course you could carry on optimizing even further, for instance one could
try to detect straight parts, and remove intermediate points that are on
such
a straight line. A few of these I have already implemented in other projects
(you can find some info on my site, see Douglas-Peucker).
Kind regards,
Nils Haeck
www.simdesign.nl
"Joe Mbatha" wrote in message
news:3f47...@newsgroups.borland.com...
> of what benefit would this be, would it improve the quality of the curve,
> easier said than done, would you
> please modify the code so that we can have a functional example of this
>
> "Nils Haeck" wrote in message