Recent

Author Topic: Conant Fractal converted from JS: No Rendered Lines  (Read 390 times)

Boleeman

  • Hero Member
  • *****
  • Posts: 721
Conant Fractal converted from JS: No Rendered Lines
« on: September 29, 2024, 08:04:36 am »
I came across a cool fractal called the Conant Gasket, but I still had no success in converting to a working Lazarus version. It was at https://somethingorotherwhatever.com/items/the-conant-gasket/   

I tried to convert it from JavaScript to Lazarus before and had another go today.

The original JavaScript version uses SVG and creates line segments with point positions, line thicknesses and line colours.

Not sure if anyone can have a look at what might be the problem (in next reply below) ?

Attached below is a working Javascript.


Attached is a jpg of what the curve looks like.

The process to draw the curve:

Start with a unit square.

    Draw a vertical line from top to bottom, dividing the square in half.
    Go to the left edge, and halfway up draw a line from left to right. Stop when you reach the vertical line.
    Return to the top edge and draw vertical lines at the midpoints of the remaining intervals (at the 1/4 and 3/4 points for the second pass), from top to bottom. If you reach a line, stop, skip to the next line and start again, stopping the next time you reach a line. Repeat until you reach the bottom.
    Return to the left edge and draw horizontal lines at the midpoints of the remaining intervals, from left to right. If you reach a line, stop, skip to the next line and start again, stopping the next time you reach a line. Repeat until you reach the right edge.
    Go back to step 3.
« Last Edit: November 02, 2024, 06:36:53 am by Boleeman »

Boleeman

  • Hero Member
  • *****
  • Posts: 721
Re: Conant Fractal: Troubles converting from JS
« Reply #1 on: November 02, 2024, 06:04:39 am »
I had another try at doing the conversion of the JavaScript code to Lazarus.

