Recent

Author Topic: [Beginner] Creating a class to draw a circle  (Read 7882 times)

Skorn

  • Newbie
  • Posts: 2
[Beginner] Creating a class to draw a circle
« on: August 02, 2018, 06:07:26 pm »
Hello everybody,

i am quite a beginner with Lazarus. I want to understand the class concept and how to create a class. But somehow it doesn´t click. I can´t seem to find an example that shows what what is doing. So I am trying to create a Circles Class that draws circles.

As i know this can be done via Canvas.Ellipse.

In my current understanding i could create a Class DrawEllipse, that draws and deletes an Ellipse on the Canvas.

I would need a constructor, a draw method (procedure?) and a delete method (procedure?).

But I just can´t get my head around the draw procedure for example, from my understanding the procedure DrawEllipse.Draw() would be called on my normal form.
But how is the method implemented in the class?

procedure Draw(x1,y1,x2,y2:integer);
var
x1:integer =0;
y1:integer =0;
x2:integer =30;
y2: integer =30;

begin
Canvas.Ellipse(x1,y1,x2,y2);
end;

was my first idea, but it doesn´t work as Canvas is not known.

Thanks in advance,
Skorn

engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: [Beginner] Creating a class to draw a circle
« Reply #1 on: August 02, 2018, 06:17:40 pm »
but it doesn´t work as Canvas is not known.
How about passing form Canvas to the class in the constructor, and save it in the class?
Code: Pascal  [Select][+][-]
  1.   TDrawEllipse = class
  2.   private
  3.     FCanvas: TCanvas;
  4.   public
  5.     constructor Create(ACanvas: TCanvas);
  6. ...
  7.  

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: [Beginner] Creating a class to draw a circle
« Reply #2 on: August 02, 2018, 06:23:49 pm »
Hello everybody,

i am quite a beginner with Lazarus. I want to understand the class concept and how to create a class. But somehow it doesn´t click. I can´t seem to find an example that shows what what is doing. So I am trying to create a Circles Class that draws circles.

As i know this can be done via Canvas.Ellipse.

In my current understanding i could create a Class DrawEllipse, that draws and deletes an Ellipse on the Canvas.

I would need a constructor, a draw method (procedure?) and a delete method (procedure?).

But I just can´t get my head around the draw procedure for example, from my understanding the procedure DrawEllipse.Draw() would be called on my normal form.
But how is the method implemented in the class?

procedure Draw(x1,y1,x2,y2:integer);
var
x1:integer =0;
y1:integer =0;
x2:integer =30;
y2: integer =30;

begin
Canvas.Ellipse(x1,y1,x2,y2);
end;

was my first idea, but it doesn´t work as Canvas is not known.

Thanks in advance,
Skorn
you have no class declared only a simple procedure. A class is declared like this
Code: Pascal  [Select][+][-]
  1. type
  2.   TEllipse = class
  3.   public
  4.     Procedure Draw(cons X1,Y1,X2,Y2);
  5.   end;
  6.  
  7. interface
  8.  
  9. procedure TEllipse.Draw(cons X1,Y1,X2,Y2);
  10. var
  11. x1:integer =0;
  12. y1:integer =0;
  13. x2:integer =30;
  14. y2: integer =30;
  15.  
  16. begin
  17. Canvas.Ellipse(x1,y1,x2,y2);
  18. end;
  19.  
But that is not enough either
You need to base your class in existing control that supports a canvas. Assuming you are using lazarus and lcl those are known as visual controls and they all inherited from the class TControl after that it branches in to two main types of controls virtual and system controls. Virtual controls are all the controls that are based on TGraphicsControl and system controls are all based on TWinControl the difference between them is that virtual control use no system resources for their drawing surface they use the drawing surface of the parent which makes them a bit special as they can be transparent and they can not be drawn above system controls. System controls on the other hand are the ones that have their own "window" with its own drawing surface etc and system resources.

So to make a a really long story short the above code should look like
Code: Pascal  [Select][+][-]
  1. type
  2.   TEllipse = class(TGraphicControl)
  3.   public
  4.     Procedure Draw(cons X1,Y1,X2,Y2);
  5.   end;
  6.  
  7. interface
  8.  
  9. procedure TEllipse.Draw(cons X1,Y1,X2,Y2);
  10. var
  11. x1:integer =0;
  12. y1:integer =0;
  13. x2:integer =30;
  14. y2: integer =30;
  15.  
  16. begin
  17. Canvas.Ellipse(x1,y1,x2,y2);
  18. end;
  19.  
One last thing to remember for your circle to show at runtime you need to override the paint procedure of the inherited control (in the above TGraphicControl) which will make sure that the circle is painted every time a refresh is required.

