### Bookstore

 Computer Math and Games in Pascal (preview) Lazarus Handbook

### Author Topic: Make a Dynamic Shape Array and make an Array of That  (Read 1805 times)

#### Boleeman

• Jr. Member
• Posts: 85
##### Make a Dynamic Shape Array and make an Array of That
« on: January 25, 2023, 03:40:02 am »
Hi All.

I was wondering if someone can explain how to make An ARRAY of a dynamic ARRAY of clickable shapes (The squares are shapes and not just drawn lines to make a grid), where say you set the size to five and it makes a 5x5 grid on a scrollable container. Then that array is copied/repeated say 3 times. Sort of like An Array of a Shape Array. In VB6 I made something like that but could not make the initial grid variable in size (the pattern either repeats every 3x3 or every 6x6 grid size)..

The following Pascal code creates the initial grid on a scrollbox, but how to get a repeated Array of that and have mouse clickable events to change the colour of a square on the initial grid.

Thanks in Advance.

Code: Pascal  [Select][+][-]
1. for j := 1 to numberRows do
2.   begin
3.       for i := 1 to numberCols do
4.       begin
5.               shapeArray[(j-1)*numberRows+i]:= TShape.Create(ScrollBox1);
6.               shapeArray[(j-1)*numberRows+i].Shape:=stRectangle;
7.               shapeArray[(j-1)*numberRows+i].Width:=40;
8.               shapeArray[(j-1)*numberRows+i].Height:=40;
9.               shapeArray[(j-1)*numberRows+i].Left:=i*40-1;
10.               shapeArray[(j-1)*numberRows+i].Top:=(j-1)*40-1;
11.               shapeArray[(j-1)*numberRows+i].Brush.Color:=clRed;
12.               shapeArray[(j-1)*numberRows+i].Pen.Color:=clBlack;
13.        end;
14.   end;

There is a thread on this forumn that talks about using Arrays OR a TList to do the initial array. What would be the advantages of using a TList?

« Last Edit: January 25, 2023, 04:52:07 am by Boleeman »

#### KodeZwerg

• Hero Member
• Posts: 867
• Fifty shades of code.
##### Re: Make a Dynamic Shape Array and make an Array of That
« Reply #1 on: January 25, 2023, 05:15:08 am »
I havent understand yet all, does this help you?
That would be my basics for one block of shapes, each shape with mouse handler.
Code: Pascal  [Select][+][-]
1. unit Unit1;
2.
3. {\$mode objfpc}{\$H+}
4.
5. interface
6.
7. uses
8.   Classes , SysUtils , Forms , Controls , Graphics , Dialogs , ExtCtrls;
9.
10. type
11.   TShapes = array of array of TShape;
12.
13.   { TForm1 }
14.
15.   TForm1 = class(TForm)
16.     procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
17.     procedure FormCreate(Sender: TObject);
18.   strict private
19.     FShapes: TShapes;
20.     FRowMax: Integer;
21.     FColMax: Integer;
22.   private
23.     procedure GenerateShapes;
24.     procedure ShapeEvent(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X , Y: Integer);
25.   public
26.
27.   end;
28.
29. var
30.   Form1: TForm1;
31.
32. implementation
33.
34. {\$R *.lfm}
35.
36. { TForm1 }
37.
38. procedure TForm1.FormCreate(Sender: TObject);
39. begin
40.   Randomize;
41.   FRowMax := 5;
42.   FColMax := 5;
43.   FShapes := nil;
44.   GenerateShapes;
45. end;
46.
47. procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
48. var
49.   rows, cols: Integer;
50. begin
51.   for rows := 0 to Pred(FRowMax) do
52.     begin
53.       for cols := 0 to Pred(FColMax) do
54.         begin
55.           FShapes[rows, cols].Free;
56.         end;
57.     end;
58.   CanClose := True;
59. end;
60.
61. procedure TForm1.GenerateShapes;
62. var
63.   rows, cols: Integer;
64. begin
65.   SetLength(FShapes, FRowMax, FColMax);
66.   for rows := 0 to Pred(FRowMax) do
67.     begin
68.       for cols := 0 to Pred(FColMax) do
69.         begin
70.           FShapes[rows, cols] := TShape.Create(Self);
71.           try
72.             FShapes[rows, cols].Parent      := Self;
73.             FShapes[rows, cols].OnMouseUp   := @ShapeEvent;
74.             FShapes[rows, cols].Shape       := stRectangle;
75.             FShapes[rows, cols].Width       := 40;
76.             FShapes[rows, cols].Height      := 40;
77.             FShapes[rows, cols].Left        := cols * 40 - 1;
78.             FShapes[rows, cols].Top         := (rows - 1) * 40 - 1;
79.             FShapes[rows, cols].Brush.Color := clRed;
80.             FShapes[rows, cols].Pen.Color   := clBlack;
81.           finally
82.             FShapes[rows, cols].Visible := True;
83.           end;
84.         end;
85.     end;
86. end;
87.
88. procedure TForm1.ShapeEvent(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X , Y: Integer);
89. begin
90.   (Sender as TShape).Brush.Color := Random(High(TColor));
91. end;
92.
93. end.
You can modify "GenerateShapes" to have arguments like TShapes/top/left etc to have as many blocks as you want from such an array.
« Last Edit: Tomorrow at 31:76:97 by KodeZwerg »