Code compiles OK, but I don't see any lines rendered to the TBgrabmp canvas.

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, BGRABitmap, Math,
  9.   BGRABitmapTypes;
  10.  
  11. type
  12.   { TLineInfo }
  13.   TLineInfo = record
  14.     X1, Y1, X2, Y2: Double;
  15.     StrokeColor: TBGRAPixel;
  16.     StrokeWidth: Double;
  17.   end;
  18.  
  19.   { TDyadic }
  20.   TDyadic = class
  21.   private
  22.     FA, FB: Integer;
  23.   public
  24.     constructor Create(AValue: Integer; BValue: Integer);
  25.     function ToString: string; override;
  26.     function ToDecimal: Double;
  27.     function Leq(d: TDyadic): Boolean;
  28.     function Geq(d: TDyadic): Boolean;
  29.     function Lt(d: TDyadic): Boolean;
  30.     function Gt(d: TDyadic): Boolean;
  31.     function Eq(d: TDyadic): Boolean;
  32.     function ToDenominator(b2: Integer): TDyadic;
  33.   end;
  34.  
  35.   TSegment = array[0..1] of TDyadic;
  36.   TSegmentsArray = array of TSegment;
  37.  
  38.   { TSegments }
  39.   TSegments = class
  40.   private
  41.     FPos: TDyadic;
  42.     FSegments: TSegmentsArray;
  43.     FAxes: array of string;
  44.   public
  45.     constructor Create(pos: TDyadic; segments: TSegmentsArray; axes: array of string);
  46.     function ToString: string; override;
  47.     function Contains(p: TDyadic): Boolean;
  48.     procedure Draw(Bmp: TBGRABitmap);
  49.   end;
  50.  
  51.   { TForm1 }
  52.   TForm1 = class(TForm)
  53.     procedure FormPaint(Sender: TObject);
  54.   private
  55.     procedure StepFractal;
  56.   public
  57.     constructor Create(TheOwner: TComponent); override;
  58.   end;
  59.  
  60. var
  61.   Form1: TForm1;
  62.   Directions: array[0..1] of TList;
  63.  
  64. implementation
  65.  
  66. {$R *.lfm}
  67.  
  68. { TDyadic }
  69.  
  70. constructor TDyadic.Create(AValue: Integer; BValue: Integer);
  71. begin
  72.   FA := AValue;
  73.   FB := BValue;
  74. end;
  75.  
  76. function TDyadic.ToString: string;
  77. begin
  78.   Result := Format('%d / 2^%d', [FA, FB]);
  79. end;
  80.  
  81. function TDyadic.ToDecimal: Double;
  82. begin
  83.   Result := FA / Power(2, FB);
  84. end;
  85.  
  86. function TDyadic.Leq(d: TDyadic): Boolean;
  87. begin
  88.   Result := (FA * Power(2, d.FB)) <= (d.FA * Power(2, FB));
  89. end;
  90.  
  91. function TDyadic.Geq(d: TDyadic): Boolean;
  92. begin
  93.   Result := (FA * Power(2, d.FB)) >= (d.FA * Power(2, FB));
  94. end;
  95.  
  96. function TDyadic.Lt(d: TDyadic): Boolean;
  97. begin
  98.   Result := (FA * Power(2, d.FB)) < (d.FA * Power(2, FB));
  99. end;
  100.  
  101. function TDyadic.Gt(d: TDyadic): Boolean;
  102. begin
  103.   Result := (FA * Power(2, d.FB)) > (d.FA * Power(2, FB));
  104. end;
  105.  
  106. function TDyadic.Eq(d: TDyadic): Boolean;
  107. begin
  108.   Result := (FA * Power(2, d.FB)) = (d.FA * Power(2, FB));
  109. end;
  110.  
  111. function TDyadic.ToDenominator(b2: Integer): TDyadic;
  112. begin
  113.   Result := TDyadic.Create(FA shl (b2 - FB), b2);
  114. end;
  115.  
  116. { TSegments }
  117.  
  118. constructor TSegments.Create(pos: TDyadic; segments: TSegmentsArray; axes: array of string);
  119. var
  120.   i: Integer;
  121. begin
  122.   FPos := pos;
  123.   FSegments := segments;
  124.   SetLength(FAxes, Length(axes));
  125.   for i := 0 to High(axes) do
  126.     FAxes[i] := axes[i];
  127. end;
  128.  
  129. function TSegments.ToString: string;
  130. begin
  131.   Result := Format('Position: %s', [FPos.ToString]);
  132. end;
  133.  
  134. function TSegments.Contains(p: TDyadic): Boolean;
  135. var
  136.   i: Integer;
  137.   SegStart, SegEnd: TDyadic;
  138. begin
  139.   Result := False;
  140.   for i := 0 to High(FSegments) do
  141.   begin
  142.     SegStart := FSegments[i][0];
  143.     SegEnd := FSegments[i][1];
  144.     if (p.Lt(SegEnd) and p.Geq(SegStart)) or (p.Gt(SegStart) and p.Leq(SegEnd)) then
  145.     begin
  146.       Result := True;
  147.       Break;
  148.     end;
  149.   end;
  150. end;
  151.  
  152. procedure TSegments.Draw(Bmp: TBGRABitmap);
  153. var
  154.   i: Integer;
  155.   SegStart, SegEnd: TDyadic;
  156.   PenWidth: Single;
  157. begin
  158.   PenWidth := 1.0;
  159.   for i := 0 to High(FSegments) do
  160.   begin
  161.     SegStart := FSegments[i][0];
  162.     SegEnd := FSegments[i][1];
  163.     Bmp.DrawLineAntialias(Round(SegStart.ToDecimal), Round(SegStart.ToDecimal),
  164.                           Round(SegEnd.ToDecimal), Round(SegEnd.ToDecimal),
  165.                           BGRABlack, PenWidth);
  166.   end;
  167. end;
  168.  
  169. { TForm1 }
  170.  
  171. constructor TForm1.Create(TheOwner: TComponent);
  172. begin
  173.   inherited Create(TheOwner);
  174.   Directions[0] := TList.Create;
  175.   Directions[1] := TList.Create;
  176. end;
  177.  
  178. procedure TForm1.FormPaint(Sender: TObject);
  179. var
  180.   Bitmap: TBGRABitmap;
  181.   Segments: TSegments;
  182. begin
  183.   Segments := TSegments.Create(TDyadic.Create(0, 0), [], ['X', 'Y']);
  184.   Bitmap := TBGRABitmap.Create(ClientWidth, ClientHeight);
  185.   try
  186.     Bitmap.Fill(BGRAWhite);
  187.     StepFractal();
  188.     Segments.Draw(Bitmap);
  189.     Bitmap.Draw(Canvas, 0, 0);
  190.   finally
  191.     Bitmap.Free;
  192.     Segments.Free;
  193.   end;
  194. end;
  195.  
  196. procedure TForm1.StepFractal;
  197. var
  198.   n, x, i, j: Integer;
  199.   ATop: Integer;
  200.   Move, Hit: TList;
  201.   A, P, SegStart, SegEnd: TDyadic;
  202.   Up: Boolean;
  203.   Segment: TSegments;
  204.   S: TSegments; // Declare S as a TSegments variable for casting items from Hit
  205. begin
  206.   Inc(i);
  207.   n := (i + (i mod 2)) div 2;
  208.   //ATop := Round(Power(2, n));   // Gives Overflow error n too large, resulting in a floating-point overflow when calculating Power(2, n)
  209.   ATop := 1 shl n; // Equivalent to Power(2, n) but using integer arithmetic and bitwise shift
  210.  
  211.   Move := Directions[0];
  212.   Hit := Directions[1];
  213.  
  214.   for x := 1 to ATop - 1 do
  215.   begin
  216.     if x mod 2 = 1 then
  217.     begin
  218.       P := TDyadic.Create(x, n);
  219.       Up := True;
  220.       Segment := TSegments.Create(P, [], []);
  221.  
  222.       A := TDyadic.Create(0, 0);
  223.       for j := 0 to Hit.Count - 1 do
  224.       begin
  225.         S := TSegments(Hit[j]);  // Cast each item in Hit to TSegments
  226.         if S.Contains(P) then
  227.         begin
  228.           if Up then
  229.           begin
  230.             SegStart := A;
  231.             SegEnd := S.FPos;
  232.             SetLength(Segment.FSegments, Length(Segment.FSegments) + 1);
  233.             Segment.FSegments[High(Segment.FSegments)][0] := SegStart;
  234.             Segment.FSegments[High(Segment.FSegments)][1] := SegEnd;
  235.           end
  236.           else
  237.             A := S.FPos;
  238.           Up := not Up;
  239.         end;
  240.       end;
  241.  
  242.       if Up then
  243.       begin
  244.         SegStart := A;
  245.         SegEnd := TDyadic.Create(1, 0);
  246.         SetLength(Segment.FSegments, Length(Segment.FSegments) + 1);
  247.         Segment.FSegments[High(Segment.FSegments)][0] := SegStart;
  248.         Segment.FSegments[High(Segment.FSegments)][1] := SegEnd;
  249.       end;
  250.  
  251.       Segment.Free;
  252.     end;
  253.   end;
  254. end;
  255.  
  256. end.
  257.  

Attached is also a captured animated version of the JavaScript version, to explain how the fractal is contructed from horizontal and vertical line segments.
« Last Edit: November 02, 2024, 06:26:46 am by Boleeman »

 

TinyPortal © 2005-2018