Lazarus

Free Pascal => Beginners => Topic started by: Boleeman on January 25, 2023, 03:40:02 am

Title: Make a Dynamic Shape Array and make an Array of That
Post by: Boleeman 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?



Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: KodeZwerg 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.
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: Handoko 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 (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/controls/tmouse.cursorpos.html)
https://lazarus-ccr.sourceforge.io/docs/lcl/lclintf/screentoclient.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,57003.msg423728.html#msg423728)
https://forum.lazarus.freepascal.org/index.php/topic,48291.msg348084.html#msg348084 (https://forum.lazarus.freepascal.org/index.php/topic,48291.msg348084.html#msg348084)
https://forum.lazarus.freepascal.org/index.php/topic,41036.msg284131.html#msg284131 (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.
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: KodeZwerg 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 (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.
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: Boleeman 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.
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: KodeZwerg 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"
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: Boleeman 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.
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: KodeZwerg 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.
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: Boleeman 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?
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: KodeZwerg 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.
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: Boleeman on January 25, 2023, 06:35:44 am
OK, I see.

For some reason the 1st row is missing.
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: KodeZwerg 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??
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: Boleeman 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)
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: Boleeman 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.
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: KodeZwerg 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
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: Boleeman on January 25, 2023, 07:10:07 am
That's OK. Over here it is 4.56 pm afternoon, so over where you live must be different.

As ALWAYS, Thanks for helping out KodeZwerg.

I appreciate all your great help. Take care for now.
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: TRon on January 25, 2023, 07:10:43 am
@Boleeman:
If you have issues with loops and using wrong numbers but have no idea then you could use the debugger. That allows you to step through your code at runtime. The debugger allows you to see the contents of variables and you can even pace watches on variables.
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: Boleeman on January 25, 2023, 07:14:27 am
Yes I will try that. I looked at KodeZwerg's code but could not find a fault.

The difference in squares occurs at the very start (row1), so it must be some sort of initialization qwerk.


Thanks to everyone for their assistance. Time to eat supper.

TRon, just turned out that the initial position needed to be shifted the other way!
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: TRon on January 25, 2023, 07:17:38 am
The difference in squares occurs at the very start (row1), so it must be some sort of initialization qwerk.
Might be, might be not  :P

Instead of just telling you, try to toy with the debugger. You will be able to learn a lot from that. e.g. this is meant to inspire you to do an exercise (booh ! boring ! we hate learning !  ;) ) .
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: KodeZwerg on January 25, 2023, 07:18:27 am
Replace my inner loop with this and you get whats in attachment.
Code: Pascal  [Select][+][-]
  1.                     Lleft                                           := Bcols * (5 + (FBlockCol * 40));
  2.                     Ltop                                            := Brows * (5 + (FBlockRow * 40));
  3.                     FBlocks[Brows, Bcols, Srows, Scols].Parent      := Self;
  4.                     FBlocks[Brows, Bcols, Srows, Scols].OnMouseUp   := @ShapeEvent;
  5.                     FBlocks[Brows, Bcols, Srows, Scols].Shape       := stRectangle;
  6.                     FBlocks[Brows, Bcols, Srows, Scols].Width       := 40;
  7.                     FBlocks[Brows, Bcols, Srows, Scols].Height      := 40;
  8.                     FBlocks[Brows, Bcols, Srows, Scols].Left        := Lleft + Pred(Scols * 40) + 5;
  9.                     FBlocks[Brows, Bcols, Srows, Scols].Top         := Ltop + Pred(Srows * 40) + 5;
  10.                     FBlocks[Brows, Bcols, Srows, Scols].Brush.Color := clRed;
  11.                     FBlocks[Brows, Bcols, Srows, Scols].Pen.Color   := clBlack;
This time really 3x3 with each 5x5
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: KodeZwerg on January 25, 2023, 07:39:40 am
Yes I will try that. I looked at KodeZwerg's code but could not find a fault.
My error was, that I used your initial code which contained an error  >:D
As ALWAYS, Thanks for helping out KodeZwerg.
As always, you are welcome.  ;)
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: Boleeman on January 25, 2023, 08:20:17 am
Ah, in the initial code             FShapes[rows, cols].Top         := (rows - 1) * 40 - 1;
(Shape drawn in TitleBar!)