#### Handoko

• Hero Member
• Posts: 4764
• My goal: build my own game engine using Lazarus
##### Re: Make a Dynamic Shape Array and make an Array of That
« Reply #2 on: January 25, 2023, 05:16:00 am »
... how to get a repeated Array of that and have mouse clickable events to change the colour of a square on the initial grid.

You can do it using easy way or hard way. The easy one is to use an array of TShape. But if performance is important, you should go with the lightweight solution, which is the hard way.

Because the easy way is easy, all the explanation below if for the hard way solution.

Basically you need 2 things:
1. Store the information of all the clickable regions
2. Check if the mouse click any of the regions

The information you need to store is the X1, Y1, X2, Y2 of the clickable position. Alternatively you can store X, Y, W, H. You may want to consider to use TRect:
https://lazarus-ccr.sourceforge.io/docs/rtl/types/trect.html

For checking if mouse click on any of the region, you can use OnClick or OnMouseDown events. To read the mouse position you use CursorPos. But then you need to do the conversion make it relative to the client coordinates.

Read more:
https://lazarus-ccr.sourceforge.io/docs/lcl/controls/tmouse.cursorpos.html
https://lazarus-ccr.sourceforge.io/docs/lcl/lclintf/screentoclient.html

You can learn from these examples:
https://forum.lazarus.freepascal.org/index.php/topic,57003.msg423728.html#msg423728
https://forum.lazarus.freepascal.org/index.php/topic,48291.msg348084.html#msg348084
https://forum.lazarus.freepascal.org/index.php/topic,41036.msg284131.html#msg284131

There is a thread on this forumn that talks about using Arrays OR a TList to do the initial array. What would be the advantages of using a TList?

For beginners, just pick the one most convenient to you.

But if you interested, here is how I choose between array and TList.

For something well-arranged like grids, I prefer array or dynamic array. Especially if you can calculate which cell is clicked because the grid has fix column widths and row heights.

TList is good for items that frequently need to be inserted and removed, because TList has the methods for doing insertion, deletion and sorting. But TList is slower than array as it is not lightweight, it perform a lot things behind the scene.

You can still use array to store items that frequently need to be inserted and removed. But you have to write the code for performing the insertion and deletion, that is relatively not easy. For this case, you should simply use TList. Because TList is properly written and tightly optimized, it should perform faster than the code written from non-professionals.

I done a test for my game engine, using TList vs array to store the game items than spawn on the screen runtime. When using array, I properly optimized the code. The result is, there were cases TList run faster than dynamic array. But on some cases dynamic array run faster. The performance difference, if I remember correctly is less than 1%.

Nowadays computer run very fast. You may not notice performance different using TList or array, unless you're doing some gigantic scientific calculations.
« Last Edit: January 25, 2023, 05:23:23 am by Handoko »

#### KodeZwerg

• Hero Member
• Posts: 867
• Fifty shades of code.
##### Re: Make a Dynamic Shape Array and make an Array of That
« Reply #3 on: January 25, 2023, 05:23:19 am »
1. Store the information of all the clickable regions
2. Check if the mouse click any of the regions

The information you need to store is the X1, Y1, X2, Y2 of the clickable position. Alternatively you can store X, Y, W, H. You may want to consider to use TRect:
https://lazarus-ccr.sourceforge.io/docs/rtl/types/trect.html

