Recent

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

Boleeman

  • Sr. Member
  • ****
  • Posts: 433
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: 2058
  • Fifty shades of code.
    • Delphi & FreePascal
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 xm by KodeZwerg »

Handoko

  • Hero Member
  • *****
  • Posts: 5152
  • 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: 2058
  • Fifty shades of code.
    • Delphi & FreePascal
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 xm by KodeZwerg »

Boleeman

  • Sr. Member
  • ****
  • Posts: 433
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: 2058
  • Fifty shades of code.
    • Delphi & FreePascal
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 xm by KodeZwerg »

Boleeman

  • Sr. Member
  • ****
  • Posts: 433
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: 2058
  • Fifty shades of code.
    • Delphi & FreePascal
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 xm by KodeZwerg »

Boleeman

  • Sr. Member
  • ****
  • Posts: 433
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: 2058
  • Fifty shades of code.
    • Delphi & FreePascal
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 xm by KodeZwerg »

Boleeman

  • Sr. Member
  • ****
  • Posts: 433
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: 2058
  • Fifty shades of code.
    • Delphi & FreePascal
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 xm by KodeZwerg »

Boleeman

  • Sr. Member
  • ****
  • Posts: 433
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

  • Sr. Member
  • ****
  • Posts: 433
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: 2058
  • Fifty shades of code.
    • Delphi & FreePascal
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  :P
« Last Edit: Tomorrow at 31:76:97 xm by KodeZwerg »

 

TinyPortal © 2005-2018