### Bookstore

 Computer Math and Games in Pascal (preview) Lazarus Handbook

### Author Topic: [Beginner] Creating a class to draw a circle  (Read 6824 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.

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.

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: 5106
• 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
19.     constructor Create(TheOwner: TComponent); override;
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
70.   if (MyCircle.Radius > 80) then
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;
90.   Parent := (TheOwner as TWinControl);
91.   Redraw;
92. end;
93.
95. begin
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.

--- edit ---
Shape  := stCircle;
« Last Edit: August 02, 2018, 07:33:02 pm by Handoko »

#### 440bx

• Hero Member
• Posts: 3875
##### Re: [Beginner] Creating a class to draw a circle
« Reply #4 on: August 02, 2018, 07:17:18 pm »

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.1 both fixes) on Windows 7 SP1 64bit.

#### Handoko

• Hero Member
• Posts: 5106
• 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: 5106
• 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
19.     constructor Create(TheOwner: TComponent); override;
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
77.   if (MyCircle.Radius > 80) then
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;
98.   Parent := (TheOwner as TWinControl);
99.   Redraw;
100. end;
101.
103. begin
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 »

I will work through them and try to understand it.

#### Handoko

• Hero Member
• Posts: 5106
• 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: 8741
• 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;
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;
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