should be  FShapes[rows, cols].Top         := (rows) * 40 - 1;




Thanks KodeZwerg for that fix.

Works well now.




Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: KodeZwerg on January 25, 2023, 08:39:24 am
Code: Pascal  [Select][+][-]
  1.               shapeArray[(j-1)*numberRows+i].Top:=(j-1)*40-1;
That was the original bugged code by you, I just copy and paste without much thinking about %)

If needed, my current fully working example as project zipped in attachment, some minor additions made if you plan to put some more configuration in.  :)

Enjoy and stay healthy  O:-)
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: KodeZwerg on January 25, 2023, 02:05:38 pm
For easier identification, if wanted at all - exemplary to do something specific inside the mouse event for a block instead for a single shape, i adjusted code again that it now looks like this:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.GenerateBlocks(const AOwner: TComponent; const AParent: TWinControl);
  2. var
  3.   Brows, Bcols, Srows, Scols: Integer;
  4.   Lleft, Ltop: Integer;
  5.   LTag: Integer;
  6. begin
  7.   LTag := 1;
  8.   SetLength(FBlocks, FBlocksRow, FBlocksCol, FBlockRow, FBlockCol);
  9.   for Brows := 0 to Pred(FBlocksRow) do
  10.     for Bcols := 0 to Pred(FBlocksCol) do
  11.       for Srows := 0 to Pred(FBlockRow) do
  12.         for Scols := 0 to Pred(FBlockCol) do
  13.           begin
  14.             FBlocks[Brows, Bcols, Srows, Scols] := TShape.Create(AOwner);
  15.             try
  16.               Lleft                                           := Bcols * (FSpacing + (FBlockCol * FShapeHeight));
  17.               Ltop                                            := Brows * (FSpacing + (FBlockRow * FShapeWidth));
  18.               FBlocks[Brows, Bcols, Srows, Scols].Parent      := AParent;
  19.               FBlocks[Brows, Bcols, Srows, Scols].OnMouseUp   := @ShapeEvent;
  20.               FBlocks[Brows, Bcols, Srows, Scols].Shape       := stRectangle;
  21.               FBlocks[Brows, Bcols, Srows, Scols].Width       := FShapeWidth;
  22.               FBlocks[Brows, Bcols, Srows, Scols].Height      := FShapeHeight;
  23.               FBlocks[Brows, Bcols, Srows, Scols].Left        := Lleft + Pred(Scols * FShapeWidth) + FSpacing;
  24.               FBlocks[Brows, Bcols, Srows, Scols].Top         := Ltop + Pred(Srows * FShapeHeight) + FSpacing;
  25.               FBlocks[Brows, Bcols, Srows, Scols].Brush.Color := clRed;
  26.               FBlocks[Brows, Bcols, Srows, Scols].Brush.Style := bsSolid;
  27.               FBlocks[Brows, Bcols, Srows, Scols].Pen.Color   := clBlack;
  28.               FBlocks[Brows, Bcols, Srows, Scols].Pen.Style   := psSolid;
  29.               FBlocks[Brows, Bcols, Srows, Scols].Tag         := LTag;
  30.               FBlocks[Brows, Bcols, Srows, Scols].Name        := 'DynShape' + LTag.ToString;
  31. //              FBlocks[Brows, Bcols, Srows, Scols].Hint        := 'DynShape' + LTag.ToString;
  32. //              FBlocks[Brows, Bcols, Srows, Scols].ShowHint    := True;
  33.               Inc(LTag);
  34.             finally
  35.               FBlocks[Brows, Bcols, Srows, Scols].Visible := True;
  36.             end;
  37.           end;
  38. end;
You can remove the "//" and hover with mouse over to see how it works with Tags, so in the mouse event you just would need to check in what block it is by checking in what range the tag property for current shape is or use the name property to quick find a wanted shape for actions... however you do - it should work in all ways
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: Boleeman on January 25, 2023, 10:53:59 pm
Thanks KodeZwerg for the update and the new procedure with the additional 2 arguements (AOwner and AParent)

I added the modified GenerateBlocks procedure  to replace other one without the arguements   


  private
    //procedure GenerateBlocks;
    procedure GenerateBlocks(const AOwner: TComponent; const AParent: TWinControl);
    procedure ShapeEvent(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X , Y: Integer);
  public 


