Recent

Author Topic: Snaking ZigZag Curve: PROBLEMS with shape consistency !  (Read 336 times)

Boleeman

  • Hero Member
  • *****
  • Posts: 902
Snaking ZigZag Curve: PROBLEMS with shape consistency !
« on: April 23, 2025, 02:46:10 pm »
Hi All.

I tried making a Snaking ZigZag curve using Lazarus but the curve does not seem to be working consistently to give a ZigZag geometry (As shown in the 1st attached png).

Not sure why it is happening? Wonder if anyone can figure out why?
(sort of seems to rest when both TSpinEdits are the same value)


I also realized now that I made it differently to a JavaScript version which seems to zigzag/loop more often.
(2nd attached png)

Handoko

  • Hero Member
  • *****
  • Posts: 5417
  • My goal: build my own game engine using Lazarus
Re: Snaking ZigZag Curve: PROBLEMS with shape consistency !
« Reply #1 on: April 23, 2025, 10:12:04 pm »
Bugs fixed.

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, StdCtrls,
  9.   Spin, BGRABitmap, BGRABitmapTypes, Math;
  10.  
  11. type
  12.  
  13.   { TForm1 }
  14.  
  15.   TForm1 = class(TForm)
  16.     btnRefresh: TButton;
  17.     CheckBox1: TCheckBox;
  18.     Label1: TLabel;
  19.     Label2: TLabel;
  20.     PaintBox1: TPaintBox;
  21.     Panel1: TPanel;
  22.     seiAcross: TSpinEdit;
  23.     sejDown: TSpinEdit;
  24.     procedure btnRefreshClick(Sender: TObject);
  25.     procedure PaintBox1Paint(Sender: TObject);
  26.     procedure SpinChange(Sender: TObject);
  27.   private
  28.     procedure DrawZigZagPath(across, down: Integer; bmp: TBGRABitmap);
  29.   public
  30.  
  31.   end;
  32.  
  33. var
  34.   Form1: TForm1;
  35.  
  36. implementation
  37.  
  38. {$R *.lfm}
  39.  
  40. procedure TForm1.DrawZigZagPath(across, down: Integer; bmp: TBGRABitmap);
  41. var
  42.   points, rotated: array of TPointF;
  43.   x, y, stepCount, dx, dy: Integer;
  44.   gridX, gridY: Single;
  45.   i: Integer;
  46.   minX, maxX, minY, maxY, cx, cy: Single;
  47.   Alternatif: Boolean;
  48.   Temp: TPointF;
  49. begin
  50.   bmp.Fill(BGRA(0, 0, 0));
  51.  
  52.   //across := seiAcross.Value;
  53.   //down := sejDown.Value;
  54.  
  55.   gridX := bmp.Width / (across + 2);
  56.   gridY := bmp.Height / (down + 2);
  57.  
  58.   SetLength(points, 0);
  59.   x := 0; y := 0;
  60.   stepCount := 1;
  61.  
  62.   SetLength(points, Length(points) + 1);
  63.   points[High(points)] := PointF((x + 1) * gridX, (y + 1) * gridY);
  64.   Inc(y);
  65.   SetLength(points, Length(points) + 1);
  66.   points[High(points)] := PointF((x + 1) * gridX, (y + 1) * gridY);
  67.  
  68.   while True do
  69.   begin
  70.     if stepCount mod 2 = 1 then
  71.     begin
  72.       dx := stepCount;
  73.       dy := -stepCount;
  74.     end
  75.     else
  76.     begin
  77.       dx := -stepCount;
  78.       dy := stepCount;
  79.     end;
  80.  
  81.     Inc(x, dx);
  82.     Inc(y, dy);
  83.     if (x < 0) or (x > across) or (y < 0) or (y > down) then Break;
  84.  
  85.     SetLength(points, Length(points) + 1);
  86.     points[High(points)] := PointF((x + 1) * gridX, (y + 1) * gridY);
  87.  
  88.     if stepCount mod 2 = 1 then
  89.       Inc(x)
  90.     else
  91.       Inc(y);
  92.  
  93.     if (x < 0) or (x > across) or (y < 0) or (y > down) then Break;
  94.  
  95.     SetLength(points, Length(points) + 1);
  96.     points[High(points)] := PointF((x + 1) * gridX, (y + 1) * gridY);
  97.  
  98.     Inc(stepCount);
  99.   end;
  100.  
  101.   // Rotate the path 180° about its center
  102.   if Length(points) > 0 then
  103.   begin
  104.     minX := points[0].X; maxX := minX;
  105.     minY := points[0].Y; maxY := minY;
  106.  
  107.     for i := 1 to High(points) do
  108.     begin
  109.       if points[i].X < minX then minX := points[i].X else if points[i].X > maxX then maxX := points[i].X;
  110.       if points[i].Y < minY then minY := points[i].Y else if points[i].Y > maxY then maxY := points[i].Y;
  111.     end;
  112.  
  113.     cx := (minX + maxX) / 2;
  114.     cy := (minY + maxY) / 2;
  115.  
  116.     SetLength(rotated, Length(points));
  117.     for i := 0 to High(points) do
  118.       rotated[i] := PointF(2 * cx - points[i].X, 2 * cy - points[i].Y);
  119.  
  120.     Alternatif := False;
  121.     if Odd(seiAcross.Value+1) and (sejDown.Value>seiAcross.Value) then
  122.       Alternatif := True;
  123.     if Odd(seiAcross.Value+1) and (sejDown.Value<seiAcross.Value) and Odd(sejDown.Value) then
  124.       Alternatif := True;
  125.     if Odd(seiAcross.Value) and odd(sejDown.Value) and (seiAcross.Value > sejDown.Value) then
  126.       Alternatif := True;
  127.  
  128.     for i := 1 to High(rotated) do
  129.     begin
  130.       if rotated[i].X = rotated[i+1].X-100 then rotated[i].X := rotated[i+1].X;
  131.       if not(Alternatif) then Continue;
  132.       if i mod 4 = 2 then
  133.       begin
  134.         Temp := rotated[i];
  135.         rotated[i] := rotated[i-1];
  136.         rotated[i-1] := Temp;
  137.       end;
  138.       if i mod 4 = 3 then
  139.       begin
  140.         Temp := rotated[i];
  141.         rotated[i] := rotated[i+1];
  142.         rotated[i+1] := Temp;
  143.       end;
  144.     end;
  145.   end;
  146.  
  147.   for i := 0 to High(points) - 1 do
  148.     bmp.DrawLineAntialias(points[i].X, points[i].Y, points[i + 1].X, points[i + 1].Y, BGRA(255, 255, 0), 2);
  149.  
  150.   for i := 0 to High(rotated) - 2 do
  151.     bmp.DrawLineAntialias(rotated[i].X, rotated[i].Y, rotated[i + 1].X, rotated[i + 1].Y, BGRA(0, 255, 255), 2);
  152. end;
  153.  
  154.  
  155. procedure TForm1.btnRefreshClick(Sender: TObject);
  156. begin
  157.  Paintbox1.Invalidate;
  158. end;
  159.  
  160. procedure TForm1.PaintBox1Paint(Sender: TObject);
  161. var
  162.   bmp: TBGRABitmap;
  163. begin
  164.   bmp := TBGRABitmap.Create(PaintBox1.Width, PaintBox1.Height);
  165.   try
  166.     DrawZigZagPath(seiAcross.Value, sejDown.Value, bmp);
  167.     bmp.Draw(PaintBox1.Canvas, 0, 0, True);
  168.   finally
  169.     bmp.Free;
  170.   end;
  171. end;
  172.  
  173. procedure TForm1.SpinChange(Sender: TObject);
  174. begin
  175.   if CheckBox1.Checked then
  176.     PaintBox1.Invalidate;
  177. end;
  178.  
  179. end.


The edited code were lines #120 - #144 and #150.

Boleeman

  • Hero Member
  • *****
  • Posts: 902
Re: Snaking ZigZag Curve: PROBLEMS with shape consistency !
« Reply #2 on: April 24, 2025, 12:42:14 pm »
Thanks Handoko for help me out.

The curved path is now uniform, but the logic in my code is not quite correct.

When increasing the across TSpinEdit to say 17, only 10 grid spaces are used to draw the pattern but what should happen is that the remaining 7 grid spaces also need to be used to draw the remaining lines.

Also for some reason on my AMD CPU laptop I kept on getting an error when having Checkbox1.checked.


After reflecting a bit today I realized I was using using grid spaces, rather than using points across and down (like in the JavaScript version. Need to go back to the drawing board and rethink the algorithm.

Thanks Handoko for that updated code. Much appreciated.

 

TinyPortal © 2005-2018