For checking if mouse click on any of the region, you can use OnClick or OnMouseDown events. To read the mouse position you use CursorPos. But then you need to do the conversion make it relative to the client coordinates.
Please take a look on my source that not need anything what you wrote but works individual for each shape.
« Last Edit: Tomorrow at 31:76:97 by KodeZwerg »

#### Boleeman

• Jr. Member
• Posts: 85
##### Re: Make a Dynamic Shape Array and make an Array of That
« Reply #4 on: January 25, 2023, 05:25:14 am »
Thanks KodeZwerg and Handoko for replying.

Handoko :
Alternatively you can store X, Y, W, H. You may want to consider to use TRect"" saw that in some threads but I wanted to trigger with a shape click and not a pixel based region in rectangle click.

KodeZwerg:
Yes sort of like that but ...

now make a copy of that array 3 times across and 3 times down

There is an array example using buttons (just in one columns with events. Wanted to do that with shapes/images or some self contained control.

Code: Pascal  [Select][+][-]
1. unit Unit1;
2.
3. {\$mode objfpc}{\$H+}
4.
5. interface
6.
7. uses
8.   SysUtils, Forms, Controls, Graphics, StdCtrls, Spin, ExtCtrls;
9.
10. type
11.
12.   TArrayofButtons = array of TButton;
13.
14.   { TForm1 }
15.
16.   TForm1 = class(TForm)
17.     Panel1: TPanel;
18.     ScrollBox1: TScrollBox;
19.     procedure FormCreate(Sender: TObject);
20.   private
21.     FButtonIndexSpinEdit: TSpinEdit;
22.     FMainButtonArray: TArrayofButtons;
23.     procedure FButtonIndexSpinEditChange(Sender: TObject);
24.     procedure SetButtonFontStyle(aButtonIndex: Integer; aButtonArray: TArrayofButtons);
25.   end;
26.
27. var
28.   Form1: TForm1;
29.
30. implementation
31.
32. {\$R *.lfm}
33.
34. procedure TForm1.FormCreate(Sender: TObject);
35. const
36.   ButtonArrayLength = 30; // change to suit
37. var
38.   i: Integer;
39. begin
40.   SetLength(FMainButtonArray, ButtonArrayLength);
41.   for i:=0 to High(FMainButtonArray) do begin
42.     FMainButtonArray[i]:=TButton.Create(ScrollBox1);
43.     with FMainButtonArray[i] do begin
44.       Caption:=Format('Button#%d',[i]);
45.       Left:=300;
46.       Top:=10 + i*30;
47.       Parent:=Self;
48.     end;
49.   end;
50.   FButtonIndexSpinEdit:=TSpinEdit.Create(Self);
51.   with FButtonIndexSpinEdit do begin
52.     MaxValue:=ButtonArrayLength - 1;
53.     Left:=30;
54.     Top:=10;
55.     OnChange:=@FButtonIndexSpinEditChange;
56.     Parent:=Self;
57.   end;
58. end;
59.
60. procedure TForm1.FButtonIndexSpinEditChange(Sender: TObject);
61. begin
62.   SetButtonFontStyle(FButtonIndexSpinEdit.Value, FMainButtonArray);
63. end;
64.
65. procedure TForm1.SetButtonFontStyle(aButtonIndex: Integer; aButtonArray: TArrayofButtons);
66. // note you don't need the second parameter, since you can refer to FMainButtonArray directly, but I left it in
67. // the code since your program may need it.
68. var
69.   b: TButton;
70. begin
71.   // clear the Font.Style of all buttons in the array
72.   for b in aButtonArray do
73.     b.Font.Style:=[];
74.
75.   // set Font.Style of the indexed button
76.   if (aButtonIndex >= 0) and (aButtonIndex < Length(aButtonArray)) then
77.     aButtonArray[aButtonIndex].Font.Style:=
78.       aButtonArray[aButtonIndex].Font.Style + [fsBold, fsItalic];
79. end;
80.
81. end.

Sorry put in wrong code before.
« Last Edit: January 25, 2023, 06:16:17 am by Boleeman »

#### KodeZwerg

• Hero Member
• Posts: 867
• Fifty shades of code.
##### Re: Make a Dynamic Shape Array and make an Array of That
« Reply #5 on: January 25, 2023, 05:34:25 am »
Thanks KodeZwerg and Handoko for replying.
You are welcome.

now make a copy of that array 3 times across and 3 times down
You can modify "GenerateShapes" to have arguments like TShapes/top/left etc to have as many blocks as you want from such an array.
Was I not clear enough? Simple create more than one FShapes and modify "GenerateShapes"
« Last Edit: Tomorrow at 31:76:97 by KodeZwerg »

#### Boleeman

• Jr. Member
• Posts: 85
##### Re: Make a Dynamic Shape Array and make an Array of That
« Reply #6 on: January 25, 2023, 05:36:14 am »
KodeZwerg,

After setting number of rows and columns I wanted (like 3x3) copy that 3x3 twice more left so have 9 squares across?

Sort of like repeat that array procedure translating 4 blocks and 7 blocks left, but the new blocks share the same colour change click event.

Sorry my mouse keeps on siezing up.

Also in your code sample
FRowMax := 5;
FColMax := 5;

but I can see 5 squares across and only 4 squares down.
« Last Edit: January 25, 2023, 05:57:11 am by Boleeman »

#### KodeZwerg

• Hero Member
• Posts: 867
• Fifty shades of code.
##### Re: Make a Dynamic Shape Array and make an Array of That
« Reply #7 on: January 25, 2023, 05:56:47 am »
Solution: do NOT copy, do CREATE. At least I assume that you literal copy that array, so ofc they share same stuff.
« Last Edit: Tomorrow at 31:76:97 by KodeZwerg »

#### Boleeman

• Jr. Member
• Posts: 85
##### Re: Make a Dynamic Shape Array and make an Array of That
« Reply #8 on: January 25, 2023, 06:02:24 am »
So clicking on 1st square changes colours on square 6 and square 11 across.

So you are saying use create to make those squares, but how do they share the same click event?

Thats is what I am confused about.

Actually Now I understand just add a property where the first square is to begin change that position when you create the other ones. But how do they share the same click event?
« Last Edit: January 25, 2023, 06:17:14 am by Boleeman »

#### KodeZwerg

• Hero Member
• Posts: 867
• Fifty shades of code.
##### Re: Make a Dynamic Shape Array and make an Array of That
« Reply #9 on: January 25, 2023, 06:25:55 am »
Very alpha status, but since you keep asking, thats how I would do.
Code: Pascal  [Select][+][-]
1. unit Unit1;
2.
3. {\$mode objfpc}{\$H+}
4.
5. interface
6.
7. uses
8.   Classes , SysUtils , Forms , Controls , Graphics , Dialogs , ExtCtrls;
9.
10. type
11.   TBlock = array of array of TShape;
12.   TBlocks = array of array of TBlock;
13.
14.   { TForm1 }
15.
16.   TForm1 = class(TForm)
17.     procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
18.     procedure FormCreate(Sender: TObject);
19.   strict private
20.     FBlocks: TBlocks;
21.     FBlockRow: Integer;
22.     FBlockCol: Integer;
23.     FBlocksRow: Integer;
24.     FBlocksCol: Integer;
25.   private
26.     procedure GenerateBlocks;
27.     procedure ShapeEvent(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X , Y: Integer);
28.   public
29.
30.   end;
31.
32. var
33.   Form1: TForm1;
34.
35. implementation
36.
37. {\$R *.lfm}
38.
39. { TForm1 }
40.
41. procedure TForm1.FormCreate(Sender: TObject);
42. begin
43.   Randomize;
44.   FBlockRow := 5;
45.   FBlockCol := 5;
46.   FBlocksRow := 3;
47.   FBlocksCol := 3;
48.   FBlocks := nil;
49.   GenerateBlocks;
50. end;
51.
52. procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
53. var
54.   Brows, Bcols, Srows, Scols: Integer;
55. begin
56.   for Brows := 0 to Pred(FBlocksRow) do
57.   begin
58.     for Bcols := 0 to Pred(FBlocksCol) do
59.     begin
60.       for Srows := 0 to Pred(FBlockRow) do
61.         begin
62.           for Scols := 0 to Pred(FBlockCol) do
63.             begin
64.               FBlocks[Brows, Bcols, Srows, Scols].Free;
65.             end;
66.         end;
67.     end;
68.   end;
69.   CanClose := True;
70. end;
71.
72. procedure TForm1.GenerateBlocks;
73. var
74.   Brows, Bcols, Srows, Scols: Integer;
75.   Lleft, Ltop: Integer;
76. begin
77.   SetLength(FBlocks, FBlocksRow, FBlocksCol, FBlockRow, FBlockCol);
78.   for Brows := 0 to Pred(FBlocksRow) do
79.     begin
80.       for Bcols := 0 to Pred(FBlocksCol) do
81.         begin
82.           for Srows := 0 to Pred(FBlockRow) do
83.             begin
84.               for Scols := 0 to Pred(FBlockCol) do
85.                 begin
86.                   FBlocks[Brows, Bcols, Srows, Scols] := TShape.Create(Self);
87.                   try
88.                     FBlocks[Brows, Bcols, Srows, Scols].Parent      := Self;
89.                     FBlocks[Brows, Bcols, Srows, Scols].OnMouseUp   := @ShapeEvent;
90.                     FBlocks[Brows, Bcols, Srows, Scols].Shape       := stRectangle;
91.                     FBlocks[Brows, Bcols, Srows, Scols].Width       := 40;
92.                     FBlocks[Brows, Bcols, Srows, Scols].Height      := 40;
93.                     Lleft := Bcols * (5 + (FBlockCol * 40));
94.                     FBlocks[Brows, Bcols, Srows, Scols].Left        := Lleft + Pred(Scols * 40);
95.                     Ltop := Brows * (5 + (FBlockRow * 40));
96.                     FBlocks[Brows, Bcols, Srows, Scols].Top         := Ltop + Pred(Pred(Srows) * 40);
97.                     FBlocks[Brows, Bcols, Srows, Scols].Brush.Color := clRed;
98.                     FBlocks[Brows, Bcols, Srows, Scols].Pen.Color   := clBlack;
99.                   finally
100.                     FBlocks[Brows, Bcols, Srows, Scols].Visible := True;
101.                   end;
102.                 end;
103.             end;
104.         end;
105.     end;
106. end;
107.
108. procedure TForm1.ShapeEvent(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X , Y: Integer);
109. begin
110.   (Sender as TShape).Brush.Color := Random(High(TColor));
111. end;
112.
113. end.
« Last Edit: Tomorrow at 31:76:97 by KodeZwerg »

#### Boleeman

• Jr. Member
• Posts: 85
##### Re: Make a Dynamic Shape Array and make an Array of That
« Reply #10 on: January 25, 2023, 06:35:44 am »
OK, I see.

For some reason the 1st row is missing.

#### KodeZwerg

• Hero Member
• Posts: 867
• Fifty shades of code.
##### Re: Make a Dynamic Shape Array and make an Array of That
« Reply #11 on: January 25, 2023, 06:39:20 am »
OK, I see.

For some reason the 1st row is missing.
Can you express yourself more understandable?
You wanted 3x3 where each hold 5x5, and that is what I gave as you can see on my image.
Clicks working for each as individual.

So what is not working??
« Last Edit: Tomorrow at 31:76:97 by KodeZwerg »

#### Boleeman

• Jr. Member
• Posts: 85
##### Re: Make a Dynamic Shape Array and make an Array of That
« Reply #12 on: January 25, 2023, 06:49:15 am »
Sorry.

I did a screen shot to explain what I mean.

.. that the 1st row is missing (only 4 squares and not 5)

#### Boleeman

• Jr. Member
• Posts: 85
##### Re: Make a Dynamic Shape Array and make an Array of That
« Reply #13 on: January 25, 2023, 07:05:22 am »
Here is my VB6 Tesselator, if anyone wants to play around with it to see how it works.
« Last Edit: January 25, 2023, 08:26:54 am by Boleeman »

#### KodeZwerg

• Hero Member
• Posts: 867
• Fifty shades of code.
##### Re: Make a Dynamic Shape Array and make an Array of That
« Reply #14 on: January 25, 2023, 07:06:12 am »
Sorry.

I did a screen shot to explain what I mean.

.. that the 1st row is missing (only 4 squares and not 5)
Ohhh  Now I see... dunno... I am too tired right now to hunt down error, somewhere I made a small mistake but can not locate  :'(
Thats why I wrote alpha status
« Last Edit: Tomorrow at 31:76:97 by KodeZwerg »