but in

procedure TForm1.FormCreate(Sender: TObject);
begin
  Randomize;
  FBlockRow := 4;
  FBlockCol := 4;
  FBlocksRow := 5;
  FBlocksCol := 5;
  FShapeWidth := 40;
  FShapeHeight := 40;
  FSpacing := 5;
  FBlocks := nil;
  Self.Width := (FBlockCol * FShapeWidth) * FBlocksCol + (FSpacing * 4);
  Self.Height := (FBlockRow * FShapeHeight) * FBlocksRow + (FSpacing * 4);
  GenerateBlocks(TForm1, TScrollBox1);
end;                                   

what do I put here:     GenerateBlocks(TForm1, which windowed control?);

I thought TWinControl was the scrollbox1 that I wanted the squares to be on, but comes up with error.

I am a bit confused about what to put in GenerateBlocks(TForm1, "????????);

Help say:  TWinControl Implements a windowed control which can contain other child controls.

Thanks in Advance.
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: KodeZwerg on January 25, 2023, 11:07:11 pm
Thanks KodeZwerg for the update and the new procedure with the additional 2 arguements (AOwner and AParent)

I added the modified GenerateBlocks procedure  to replace other one without the arguements   


  private
    //procedure GenerateBlocks;
    procedure GenerateBlocks(const AOwner: TComponent; const AParent: TWinControl);
    procedure ShapeEvent(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X , Y: Integer);
  public 


but in

procedure TForm1.FormCreate(Sender: TObject);
begin
  Randomize;
  FBlockRow := 4;
  FBlockCol := 4;
  FBlocksRow := 5;
  FBlocksCol := 5;
  FShapeWidth := 40;
  FShapeHeight := 40;
  FSpacing := 5;
  FBlocks := nil;
  Self.Width := (FBlockCol * FShapeWidth) * FBlocksCol + (FSpacing * 4);
  Self.Height := (FBlockRow * FShapeHeight) * FBlocksRow + (FSpacing * 4);
  GenerateBlocks(TForm1, TScrollBox1);
end;                                   

what do I put here:     GenerateBlocks(TForm1, which windowed control?);

I thought TWinControl was the scrollbox1 that I wanted the squares to be on, but comes up with error.

I am a bit confused about what to put in GenerateBlocks(TForm1, "????????);

Help say:  TWinControl Implements a windowed control which can contain other child controls.

Thanks in Advance.
AOwner = Self // in that case, TForm1 will be automagical used and is responsable to free my stuff
AParent = Self // in that case, everything will be drawn/put/placed on TForm1 automagical. You can use Panel1 etc... (I assume you made a typo, not write TScrollBox1, write ScrollBox1)
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: KodeZwerg on January 25, 2023, 11:11:15 pm
I just checked, yes in my test I did used it like this ->
Code: Pascal  [Select][+][-]
  1.   GenerateBlocks(Self, Self);
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: KodeZwerg on January 25, 2023, 11:14:16 pm
In attachment is a variant where all is put into a scrollbox. Enjoy.  :-*
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: KodeZwerg on January 25, 2023, 11:36:24 pm
  GenerateBlocks(TForm1, TScrollBox1);
Both is wrong!
GenerateBlocks(Form1, ScrollBox1);
And instead of Form1, please adapt like I showed in source, use Self when you want that current form, whatever its name may be, will be used.
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: Boleeman on January 26, 2023, 01:17:38 am
AH thanks KodeZwerg.


"Self"and not "Form1"   I tried a number of combinations but went round in circles.

 
Pascal way is a bit different VB6 so still getting used to it. I like the way you looped through to create the multiples of the original arrays. Looking at the code your naming of the variables is great (like FBlockRow and Srows). Still need to refine that technique.

Now for trying to make the same square of each repeated block the same colour after a mouse click (Mapping the mouse clicks to the original Srows x Scols array)?

I played around with Tag Hints that you suggested.

I also tried altering it a bit to show columns and rows but failed in that attempt (got not overloaded error message)              FBlocks[Brows, Bcols, Srows, Scols].Name        := 'DynShape' + LTag.ToString + InttoStr(Srows)+ ' , ' + InttoStr(Scols);

What I noticed was:
For each array group the 1st element is 1 more than a multiple of 25 = Srows * Scols

n * Srows * Scols + 1                          where n is an integer.

So this would be a similar pattern for others. 2nd element n * Srows * Scols + 2 ... and so on


So using the tag of the control is a smart way of keeping track of things.


Now how to make a synchronised mouse click event in  TForm1.ShapeEvent ?

Code: Pascal  [Select][+][-]
  1. procedure TForm1.ShapeEvent(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X , Y: Integer);
  2.                                                  
  3. var
  4.   Brows, Bcols, Srows, Scols, n: Integer;
  5.   LTag: Integer;  
  6.  
  7.  
  8.   for Brows := 0 to Pred(FBlocksRow) do
  9.     for Bcols := 0 to Pred(FBlocksCol) do
  10.       for Srows := 0 to Pred(FBlockRow) do
  11.         for Scols := 0 to Pred(FBlockCol) do
  12.           begin        
  13.  
  14.              If StrtoInt(LTag.ToString) mod Srows * Scols = //index of controlclicked[// then  
  15.  
  16.                 //  (Sender as TShape).Brush.Color := clBlue;   Make All the corresponding shapes Blue
  17.  
  18.           end;

Wow. This is getting confusing.
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: KodeZwerg on January 26, 2023, 12:00:25 pm
Now how to make a synchronised mouse click event in  TForm1.ShapeEvent ?
I show you one way of how to solve in attachment.
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: KodeZwerg on January 26, 2023, 12:06:12 pm
 :o I missed to put correct code into the "else"
Please update line #152 to:
Code: Pascal  [Select][+][-]
  1.       (Sender as TShape).Brush.Color := btnColor.ButtonColor;
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: Boleeman on January 26, 2023, 12:53:17 pm
Wow KodeZwerg. That's so cool.
(Just got back from an outdoors park gym workout at 10.30 pm).

I was looking at your magnificent code:

 LTag := (Sender as TShape).Tag;  means the tag number is the shape clicked tag. (that's one thing I did not how to write in the code. It makes sense now.
 if (LTag > (FBlockRow * FBlockCol)) means the click is not i the initial row by column block
 LTag := LTag mod (FBlockRow * FBlockCol); means tag number is the left over from mod division

The part I am still trying to understand is:

Code: Pascal  [Select][+][-]
  1.                [color=red][b] if ((i = LTag) or (i - (FBlockRow * FBlockCol) = LTag)) [/b][/color]then
  2.                   FBlocks[Brows, Bcols, Srows, Scols].Brush.Color := btnColor.ButtonColor;
  3.                 Inc(i);
  4.                 if (i > (FBlockRow * FBlockCol)) then
  5.                   i := 1;

Would i be the block number, like the second 5x5 block, 3rd 5x5 block number and so on.

Still can't get over how you coded all of that so efficiently. I was struggling with the logic and how to write it into TfrmMain.ShapeEvent procedure code.

I am so impressed.

Thanks for helping out once again KodeZwerg.





Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: KodeZwerg on January 26, 2023, 01:07:11 pm
Please update or just read  :P
Code: Pascal  [Select][+][-]
  1. procedure TfrmMain.ShapeEvent(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X , Y: Integer);
  2. var
  3.   Brows, Bcols, Srows, Scols: Integer;
  4.   LTag, i: Integer;
  5. begin
  6.   if cbSync.Checked then
  7.     begin
  8.       // my counter (from 1 to 25 if its 5x5)
  9.       i := 1;
  10.       // get correct tag (from 1 to 25 if its 5x5)
  11.       LTag := (Sender as TShape).Tag;
  12.       // if we are outside of 25, recalculate
  13.       if (LTag > (FBlockRow * FBlockCol)) then
  14.         LTag := LTag mod (FBlockRow * FBlockCol);
  15.       // it was exact the 25, so set it to 25
  16.       if LTag = 0 then
  17.         LTag := (FBlockRow * FBlockCol);
  18.       // loop to update tag
  19.       for Brows := 0 to Pred(FBlocksRow) do
  20.         for Bcols := 0 to Pred(FBlocksCol) do
  21.           for Srows := 0 to Pred(FBlockRow) do
  22.             for Scols := 0 to Pred(FBlockCol) do
  23.               begin
  24.                 // "i" represent now every shape from 1 to 25 inside each block
  25.                 if (i = LTag) then
  26.                   FBlocks[Brows, Bcols, Srows, Scols].Brush.Color := btnColor.ButtonColor;
  27.                 Inc(i);
  28.                 if (i > (FBlockRow * FBlockCol)) then
  29.                   i := 1;
  30.               end;
  31.     end
  32.     else
  33.       (Sender as TShape).Brush.Color := btnColor.ButtonColor;
  34. end;

Quote
(i - (FBlockRow * FBlockCol) = LTag))
that was a relict, outdated and i missed to remove.
Wow KodeZwerg. That's so cool.
I am happy to help.  O:-)
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: Boleeman on January 26, 2023, 01:16:52 pm
OK I will update.

Thanks for adding the explanations.

So  "i" represent now every shape from 1 to 25 inside each block

I like looking over the code to analyse and learn from it.
It's great that you explained it further for me to better understand.

Still can't believe that you achieved what I thought was impossible to achieve. I hard coded mine in VB6, but looks like Lazarus does arrays better. I liked how you put in hints in the previous code to look for patterns. When you code do you use any tools? I was thinking of using a snippets library program, to store bits and pieces of code.
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: KodeZwerg on January 26, 2023, 03:26:48 pm
Thanks for adding the explanations.
XOXOXO

Still can't believe that you achieved what I thought was impossible to achieve. I hard coded mine in VB6, but looks like Lazarus does arrays better. I liked how you put in hints in the previous code to look for patterns.
Hardcoded = wrong coded  >:D 8-)
I always try to make everything generic working  O:-)
Alpha releases often contain hardcoded, beta is than refactored to become final.
If I ever refactor this demo, I would for example replace "FBlockRow * FBlockCol" with a variable like "LMax" to just calculate "FBlockRow * FBlockCol" once.

