Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Bezier curve

112 views
Skip to first unread message

Dave Jones

unread,
Aug 22, 2003, 3:01:47 AM8/22/03
to
are there any graphics masters out there with code for pixel plotting a
Bezier curve, given 4 points, not looking to draw with the PolyBezier
function , or with lines,
but from scratch with pixels, and need smooth curves


Finn Tolderlund

unread,
Aug 22, 2003, 11:44:27 AM8/22/03
to

"Dave Jones" <da...@graphics.net> skrev i en meddelelse
news:3f45...@newsgroups.borland.com...

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

Nils Haeck

unread,
Aug 22, 2003, 3:31:41 PM8/22/03
to
Hi Finn,

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

Finn Tolderlund

unread,
Aug 22, 2003, 4:16:27 PM8/22/03
to
Thanks for your suggestion Nils.
You are right that a fixed stepsize is not optimal.
The step size of 0.01 controls the smoothness of the bezier curve, in other
words the number of points/lines that the curve will consist of.
I think that the stepsize should be set depending on the length of the
curve, in order to get a smooth curve but not use too many unnecessary
steps. This could be approximated before beginning to draw the curve, but I
didn't do that in my simple test project.
--
Finn Tolderlund


"Nils Haeck" <n.haeck...@chello.nl> skrev i en meddelelse
news:3f466d7b$1...@newsgroups.borland.com...

Joe Mbatha

unread,
Aug 23, 2003, 8:12:31 AM8/23/03
to
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.

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

Joe Mbatha

unread,
Aug 23, 2003, 8:02:19 AM8/23/03
to
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" <n.haeck...@chello.nl> wrote in message
news:3f466d7b$1...@newsgroups.borland.com...

Finn Tolderlund

unread,
Aug 23, 2003, 10:23:36 AM8/23/03
to
If I understand you correctly that is not possible.
There is not a one-to-one relationsship between x and y values.
Because of the curving nature of a bezier curve, sometimes there are many y
values for a single x value, and there can be many x values for a single y
value.
--
Finn Tolderlund


"Joe Mbatha" <JoeM...@telkomsa.net> skrev i en meddelelse
news:3f47...@newsgroups.borland.com...

Unknown

unread,
Aug 23, 2003, 5:12:02 PM8/23/03
to
On 23-Aug-03, Joe Mbatha said:

> 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

Nils Haeck

unread,
Aug 23, 2003, 9:17:56 PM8/23/03
to
Hi Joe,

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

0 new messages