There are a huge more info on the subject which I'd rather not cover in a forum post. I suggest you take the time and read the documentation for pascal and lcl to find out what the following terms mean, parent, owner, inheritance and most of all how they are accomplished.

Instead of a circle or other visual controls it would be preferable to start with something a bit easier eg a queue or a stack.
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

Handoko

  • Hero Member
  • *****
  • Posts: 5378
  • My goal: build my own game engine using Lazarus
Re: [Beginner] Creating a class to draw a circle
« Reply #3 on: August 02, 2018, 07:03:00 pm »
@Skorn

Hope this simple compilable code can give you some idea how to write components:

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, Forms, Controls, Graphics, ExtCtrls, StdCtrls;
  9.  
  10. type
  11.  
  12.   { TMyCircle }
  13.  
  14.   TMyCircle = class(TShape)
  15.   private
  16.     procedure Redraw;
  17.   public
  18.     PosX, PosY, Radius: Integer;
  19.     constructor Create(TheOwner: TComponent); override;
  20.     procedure ChangeRadius(NewRadius: Integer);
  21.     procedure ChangePos(NewX, NewY: Integer);
  22.   end;
  23.  
  24. type
  25.  
  26.   { TForm1 }
  27.  
  28.   TForm1 = class(TForm)
  29.     Button1: TButton;
  30.     Label1: TLabel;
  31.     Timer1: TTimer;
  32.     procedure Button1Click(Sender: TObject);
  33.     procedure FormKeyPress(Sender: TObject; var Key: char);
  34.     procedure Timer1Timer(Sender: TObject);
  35.   private
  36.     MyCircle: TMyCircle;
  37.   end;
  38.  
  39. var
  40.   Form1: TForm1;
  41.  
  42. implementation
  43.  
  44. {$R *.lfm}
  45.  
  46. { TForm1 }
  47.  
  48. procedure TForm1.Button1Click(Sender: TObject);
  49. begin
  50.   MyCircle        := TMyCircle.Create(Self);
  51.   Button1.Visible := False;
  52.   Label1.Visible  := True;
  53.   Timer1.Enabled  := True;
  54. end;
  55.  
  56. procedure TForm1.FormKeyPress(Sender: TObject; var Key: char);
  57. begin
  58.   if Button1.Visible then Exit;
  59.   case Key of
  60.     'a': MyCircle.ChangePos(MyCircle.PosX-10, MyCircle.PosY);
  61.     'd': MyCircle.ChangePos(MyCircle.PosX+10, MyCircle.PosY);
  62.     'w': MyCircle.ChangePos(MyCircle.PosX,    MyCircle.PosY-10);
  63.     's': MyCircle.ChangePos(MyCircle.PosX,    MyCircle.PosY+10);
  64.   end;
  65. end;
  66.  
  67. procedure TForm1.Timer1Timer(Sender: TObject);
  68. begin
  69.   MyCircle.ChangeRadius(MyCircle.Radius+1);
  70.   if (MyCircle.Radius > 80) then
  71.     MyCircle.ChangeRadius(10);
  72. end;
  73.  
  74. { TMyCircle }
  75.  
  76. procedure TMyCircle.Redraw;
  77. begin
  78.   Left   := PosX - Radius;
  79.   Top    := PosY - Radius;
  80.   Width  := Radius * 2;
  81.   Height := Radius * 2;
  82. end;
  83.  
  84. constructor TMyCircle.Create(TheOwner: TComponent);
  85. begin
  86.   inherited;
  87.   PosX   := 100;
  88.   PosY   := 100;
  89.   Radius := 50;
  90.   Parent := (TheOwner as TWinControl);
  91.   Redraw;
  92. end;
  93.  
  94. procedure TMyCircle.ChangeRadius(NewRadius: Integer);
  95. begin
  96.   Radius := NewRadius;
  97.   Redraw;
  98. end;
  99.  
  100. procedure TMyCircle.ChangePos(NewX, NewY: Integer);
  101. begin
  102.   PosX := NewX;
  103.   PosY := NewY;
  104.   Redraw;
  105. end;
  106.  
  107. end.

Direct handling the canvas isn't easy, you need to know lots of things to make it works correctly as you want. Like what @tazz already said it will be easier to write class that inherited from other class. In my example, I use TShape.

You can download the test.zip for testing.

--- edit ---
My mistake, please add this below to line #87:
Shape  := stCircle;
« Last Edit: August 02, 2018, 07:33:02 pm by Handoko »

440bx

  • Hero Member
  • *****
  • Posts: 4752
Re: [Beginner] Creating a class to draw a circle
« Reply #4 on: August 02, 2018, 07:17:18 pm »
You can download the test.zip for testing.