When you code do you use any tools? I was thinking of using a snippets library program, to store bits and pieces of code.
The only tool I use is what the IDE offers me, from time to time a calculator if I am too sleepy to do it in my head.
I also read from time to time documentation about things to understand whats a possible correct way to handle those things.
My code library is big but to be honest, I do look very very rare into, I just keep adding  %)
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: Joanna on January 27, 2023, 12:07:27 am
I don’t know if this will be helpful or not but I’d like to pass along some advice that was given to me back when I had some frames with a lot of shapes in them....
You might eventually want to draw your shapes in a paintbox rather than using separate shape objects. When using paintbox Click Events determine which area in paintbox click occurred in simulating clicking on a shape.

If you ever have any difficulty with slowness like I did... 8)
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: Boleeman on January 27, 2023, 07:48:37 am
I can't believe the stats for this thread:    36 Replies   and   1547 Views

The reason for asking to do this Shapes Array way was to see if it could be done in Lazarus.
I made a hardcoded VB6 version for 3x3 and 6x6 squares and was always puzzled when thinking of a dynamic array way to do the tessellations. I would click on these outside the main grid to make the effects inside the larger grid. KodeZwerg's way in Lazarus was designed to click anywhere in the large grid to repeat that pattern. I was aware that the shapes would take longer to load, but the click event is not that long to execute.

