2020-05-12 13:52:09 +03:00
|
|
|
uses sysutils, BMPImage, ucomplex, utils;
|
2020-04-26 16:08:03 +03:00
|
|
|
|
2020-05-22 18:18:34 +03:00
|
|
|
// todo: generalize get_filename function
|
|
|
|
// todo: camelCase
|
|
|
|
// todo: multibrot fraction power
|
|
|
|
|
2020-04-26 16:08:03 +03:00
|
|
|
type
|
|
|
|
TArgs = record
|
|
|
|
width: longint; // TryStrToInt wants longint
|
|
|
|
height: longint;
|
2020-06-18 17:35:30 +03:00
|
|
|
power: longint;
|
2020-04-26 16:08:03 +03:00
|
|
|
zoom: extended;
|
|
|
|
center_x: extended;
|
|
|
|
center_y: extended;
|
|
|
|
out_path: string;
|
|
|
|
end;
|
|
|
|
|
|
|
|
const save_path = './images/generated/';
|
2020-05-22 18:18:34 +03:00
|
|
|
const help_text = 'Usage: command <power = 2> <width = 512> <height = 512> <zoom = 1> <centerX = 0> <centerY = 0> <out_path = ./multibrot_images>';
|
2020-04-26 16:08:03 +03:00
|
|
|
|
2020-05-22 18:18:34 +03:00
|
|
|
function get_filename(base_name: string; zoom: extended; center_x: extended; center_y: extended): string;
|
|
|
|
var
|
|
|
|
z, cx, cy: string;
|
2020-04-26 16:08:03 +03:00
|
|
|
begin
|
2020-05-22 18:18:34 +03:00
|
|
|
z := FloatToStr(zoom);
|
|
|
|
cx := FloatToStr(center_x);
|
|
|
|
cy := FloatToStr(center_y);
|
2020-04-26 16:08:03 +03:00
|
|
|
|
2020-05-22 18:18:34 +03:00
|
|
|
get_filename := base_name + '_(z=' + z + ';cx=' + cx + ';cy=' + cy + ').bmp'
|
2020-04-26 16:08:03 +03:00
|
|
|
end;
|
|
|
|
|
2020-05-22 18:18:34 +03:00
|
|
|
function f(x: integer): integer;
|
2020-04-26 16:08:03 +03:00
|
|
|
begin
|
2020-05-22 18:18:34 +03:00
|
|
|
f := round(2 * sin(x) + 3);
|
|
|
|
end;
|
2020-04-26 16:08:03 +03:00
|
|
|
|
|
|
|
function fc(x: complex; c: complex; n: integer): complex;
|
|
|
|
begin
|
|
|
|
fc := complex_sum(x.power(n), c);
|
|
|
|
end;
|
|
|
|
|
|
|
|
function translate_point(p: Point2D; width: integer; height: integer): Point2D;
|
|
|
|
var
|
|
|
|
base_zoom: integer;
|
|
|
|
begin
|
|
|
|
base_zoom := round(width * 0.8) div 2;
|
|
|
|
p.update((p.x - width / 2) / base_zoom, (p.y - height / 2) / base_zoom);
|
|
|
|
translate_point := p;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function multibrot_check(x: extended; y: extended; n: integer): integer;
|
|
|
|
// f defined as f(x) = x^2 + c, where c (complex number) is a constant
|
|
|
|
// point (x, y) is in Mandelbrot's set if f(f(...f(0))) converges, c = x + yi
|
|
|
|
var
|
|
|
|
i: integer;
|
|
|
|
v, c: complex;
|
|
|
|
begin
|
|
|
|
i := 0;
|
|
|
|
v.update(0, 0);
|
|
|
|
while (i < 255) and (v.length() <= 2) do begin
|
|
|
|
c.update(x, y);
|
|
|
|
v := fc(v, c, n);
|
|
|
|
inc(i);
|
|
|
|
end;
|
|
|
|
multibrot_check := i;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure setDefaultArgs(var args: TArgs);
|
|
|
|
begin
|
|
|
|
with args do begin
|
|
|
|
width := 512;
|
|
|
|
height := 512;
|
2020-06-18 17:35:30 +03:00
|
|
|
power := 2;
|
2020-04-26 16:08:03 +03:00
|
|
|
zoom := 1;
|
|
|
|
center_x := 0;
|
|
|
|
center_y := 0;
|
2020-06-18 17:35:30 +03:00
|
|
|
out_path := './multibrot_images';
|
2020-04-26 16:08:03 +03:00
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function getArgs(var args: TArgs): boolean;
|
|
|
|
var
|
|
|
|
error: boolean;
|
|
|
|
begin
|
|
|
|
error := false;
|
|
|
|
|
|
|
|
if (ParamCount > 7) then begin
|
|
|
|
error := true;
|
|
|
|
end else begin
|
|
|
|
if (ParamCount >= 1) then begin
|
|
|
|
if (not TryStrToInt(ParamStr(1), args.width)) then error := true;
|
|
|
|
end;
|
|
|
|
if (ParamCount >= 2) then begin
|
|
|
|
if (not TryStrToInt(ParamStr(2), args.height)) then error := true;
|
|
|
|
end;
|
|
|
|
if (ParamCount >= 3) then begin
|
|
|
|
if (not TryStrToInt(ParamStr(3), args.power)) then error := true;
|
|
|
|
end;
|
|
|
|
if (ParamCount >= 4) then begin
|
|
|
|
if (not TryStrToFloat(ParamStr(4), args.zoom)) then error := true;
|
|
|
|
end;
|
|
|
|
if (ParamCount >= 5) then begin
|
|
|
|
if (not TryStrToFloat(ParamStr(5), args.center_x)) then error := true;
|
|
|
|
end;
|
|
|
|
if (ParamCount >= 6) then begin
|
|
|
|
if (not TryStrToFloat(ParamStr(6), args.center_y)) then error := true;
|
|
|
|
end;
|
|
|
|
if (ParamCount = 7) then begin
|
|
|
|
args.out_path := ParamStr(7);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
getArgs := not error;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function generateMultibrot(
|
|
|
|
width: integer; height: integer;
|
|
|
|
power: longint; zoom: extended; center_x: extended; center_y: extended
|
|
|
|
): TPxData;
|
|
|
|
var
|
|
|
|
color: TColor;
|
|
|
|
i, j, mb_out: integer;
|
|
|
|
p: Point2D;
|
|
|
|
palette: array[0..255] of TColor;
|
|
|
|
px_data: TPxData;
|
|
|
|
begin
|
|
|
|
setLength(px_data, width, height);
|
|
|
|
|
|
|
|
color.reset();
|
|
|
|
|
|
|
|
for i := 0 to 254 do begin
|
|
|
|
palette[i].set_color(i, abs(64 - i), abs(128 - i));
|
|
|
|
end;
|
|
|
|
palette[255].set_color(0, 0, 0);
|
|
|
|
|
|
|
|
for i := 0 to width - 1 do begin
|
|
|
|
for j := 0 to height - 1 do begin
|
|
|
|
p.update(i, j);
|
|
|
|
p := translate_point(p, width, height);
|
|
|
|
|
|
|
|
p.x := (p.x / zoom) + center_x;
|
|
|
|
p.y := (p.y / zoom) + center_y;
|
|
|
|
|
|
|
|
color.reset();
|
|
|
|
if ((abs(p.x) < 0.000001) or (abs(p.y) < 0.000001)) then begin
|
|
|
|
color.g := 255;
|
|
|
|
end else begin
|
|
|
|
mb_out := multibrot_check(p.x, p.y, power);
|
|
|
|
color := palette[mb_out];
|
|
|
|
end;
|
|
|
|
|
|
|
|
px_data[i][j] := color;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
generateMultibrot := px_data;
|
|
|
|
end;
|
|
|
|
|
|
|
|
var
|
|
|
|
args: TArgs;
|
|
|
|
img: TBMPImage;
|
|
|
|
out_path, fname: string;
|
|
|
|
px_data: TPxData;
|
|
|
|
begin
|
|
|
|
setDefaultArgs(args);
|
|
|
|
|
|
|
|
if (getArgs(args)) then begin
|
|
|
|
px_data := generateMultibrot(args.width, args.height, args.power, args.zoom, args.center_x, args.center_y);
|
|
|
|
|
|
|
|
img.init(px_data);
|
|
|
|
|
|
|
|
fname := get_filename('mb' + IntToStr(args.power) + '_set', args.zoom, args.center_x, args.center_y);
|
|
|
|
out_path := args.out_path + '/' + fname;
|
|
|
|
|
|
|
|
img.save(out_path, false);
|
|
|
|
img.done();
|
|
|
|
|
|
|
|
writeln(fname);
|
|
|
|
end else begin
|
|
|
|
writeln(help_text);
|
|
|
|
end;
|
|
|
|
end.
|
|
|
|
|