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 Hit
begin
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.