Handoko, nice example but, I don't get a circle.  I only get a square, no circle. 

Also, just from a usability viewpoint.  When the form is maximized then restored to its previous size, the square/circle gets lost.    Feature request: ;)  move the circle back into the center of the form when its size is restored. :)

I hope you realize that this is "tongue in cheek". 

Thanks.
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

Handoko

  • Hero Member
  • *****
  • Posts: 5378
  • My goal: build my own game engine using Lazarus
Re: [Beginner] Creating a class to draw a circle
« Reply #5 on: August 02, 2018, 07:18:25 pm »
Oops, I didn't test it properly. :-[

Handoko

  • Hero Member
  • *****
  • Posts: 5378
  • My goal: build my own game engine using Lazarus
Re: [Beginner] Creating a class to draw a circle
« Reply #6 on: August 02, 2018, 07:31:04 pm »
This one is better:

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, Forms, Controls, Graphics, ExtCtrls, StdCtrls;
  9.  
  10. type
  11.  
  12.   { TMyCircle }
  13.  
  14.   TMyCircle = class(TShape)
  15.   private
  16.     procedure Redraw;
  17.   public
  18.     PosX, PosY, Radius: Integer;
  19.     constructor Create(TheOwner: TComponent); override;
  20.     procedure ChangeRadius(NewRadius: Integer);
  21.     procedure ChangePos(NewX, NewY: Integer);
  22.   end;
  23.  
  24. type
  25.  
  26.   { TForm1 }
  27.  
  28.   TForm1 = class(TForm)
  29.     Button1: TButton;
  30.     Label1: TLabel;
  31.     Timer1: TTimer;
  32.     procedure Button1Click(Sender: TObject);
  33.     procedure FormKeyPress(Sender: TObject; var Key: char);
  34.     procedure FormResize(Sender: TObject);
  35.     procedure Timer1Timer(Sender: TObject);
  36.   private
  37.     MyCircle: TMyCircle;
  38.   end;
  39.  
  40. var
  41.   Form1: TForm1;
  42.  
  43. implementation
  44.  
  45. {$R *.lfm}
  46.  
  47. { TForm1 }
  48.  
  49. procedure TForm1.Button1Click(Sender: TObject);
  50. begin
  51.   MyCircle        := TMyCircle.Create(Self);
  52.   Button1.Visible := False;
  53.   Label1.Visible  := True;
  54.   Timer1.Enabled  := True;
  55. end;
  56.  
  57. procedure TForm1.FormKeyPress(Sender: TObject; var Key: char);
  58. begin
  59.   if Button1.Visible then Exit;
  60.   case Key of
  61.     'a': MyCircle.ChangePos(MyCircle.PosX-10, MyCircle.PosY);
  62.     'd': MyCircle.ChangePos(MyCircle.PosX+10, MyCircle.PosY);
  63.     'w': MyCircle.ChangePos(MyCircle.PosX,    MyCircle.PosY-10);
  64.     's': MyCircle.ChangePos(MyCircle.PosX,    MyCircle.PosY+10);
  65.   end;
  66. end;
  67.  
  68. procedure TForm1.FormResize(Sender: TObject);
  69. begin
  70.   if not(Assigned(MyCircle)) then Exit;
  71.   MyCircle.ChangePos(Width div 2, Height div 2);
  72. end;
  73.  
  74. procedure TForm1.Timer1Timer(Sender: TObject);
  75. begin
  76.   MyCircle.ChangeRadius(MyCircle.Radius+1);
  77.   if (MyCircle.Radius > 80) then
  78.     MyCircle.ChangeRadius(10);
  79. end;
  80.  
  81. { TMyCircle }
  82.  
  83. procedure TMyCircle.Redraw;
  84. begin
  85.   Left   := PosX - Radius;
  86.   Top    := PosY - Radius;
  87.   Width  := Radius * 2;
  88.   Height := Radius * 2;
  89. end;
  90.  
  91. constructor TMyCircle.Create(TheOwner: TComponent);
  92. begin
  93.   inherited;
  94.   Shape  := stCircle;
  95.   PosX   := 100;
  96.   PosY   := 100;
  97.   Radius := 50;
  98.   Parent := (TheOwner as TWinControl);
  99.   Redraw;
  100. end;
  101.  
  102. procedure TMyCircle.ChangeRadius(NewRadius: Integer);
  103. begin
  104.   Radius := NewRadius;
  105.   Redraw;
  106. end;
  107.  
  108. procedure TMyCircle.ChangePos(NewX, NewY: Integer);
  109. begin
  110.   PosX := NewX;
  111.   PosY := NewY;
  112.   Redraw;
  113. end;
  114.  
  115. end.

~~~ edit ~~~

