Link to home
Start Free TrialLog in
Avatar of ginsonic
ginsonicFlag for Romania

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 .
Avatar of kretzschmar
kretzschmar
Flag of Germany image

would be a nice qow, nick :-))
Avatar of ginsonic

ASKER

I thinking to this :)
ASKER CERTIFIED SOLUTION
Avatar of Cesario Lababidi
Cesario Lababidi
Flag of Germany image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
to ginsonic:
sorry, for delay but I was last 2 weeks offline.
You can contact me  C_Lababidi@hotmail.com

Best Regards

Cesario
Avatar of tongalite
tongalite

Hi Ginsonic
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).

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 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 :)
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 :)
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 :)
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*RESOLUTION);
      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*CosTable[i]);
      y:=Center.Y+Round(R.Y*SinTable[i]);
      if (xold<>x) or (yold<>y) then Where.Pixels[x,y]:=Where.Pen.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*CosTable[i]);
      y:=Center.Y+Round(R.Y*SinTable[i]);
      if i=0 then begin
        Where.Pixels[x,y]:=Where.Pen.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;
Add a sleep(1) and see what slow is it.
ginsonic,

Why should you put sleep(1) (inside DrawEllipse() loop I assume) ?
Add a sleep(10) and it's really slow ;-)
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?
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 .
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,50,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.

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