Conant Fractal (Close to JavaScript version but not exact) lazarus / 39-Graphics ---------------------------------------------------------------------- Boleeman · 2024-09-29 06:04 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. ---------------------------------------------------------------------- Boleeman · 2024-11-02 05:04 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit Unit1; {$mode objfpc}{$H+} interface uses  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, BGRABitmap, Math,  BGRABitmapTypes; type  { TLineInfo }  TLineInfo = record    X1, Y1, X2, Y2: Double;    StrokeColor: TBGRAPixel;    StrokeWidth: Double;  end;   { TDyadic }  TDyadic = class  private    FA, FB: Integer;  public    constructor Create(AValue: Integer; BValue: Integer);    function ToString: string; override;    function ToDecimal: Double;    function Leq(d: TDyadic): Boolean;    function Geq(d: TDyadic): Boolean;    function Lt(d: TDyadic): Boolean;    function Gt(d: TDyadic): Boolean;    function Eq(d: TDyadic): Boolean;    function ToDenominator(b2: Integer): TDyadic;  end;   TSegment = array[0..1] of TDyadic;  TSegmentsArray = array of TSegment;   { TSegments }  TSegments = class  private    FPos: TDyadic;    FSegments: TSegmentsArray;    FAxes: array of string;  public    constructor Create(pos: TDyadic; segments: TSegmentsArray; axes: array of string);    function ToString: string; override;    function Contains(p: TDyadic): Boolean;    procedure Draw(Bmp: TBGRABitmap);  end;   { TForm1 }  TForm1 = class(TForm)    procedure FormPaint(Sender: TObject);  private    procedure StepFractal;  public    constructor Create(TheOwner: TComponent); override;  end; var  Form1: TForm1;  Directions: array[0..1] of TList; implementation {$R *.lfm} { TDyadic } constructor TDyadic.Create(AValue: Integer; BValue: Integer);begin  FA := AValue;  FB := BValue;end; function TDyadic.ToString: string;begin  Result := Format('%d / 2^%d', [FA, FB]);end; function TDyadic.ToDecimal: Double;begin  Result := FA / Power(2, FB);end; function TDyadic.Leq(d: TDyadic): Boolean;begin  Result := (FA * Power(2, d.FB)) <= (d.FA * Power(2, FB));end; function TDyadic.Geq(d: TDyadic): Boolean;begin  Result := (FA * Power(2, d.FB)) >= (d.FA * Power(2, FB));end; function TDyadic.Lt(d: TDyadic): Boolean;begin  Result := (FA * Power(2, d.FB)) < (d.FA * Power(2, FB));end; function TDyadic.Gt(d: TDyadic): Boolean;begin  Result := (FA * Power(2, d.FB)) > (d.FA * Power(2, FB));end; function TDyadic.Eq(d: TDyadic): Boolean;begin  Result := (FA * Power(2, d.FB)) = (d.FA * Power(2, FB));end; function TDyadic.ToDenominator(b2: Integer): TDyadic;begin  Result := TDyadic.Create(FA shl (b2 - FB), b2);end; { TSegments } constructor TSegments.Create(pos: TDyadic; segments: TSegmentsArray; axes: array of string);var  i: Integer;begin  FPos := pos;  FSegments := segments;  SetLength(FAxes, Length(axes));  for i := 0 to High(axes) do    FAxes[i] := axes[i];end; function TSegments.ToString: string;begin  Result := Format('Position: %s', [FPos.ToString]);end; function TSegments.Contains(p: TDyadic): Boolean;var  i: Integer;  SegStart, SegEnd: TDyadic;begin  Result := False;  for i := 0 to High(FSegments) do  begin    SegStart := FSegments[i][0];    SegEnd := FSegments[i][1];    if (p.Lt(SegEnd) and p.Geq(SegStart)) or (p.Gt(SegStart) and p.Leq(SegEnd)) then    begin      Result := True;      Break;    end;  end;end; procedure TSegments.Draw(Bmp: TBGRABitmap);var  i: Integer;  SegStart, SegEnd: TDyadic;  PenWidth: Single;begin  PenWidth := 1.0;  for i := 0 to High(FSegments) do  begin    SegStart := FSegments[i][0];    SegEnd := FSegments[i][1];  Bmp.DrawLineAntialias(Round(SegStart.ToDecimal), Round(SegStart.ToDecimal),                          Round(SegEnd.ToDecimal), Round(SegEnd.ToDecimal),                        BGRABlack, PenWidth);  end;end; { TForm1 } constructor TForm1.Create(TheOwner: TComponent);begin  inherited Create(TheOwner);  Directions[0] := TList.Create;  Directions[1] := TList.Create;end; procedure TForm1.FormPaint(Sender: TObject);var  Bitmap: TBGRABitmap;  Segments: TSegments;begin  Segments := TSegments.Create(TDyadic.Create(0, 0), [], ['X', 'Y']);  Bitmap := TBGRABitmap.Create(ClientWidth, ClientHeight);  try    Bitmap.Fill(BGRAWhite);    StepFractal();    Segments.Draw(Bitmap);  Bitmap.Draw(Canvas, 0, 0);  finally    Bitmap.Free;    Segments.Free;  end;end; procedure TForm1.StepFractal;var  n, x, i, j: Integer;  ATop: Integer;  Move, Hit: TList;  A, P, SegStart, SegEnd: TDyadic;  Up: Boolean;  Segment: TSegments;  S: TSegments; // Declare S as a TSegments variable for casting items from Hitbegin  Inc(i);  n := (i + (i mod 2)) div 2;  //ATop := Round(Power(2, n));   // Gives Overflow error n too large, resulting in a floating-point overflow when calculating Power(2, n)  ATop := 1 shl n; // Equivalent to Power(2, n) but using integer arithmetic and bitwise shift   Move := Directions[0];  Hit := Directions[1];   for x := 1 to ATop - 1 do  begin    if x mod 2 = 1 then    begin      P := TDyadic.Create(x, n);      Up := True;      Segment := TSegments.Create(P, [], []);       A := TDyadic.Create(0, 0);      for j := 0 to Hit.Count - 1 do      begin        S := TSegments(Hit[j]);  // Cast each item in Hit to TSegments        if S.Contains(P) then        begin          if Up then          begin            SegStart := A;            SegEnd := S.FPos;            SetLength(Segment.FSegments, Length(Segment.FSegments) + 1);            Segment.FSegments[High(Segment.FSegments)][0] := SegStart;            Segment.FSegments[High(Segment.FSegments)][1] := SegEnd;          end          else            A := S.FPos;          Up := not Up;        end;      end;       if Up then      begin        SegStart := A;      SegEnd := TDyadic.Create(1, 0);        SetLength(Segment.FSegments, Length(Segment.FSegments) + 1);        Segment.FSegments[High(Segment.FSegments)][0] := SegStart;        Segment.FSegments[High(Segment.FSegments)][1] := SegEnd;      end;     Segment.Free;    end;  end;end; end.  Attached is also a captured animated version of the JavaScript version, to explain how the fractal is contructed from horizontal and vertical line segments. ---------------------------------------------------------------------- Boleeman · 2025-07-07 20:38 I had another shot at making the Conant Gasket Fractal using Lazarus. It comes close to the JavaScript version but has some differences in certain spots (seems to be flipped and mirrored but there are parts which have long thin rectangles which don't exist in the JavaScript version). Enjoy.