Add this line below between line #64 and line #65 if you want to have press c to center form:
   'c': MyCircle.ChangePos(Width div 2, Height div 2);
« Last Edit: March 06, 2020, 05:44:59 pm by Handoko »

howardpc

  • Hero Member
  • *****
  • Posts: 4144
Re: [Beginner] Creating a class to draw a circle
« Reply #7 on: August 02, 2018, 07:53:58 pm »
... I am trying to create a Circles Class that draws circles.
...
As i know this can be done via Canvas.Ellipse.
The first question to ask is
  Where is the circle to be drawn?
This is the question engkin was suggesting you consider.
Because the FCL/LCL already has a class that draws circles particularly well, TCanvas, as you stated.
Is your class to draw screen circles on bitmaps, or on controls, or directly on a form? Or is it to draw printed output?
In each case the "Where?" question will mean identifying a different canvas to draw on. There is no single right answer.
But once you identify the needed canvas, you simply use the Ellipse method of the canvas. Where controls don't expose a canvas, it is harder.
A caveat is that for drawing on controls and forms most OSs restrict you to drawing during the Paint event (and even if you can get away with drawing outside OnPaint, doing so will mean your code is not cross-platform).

Skorn

  • Newbie
  • Posts: 2
Re: [Beginner] Creating a class to draw a circle
« Reply #8 on: August 02, 2018, 08:17:56 pm »
Thank you so much for all the helpful answers so far!

I will work through them and try to understand it.

Handoko

  • Hero Member
  • *****
  • Posts: 5378
  • My goal: build my own game engine using Lazarus
Re: [Beginner] Creating a class to draw a circle
« Reply #9 on: August 02, 2018, 08:26:53 pm »
@howardpc

:) Wow ... what you said is true. But it will scare the TS. He's just a beginner now trying to learn how to write a class (or maybe component). I don't think he care about cross platform, printer output and other issues. What's in his mind is how to take the first step in writing his own class.

@Skorn

Writing a class is not hard but to write a good class is not easy, you have to plan it properly just as what @howardpc said. Practice, read and practice are the things you can do to improve your skill. I found learning from reading others' codes is the best thing to improve my skill to in component writing.

Here are some materials that I think maybe useful for you:
https://www.tutorialspoint.com/pascal/pascal_classes.htm
http://wiki.freepascal.org/Class
http://wiki.freepascal.org/How_To_Write_Lazarus_Component

Leledumbo

  • Hero Member
  • *****
  • Posts: 8777
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: [Beginner] Creating a class to draw a circle
« Reply #10 on: August 02, 2018, 10:02:32 pm »
In my current understanding i could create a Class DrawEllipse, that draws and deletes an Ellipse on the Canvas.

I would need a constructor, a draw method (procedure?) and a delete method (procedure?).
Welcome to OOP. As in procedural programming, there are possibly multiple solutions to a problem. If in procedural you try different algorithmic approach, in OOP you try different roles approach. The key question is: who should do what? Remember, who = noun = class, what = verb = method. Think again, does DrawEllipse fit that? If doesn't, then what's better? If does, explain how. Once done with the class name, now you define the behavior. How could the class draw an ellipse? Does it hold a reference to a canvas? If yes, assigned through property or passed in constructor? Or does its Draw method has a parameter of type TCanvas? Or something else? I think that should be enough for a starting point. Paradigm shift needs to be done rather slowly.

mangakissa

  • Hero Member
  • *****
  • Posts: 1131
Re: [Beginner] Creating a class to draw a circle
« Reply #11 on: August 03, 2018, 10:04:18 am »
Why making it difficult to draw circles. Take an adressbook to learn the basics of classes. If you managed that the circles will be a step further.
Code: Pascal  [Select][+][-]
  1. interface
  2.  
  3. uses sysutils;
  4.  
  5. TPerson = class
  6. private
  7.   fFirstname : string;
  8.   fLastname  : string;
  9.   fAddress   : string;
  10.   fZipcode   : string;
  11.   fCity      : string;
  12. public
  13.   function Fullname : string;
  14.  
  15.   property Firstname : string read fFirstname write fFirstname;
  16.   property Lastname  : string read fLastname write fLastname;
  17.   property Address   : string read fAddress write fAddress;
  18.   property Zipcode   : string read fZipcode write fZipcode;
  19.   property City      : string read fCity write fCity;
  20. end;
  21.  
  22. implementation
  23.  
  24. function TPerson.Fullname : string;
  25. begin
  26.   result := format(%s %s,[fFirstname, fLastname])
  27. end;
  28.  
Lazarus 2.06 (64b) / FPC 3.0.4 / Windows 10
stucked on Delphi 10.3.1

 

TinyPortal © 2005-2018