It would be nice to see it done this Tessellation done by using the paintbox Click Events determine which area in paintbox click that it occurred, to compare the difference. Even thought of just having an original line grid array and then copying multiple canvas copies of it to a semi-transparent BGRABitmap.

I have some creative ideas for graphics programs, but tend to get bogged down by how to code it. I learnt quite a bit from KodeZwerg and VisualLab and Circular and speter and Jamie and WP and Alpine and ASerge over the Summer holiday period (Supershape, Polygons, Tesselator, Grid). I am very grateful for their patience and their guidance. I always wanted to have a go at doing Lazaras but tended to get bogged down with errors happening and not knowing how to correct them. I am slowing learning how to fix these errors, but still on that learning curve. This board has some nice members.
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: KodeZwerg on January 27, 2023, 10:41:27 am
I don’t know if this will be helpful or not but I’d like to pass along some advice that was given to me back when I had some frames with a lot of shapes in them....
You might eventually want to draw your shapes in a paintbox rather than using separate shape objects. When using paintbox Click Events determine which area in paintbox click occurred in simulating clicking on a shape.

If you ever have any difficulty with slowness like I did... 8)
Than I would rather do like Handoko telled, switching to a paintbox or image and instead of shapes, draw rectangles, get x/y location to know which "shape" was clicked on to maybe repeat that action for others.
My demo is a simplified way to get the proper result and easy handling since everything is its own object.
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: Handoko on January 27, 2023, 11:58:31 am
The way I suggested in the reply #2 - as I already mentioned - is hard way solution. It's fun, the programmer will have more control on the visual result and it is lightweight. Less memory and fast.

