ginsonic
asked on
Draw ellipse
I wish to follow an eliptic route .
For example I wish to go from a point (100,100) and to draw an ellipse ( with next dimensions for example : 100 and 75 ), point by point, to my canvas.
Attention, POINT BY POINT .
For example I wish to go from a point (100,100) and to draw an ellipse ( with next dimensions for example : 100 and 75 ), point by point, to my canvas.
Attention, POINT BY POINT .
would be a nice qow, nick :-))
ASKER
I thinking to this :)
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
to ginsonic:
sorry, for delay but I was last 2 weeks offline.
You can contact me C_Lababidi@hotmail.com
Best Regards
Cesario
sorry, for delay but I was last 2 weeks offline.
You can contact me C_Lababidi@hotmail.com
Best Regards
Cesario
Hi Ginsonic
Do you mean with a time delay in the drawing from point to point?
T.
Do you mean with a time delay in the drawing from point to point?
T.
Yes i know not POINT by POINT, but Win32 AngleArc function is not well known and needs mentioning.
I recall that it is possible to tweak Bresenham algoritm to draw ellipses.
ginsonic,
Cesario provided you with correct answer. You could tweek it a little by replacing Trunc with Round (more accurate curve) and using precalculated trigonometric table (much more speed).
Cesario provided you with correct answer. You could tweek it a little by replacing Trunc with Round (more accurate curve) and using precalculated trigonometric table (much more speed).
After a fast Google search i found this algorithm in a newsgroup message which i translated to Pascal. It is completely untested. The text is from the message.
: I am looking an algorithm for drawing an ellipse .
: The input shall be the midpoint and the two axes .
Here is one implementation in C of the algorithm...
Remark : Only one quarter of the ellipse is computated, the rest
is set by symmetry.
a and b are the half-axes (I hope this is the right
word for it) of the ellipse in x- and y-direction.
If you like a circle set a=b=radius .
-------------------------- ------- cut -------------------------- -------
procedure symmetry(x, y: Integer);
begin
PUT_PIXEL( x, y); // This would obviously be inlined!
PUT_PIXEL(-x, y); // and offset by a constant amount
PUT_PIXEL(-x,-y);
PUT_PIXEL( x,-y);
end;
procedure bresenham_ellipse(a, b: Integer);
var
x, y, a2, b2, S, T: Integer;
begin
a2 := a*a;
b2 := b*b;
x := 0;
y := b;
S := a2*(1-2*b) + 2*b2;
T := b2 - 2*a2*(2*b-1);
symmetry(x, y);
repeat
if S < 0 then
begin
S := S + 2*b2*(2*x+3);
T = T + 4*b2*(x+1);
Inc(x);
end
else
if T < 0 then
begin
S := S + 2*b2*(2*x+3) - 4*a2*(y-1);
T := T + 4*b2*(x+1) - 2*a2*(2*y-3);
Inc(x);
Dec(y);
end
else
begin
S := S - 4*a2*(y-1);
T := T - 2*a2*(2*y-3);
Dec(y);
end;
symmetry(x, y);
until y <= 0;
end;
: I am looking an algorithm for drawing an ellipse .
: The input shall be the midpoint and the two axes .
Here is one implementation in C of the algorithm...
Remark : Only one quarter of the ellipse is computated, the rest
is set by symmetry.
a and b are the half-axes (I hope this is the right
word for it) of the ellipse in x- and y-direction.
If you like a circle set a=b=radius .
--------------------------
procedure symmetry(x, y: Integer);
begin
PUT_PIXEL( x, y); // This would obviously be inlined!
PUT_PIXEL(-x, y); // and offset by a constant amount
PUT_PIXEL(-x,-y);
PUT_PIXEL( x,-y);
end;
procedure bresenham_ellipse(a, b: Integer);
var
x, y, a2, b2, S, T: Integer;
begin
a2 := a*a;
b2 := b*b;
x := 0;
y := b;
S := a2*(1-2*b) + 2*b2;
T := b2 - 2*a2*(2*b-1);
symmetry(x, y);
repeat
if S < 0 then
begin
S := S + 2*b2*(2*x+3);
T = T + 4*b2*(x+1);
Inc(x);
end
else
if T < 0 then
begin
S := S + 2*b2*(2*x+3) - 4*a2*(y-1);
T := T + 4*b2*(x+1) - 2*a2*(2*y-3);
Inc(x);
Dec(y);
end
else
begin
S := S - 4*a2*(y-1);
T := T - 2*a2*(2*y-3);
Dec(y);
end;
symmetry(x, y);
until y <= 0;
end;
ASKER
I realise that Cesario solution solve my question , so I will give the points to him.
But I wait anothers working solutions for anothers points . Alike kretzschmar's Q.O.W.
If you have solution or can improve proposed one the put a comment and take new points :)
But I wait anothers working solutions for anothers points . Alike kretzschmar's Q.O.W.
If you have solution or can improve proposed one the put a comment and take new points :)
ASKER
I realise that Cesario solution solve my question , so I will give the points to him.
But I wait anothers working solutions for anothers points . Alike kretzschmar's Q.O.W.
If you have solution or can improve proposed one the put a comment and take new points :)
But I wait anothers working solutions for anothers points . Alike kretzschmar's Q.O.W.
If you have solution or can improve proposed one the put a comment and take new points :)
ASKER
I realise that Cesario solution solve my question , so I will give the points to him.
But I wait anothers working solutions for anothers points . Alike kretzschmar's Q.O.W.
If you have solution or can improve proposed one the put a comment and take new points :)
But I wait anothers working solutions for anothers points . Alike kretzschmar's Q.O.W.
If you have solution or can improve proposed one the put a comment and take new points :)
ASKER
ups
ginsonic,
This are optimized versions (DrawEllipse() and even faster, DrawEllipse2()):
const RESOLUTION = 100; // larger=better but slower
// Requires global vars (trig. tables)
var
Form1: TForm1;
SinTable, CosTable: Array [0..360*RESOLUTION] of Double;
......
procedure BuildTrigTable;
// Precalculation of sin and cos. Resolution is 1/RESOLUTION degree.
var i: Integer;
radian: Double;
begin
for i:= 0 to 360*RESOLUTION do begin
radian:=i*PI/(180*RESOLUTI ON);
SinTable[i]:=Sin(radian);
CosTable[i]:=Cos(radian);
end;
end;
procedure DrawEllipse(Where: TCanvas; Center, R: TPoint);
// Draws ellipse, centered around Center, with halfaxes: a=R.X , b=R.Y
var i,x,y, xold, yold: Integer;
begin
for i:= 0 to 360*RESOLUTION do begin
x:=Center.X+Round(R.X*CosT able[i]);
y:=Center.Y+Round(R.Y*SinT able[i]);
if (xold<>x) or (yold<>y) then Where.Pixels[x,y]:=Where.P en.Color;
xold:=x; yold:=y;
end;
end;
procedure DrawEllipse2(Where: TCanvas; Center, R: TPoint);
// Draws ellipse, centered around Center, with halfaxes: a=R.X , b=R.Y
var i,x,y, xold, yold: Integer;
begin
for i:= 0 to 360*RESOLUTION do begin
x:=Center.X+Round(R.X*CosT able[i]);
y:=Center.Y+Round(R.Y*SinT able[i]);
if i=0 then begin
Where.Pixels[x,y]:=Where.P en.Color;
Where.MoveTo(x,y);
end
else if (xold<>x) or (yold<>y) then Where.LineTo(x,y);
xold:=x; yold:=y;
end;
end;
COMMENTS
-------------
1. DrawEllipse() uses precalculated trig. table instead of slow trig. functions. On the down-side, you must call BuildTrigTable before using DrawEllipse().
2. BuildTrigTable is called only once. After that, tables are built, and we can use them instead of real sin,cos.
It is usually called at the execution start (for example, initialization section, OnCreate event of the main form etc.)
3. RESOLUTION defines granulation of real functions. This shouldn't be below 10 for DrawEllipse().
4. DrawEllipse2() is good for very low RESOLUTION (even for RESOLUTION=1 !!). But, it uses LineTo to aproximate ellipse segments.
If you want real speed, and are willing to live without Pixels (that is, use LineTo), it's better to use DrawEllipse2() with RESOLUTION of 1..10, which is *very* fast.
EXAMPLE OF USE (place TButton on the form)
-------------------------- ---------- ---------- ------
procedure TForm1.Button1Click(Sender : TObject);
var i: Integer;
begin
BuildTrigTable; // Call it *ONLY* once (OnForm1Create is good place)
Randomize;
for i:=1 to 100 do
DrawEllipse(Form1.Canvas,
Point(100+Random(100),100+ Random(100 )),
Point(50+Random(100) , 10+Random(100)));
end;
This are optimized versions (DrawEllipse() and even faster, DrawEllipse2()):
const RESOLUTION = 100; // larger=better but slower
// Requires global vars (trig. tables)
var
Form1: TForm1;
SinTable, CosTable: Array [0..360*RESOLUTION] of Double;
......
procedure BuildTrigTable;
// Precalculation of sin and cos. Resolution is 1/RESOLUTION degree.
var i: Integer;
radian: Double;
begin
for i:= 0 to 360*RESOLUTION do begin
radian:=i*PI/(180*RESOLUTI
SinTable[i]:=Sin(radian);
CosTable[i]:=Cos(radian);
end;
end;
procedure DrawEllipse(Where: TCanvas; Center, R: TPoint);
// Draws ellipse, centered around Center, with halfaxes: a=R.X , b=R.Y
var i,x,y, xold, yold: Integer;
begin
for i:= 0 to 360*RESOLUTION do begin
x:=Center.X+Round(R.X*CosT
y:=Center.Y+Round(R.Y*SinT
if (xold<>x) or (yold<>y) then Where.Pixels[x,y]:=Where.P
xold:=x; yold:=y;
end;
end;
procedure DrawEllipse2(Where: TCanvas; Center, R: TPoint);
// Draws ellipse, centered around Center, with halfaxes: a=R.X , b=R.Y
var i,x,y, xold, yold: Integer;
begin
for i:= 0 to 360*RESOLUTION do begin
x:=Center.X+Round(R.X*CosT
y:=Center.Y+Round(R.Y*SinT
if i=0 then begin
Where.Pixels[x,y]:=Where.P
Where.MoveTo(x,y);
end
else if (xold<>x) or (yold<>y) then Where.LineTo(x,y);
xold:=x; yold:=y;
end;
end;
COMMENTS
-------------
1. DrawEllipse() uses precalculated trig. table instead of slow trig. functions. On the down-side, you must call BuildTrigTable before using DrawEllipse().
2. BuildTrigTable is called only once. After that, tables are built, and we can use them instead of real sin,cos.
It is usually called at the execution start (for example, initialization section, OnCreate event of the main form etc.)
3. RESOLUTION defines granulation of real functions. This shouldn't be below 10 for DrawEllipse().
4. DrawEllipse2() is good for very low RESOLUTION (even for RESOLUTION=1 !!). But, it uses LineTo to aproximate ellipse segments.
If you want real speed, and are willing to live without Pixels (that is, use LineTo), it's better to use DrawEllipse2() with RESOLUTION of 1..10, which is *very* fast.
EXAMPLE OF USE (place TButton on the form)
--------------------------
procedure TForm1.Button1Click(Sender
var i: Integer;
begin
BuildTrigTable; // Call it *ONLY* once (OnForm1Create is good place)
Randomize;
for i:=1 to 100 do
DrawEllipse(Form1.Canvas,
Point(100+Random(100),100+
Point(50+Random(100) , 10+Random(100)));
end;
ASKER
Add a sleep(1) and see what slow is it.
ginsonic,
Why should you put sleep(1) (inside DrawEllipse() loop I assume) ?
Why should you put sleep(1) (inside DrawEllipse() loop I assume) ?
Add a sleep(10) and it's really slow ;-)
ASKER
Just to create a nice slide draw ellipse. Try with Cesario sample and see what I wish to say.
ginsonic,
If you want to create animation effect, you can regulate delay in my code by changing RESOLUTION const. Decrease it and you'll get faster animation if that's what you want. Set it to 1 and see what happens.
But, I don't quite understand what you want:
> If you have solution or can improve proposed one the put a comment and take new points :)
Could you tell us what you mean by improved solution? What should be improved in accepted answer?
If you want to create animation effect, you can regulate delay in my code by changing RESOLUTION const. Decrease it and you'll get faster animation if that's what you want. Set it to 1 and see what happens.
But, I don't quite understand what you want:
> If you have solution or can improve proposed one the put a comment and take new points :)
Could you tell us what you mean by improved solution? What should be improved in accepted answer?
ASKER
I talk about a slow animation when draw step by step . Draw a point , sleep a while , draw next ....
About improvement ... any proposed solution not just the accepted one . Just to be a better way alike accepted .
About improvement ... any proposed solution not just the accepted one . Just to be a better way alike accepted .
a comment only:
unit EllipseUnit1;
{This is an illustration of an ellipse that starts at
point (100,100) and fits in a rectangle of 100,75. Continues
until back again to point (100,100)}
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, Math;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender : TObject);
var
mx, my, x, y, cnt: Integer;
y1: Real;
begin
Canvas.Pen.Color := clGreen;
//first a bit of a container
//100 Width, 75 Height; 1/2 width = 50; 1/2 height = 38
Canvas.MoveTo(100, 100); // Start point for request
Canvas.LineTo(150, 100);
Canvas.LineTo(150, 175);
Canvas.LineTo(50, 175);
Canvas.LineTo(50, 100);
Canvas.LineTo(100, 100);
Canvas.Pen.Color := clBlue;
Canvas.LineTo(100, 175); // Axis line
Canvas.Pen.Color := clBlack;
Canvas.MoveTo(50, 138); // Axis line
//Center is located at (100,138)
Canvas.LineTo(100, 138);
Canvas.Pen.Color := clWhite;
Canvas.LineTo(150, 138);// Axis line
{ A bit of Standard Algebra:
Draw an Ellipse
Center of the ellipse is located at h,k and the
major axis is 'a' the minor axis is 'b'
the equation is:
(((x-h)^2)/a^2) + (((y-k)^2)/b^2) = 1
Eventual substitutions:
a = 50; b = 38 ; h = 100; k = 100;
Algebraic equivalents
(((y-k)^2)/b^2) = 1 - (((x-h)^2)/a^2)
(y-k)^2) = (1- (((x-h)^2)/a^2))* b^2
and solving for y
y-k = + square root ((1- (((x-h)^2)/a^2))* b^2)
y-k = - square root ((1- (((x-h)^2)/a^2))* b^2)
y = k + square root ((1- (((x-h)^2)/a^2))* b^2)
y = k - square root ((1- (((x-h)^2)/a^2))* b^2)
substitution of values
y = 100 + square root ((1- (((x-100)^2)/50^2))* 38^2)
y = 100 - square root ((1- (((x-100)^2)/50^2))* 38^2)
square root = power raised to the 0.5
y = 100 - power(((1- ((power(x-100),2)/(power,5 0,2))* (power,38,2),0.5)
}
Canvas.TextOut(100,80, 'Starts Here at (100,100)');
Canvas.Pen.Color := clRed;
Canvas.MoveTo(100, 138);
for x := 100 to 150 do
begin
Sleep(5);
y := Round(137.5 - (power((1 - (power(x - 100, 2)) / (power(50, 2))) * power(38, 2), 0.5)));
Canvas.LineTo(X, Y);
end;
Canvas.Pen.Color := clYellow;
for x := 150 downto 50 do
begin
Sleep(5);
y := Round(137.5 + (power((1 - (power(x - 100, 2)) / (power(50, 2))) * power(38, 2), 0.5)));
Canvas.LineTo(X, Y);
end;
Canvas.Pen.Color := clBlack;
for x := 51 to 100 do
begin
Sleep(5);
y := Round(137.5 - (power((1 - (power(x - 100, 2)) / (power(50, 2))) * power(38, 2), 0.5)));
Canvas.LineTo(X, Y);
end;
Canvas.TextOut(100,80, 'Ends Here at (100,100) ');
end;
procedure TForm1.Button2Click(Sender : TObject);
begin
Application.Terminate;
end;
end.
// Form as text
object Form1: TForm1
Left = 456
Top = 182
Width = 258
Height = 368
Caption = 'Ellipse'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -13
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 120
TextHeight = 16
object Button1: TButton
Left = 64
Top = 232
Width = 115
Height = 25
Caption = 'Show Ellipse'
TabOrder = 0
OnClick = Button1Click
end
object Button2: TButton
Left = 64
Top = 272
Width = 115
Height = 25
Caption = 'Close'
TabOrder = 1
OnClick = Button2Click
end
end
Goal :
From: ginsonic
Status: Waiting for Answer Points: 100
Email A Friend
I wish to follow an eliptic route.
for example I wish to go from a point(100, 100) and to draw an ellipse(with next dimensions for example
: 100 and 75), point by point, to my canvas.
unit EllipseUnit1;
{This is an illustration of an ellipse that starts at
point (100,100) and fits in a rectangle of 100,75. Continues
until back again to point (100,100)}
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, Math;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender
var
mx, my, x, y, cnt: Integer;
y1: Real;
begin
Canvas.Pen.Color := clGreen;
//first a bit of a container
//100 Width, 75 Height; 1/2 width = 50; 1/2 height = 38
Canvas.MoveTo(100, 100); // Start point for request
Canvas.LineTo(150, 100);
Canvas.LineTo(150, 175);
Canvas.LineTo(50, 175);
Canvas.LineTo(50, 100);
Canvas.LineTo(100, 100);
Canvas.Pen.Color := clBlue;
Canvas.LineTo(100, 175); // Axis line
Canvas.Pen.Color := clBlack;
Canvas.MoveTo(50, 138); // Axis line
//Center is located at (100,138)
Canvas.LineTo(100, 138);
Canvas.Pen.Color := clWhite;
Canvas.LineTo(150, 138);// Axis line
{ A bit of Standard Algebra:
Draw an Ellipse
Center of the ellipse is located at h,k and the
major axis is 'a' the minor axis is 'b'
the equation is:
(((x-h)^2)/a^2) + (((y-k)^2)/b^2) = 1
Eventual substitutions:
a = 50; b = 38 ; h = 100; k = 100;
Algebraic equivalents
(((y-k)^2)/b^2) = 1 - (((x-h)^2)/a^2)
(y-k)^2) = (1- (((x-h)^2)/a^2))* b^2
and solving for y
y-k = + square root ((1- (((x-h)^2)/a^2))* b^2)
y-k = - square root ((1- (((x-h)^2)/a^2))* b^2)
y = k + square root ((1- (((x-h)^2)/a^2))* b^2)
y = k - square root ((1- (((x-h)^2)/a^2))* b^2)
substitution of values
y = 100 + square root ((1- (((x-100)^2)/50^2))* 38^2)
y = 100 - square root ((1- (((x-100)^2)/50^2))* 38^2)
square root = power raised to the 0.5
y = 100 - power(((1- ((power(x-100),2)/(power,5
}
Canvas.TextOut(100,80, 'Starts Here at (100,100)');
Canvas.Pen.Color := clRed;
Canvas.MoveTo(100, 138);
for x := 100 to 150 do
begin
Sleep(5);
y := Round(137.5 - (power((1 - (power(x - 100, 2)) / (power(50, 2))) * power(38, 2), 0.5)));
Canvas.LineTo(X, Y);
end;
Canvas.Pen.Color := clYellow;
for x := 150 downto 50 do
begin
Sleep(5);
y := Round(137.5 + (power((1 - (power(x - 100, 2)) / (power(50, 2))) * power(38, 2), 0.5)));
Canvas.LineTo(X, Y);
end;
Canvas.Pen.Color := clBlack;
for x := 51 to 100 do
begin
Sleep(5);
y := Round(137.5 - (power((1 - (power(x - 100, 2)) / (power(50, 2))) * power(38, 2), 0.5)));
Canvas.LineTo(X, Y);
end;
Canvas.TextOut(100,80, 'Ends Here at (100,100) ');
end;
procedure TForm1.Button2Click(Sender
begin
Application.Terminate;
end;
end.
// Form as text
object Form1: TForm1
Left = 456
Top = 182
Width = 258
Height = 368
Caption = 'Ellipse'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -13
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 120
TextHeight = 16
object Button1: TButton
Left = 64
Top = 232
Width = 115
Height = 25
Caption = 'Show Ellipse'
TabOrder = 0
OnClick = Button1Click
end
object Button2: TButton
Left = 64
Top = 272
Width = 115
Height = 25
Caption = 'Close'
TabOrder = 1
OnClick = Button2Click
end
end
Goal :
From: ginsonic
Status: Waiting for Answer Points: 100
Email A Friend
I wish to follow an eliptic route.
for example I wish to go from a point(100, 100) and to draw an ellipse(with next dimensions for example
: 100 and 75), point by point, to my canvas.
ASKER
I will stop the topic now. Don't have enough time , sorry .
But not before accept the delphi3 comments , too.
Take a look for your points in questions list .
Thanks to all for support,
Nick
But not before accept the delphi3 comments , too.
Take a look for your points in questions list .
Thanks to all for support,
Nick