Sorry I don't mean to discourage the OP. KodeZwerg solution is relatively easy and he provided good explanation, unfortunately the OP seems a bit slow following it. I would recommend anybody should already able fully understand KodeZwerg's solution before try doing the hard way.
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: Joanna on January 27, 2023, 02:23:41 pm
It’s definitely easier to start with actual shape controls at beginning.
A lot of complicated things you need to work your way up to with easier stuff otherwise it will be overwhelming... %)

 In my case it was a frame for picking colors so each shape was a color to choose. I created all the shapes at runtime.
If I remember correctly there was no click event for tshapes so I used onmouseup event.

Playing with colorful shapes is definitely a fun way to learn programming.
Lazarus reminds me of vbasic only better :)
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: Boleeman on January 27, 2023, 02:24:13 pm
The other important property is that you can remove the border of each shape to make interesting continuous colour effects. In the attached picture the brown line is continuous (just as a simple example). Would the grid line method be able to have no borders?

BTW:
Is there a quick way to clear the colour of all shapes (like a collection way used in Vb6) rather than looping through all the array items? I tried this but it did not work. I think Controls is the problem?

Code: Pascal  [Select][+][-]
  1. procedure TfrmMain.bnClearClick(Sender: TObject);
  2. var
  3.   i: integer;
  4. begin
  5.   for i := 0 to ControlCount-1 do
  6.     if Controls[i] is TShape then
  7.     begin
  8.       (Controls[i] as TShape).Brush.Color:=clWhite;
  9.     end;
  10. end;              
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: KodeZwerg on January 27, 2023, 02:43:57 pm
Added your feature in a correct way  :-*
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: Boleeman on January 27, 2023, 02:52:34 pm
Ah I see not "controls(i) but Components "

What does Pred mean?  Hovering over Pred says Type LongInt  so does that change type from longInt to integer?


I saw Pred in the code but did not really know why it was used (just assumed it was needed).

OK:  Pred returns the element that precedes the element that was passed to it. Thanks KodeZwerg.

Was also looking to toggle the border of each shape ON and OFF  and the 5 pixel gap between blocks with one checkbox but could not find the border property in

FBlocks[Brows, Bcols, Srows, Scols].??????

Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: KodeZwerg on January 27, 2023, 02:59:02 pm
Ah I see not "controls(i) but Components "

What does Pred mean?  Hovering over Pred says Type LongInt  so does that change type from longInt to integer?
Lol, I literal used that method all the time and just by now you asking me what Pred() (https://www.freepascal.org/docs-html/rtl/system/pred.html) means?  :o
Quote
Pred returns the element that precedes the element that was passed to it.
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: KodeZwerg on January 27, 2023, 03:18:09 pm
And another update to also set the bordercolor (in code you have the ability to set/unset the complete background color)
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: Boleeman on January 27, 2023, 03:32:57 pm
What about border ON/OFF boolean toggle? 

Here it is 1.30 AM so time to go to sleep. Getting tired now, as I returned to work (yesterday). See you for now. Thanks for the updates and explanations KodeZwerg.
Title: Re: Make a Dynamic Shape Array and make an Array of That
Post by: KodeZwerg on January 27, 2023, 03:50:34 pm
What about border ON/OFF boolean toggle? 
In GenerateBlocks you can update the Pen.Color to:
Code: Pascal  [Select][+][-]
  1.               FBlocks[Brows, Bcols, Srows, Scols].Pen.Color   := clNone;
That would produce that there is no visible difference between the underlaying ScrollBox and the space between those shapes.
Later you can press the new button "Border Color", maybe rename that button to Background Color  :-X
Here it is 1.30 AM so time to go to sleep. Getting tired now, as I returned to work (yesterday). See you for now. Thanks for the updates and explanations KodeZwerg.
Have a good rest and whenever you are curious about updates, just ask :P
TinyPortal © 2005-2018