diff --git a/.gitignore b/.gitignore index 867f741..30d850f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +images/* + *.o *.ppu *.exe diff --git a/build.bat b/build.bat index 5a24833..0ba3890 100644 --- a/build.bat +++ b/build.bat @@ -1,9 +1,15 @@ -rm generator.exe +rm multibrot_imaging.exe +rm function_imaging.exe rm gui.exe cd src + fpc mandelbrot_imaging.pas -mv mandelbrot_imaging.exe ../generator.exe +mv mandelbrot_imaging.exe ../multibrot_imaging.exe + +fpc function_imaging.pas +mv function_imaging.exe ../function_imaging.exe + cd gui fpc gui.pas mv gui.exe ../../gui.exe diff --git a/generated_images/.gitignore b/function_images/.gitignore similarity index 100% rename from generated_images/.gitignore rename to function_images/.gitignore diff --git a/multibrot_images/.gitignore b/multibrot_images/.gitignore new file mode 100644 index 0000000..4a04015 --- /dev/null +++ b/multibrot_images/.gitignore @@ -0,0 +1 @@ +*.bmp \ No newline at end of file diff --git a/src/function_imaging.pas b/src/function_imaging.pas index 0f50513..4a0a454 100644 --- a/src/function_imaging.pas +++ b/src/function_imaging.pas @@ -106,6 +106,7 @@ begin pxData[x][y].set_color(255, 255, 255); end; end else begin + fParser.expression := stringReplace(fParser.expression, '=', '-', []); fRes1 := fParser.evaluate().resFloat; // finding derivative of a function at this point (numeric) diff --git a/src/gui/constants.pas b/src/gui/constants.pas index 54aa779..1b7e546 100644 --- a/src/gui/constants.pas +++ b/src/gui/constants.pas @@ -2,7 +2,10 @@ unit constants; interface const cmAbout = 1001; - const cmGenerate = 1002; + const cmMbGenerate = 1002; + const cmFnGenerate = 1003; + const cmMbNew = 1004; + const cmFnNew = 1005; implementation end. diff --git a/src/gui/gui.pas b/src/gui/gui.pas index 25a5bfb..642ed72 100644 --- a/src/gui/gui.pas +++ b/src/gui/gui.pas @@ -4,8 +4,8 @@ uses utils, constants; type - PGenerateData = ^TGenerateData; - TGenerateData = record + PMbGenerateData = ^TMbGenerateData; + TMbGenerateData = record path: string[128]; width: string[16]; height: string[16]; @@ -15,46 +15,72 @@ type centerY: string[64]; end; + PFnGenerateData = ^TFnGenerateData; + TFnGenerateData = record + path: string[128]; + width: string[16]; + height: string[16]; + fn: string[255]; + zoom: string[64]; + centerX: string[64]; + centerY: string[64]; + end; + PGeneratorWindow = ^TGeneratorWindow; TGeneratorWindow = object(TDialog) pGeneratingBtn: PButton; winRect: TRect; curY: integer; - constructor Init; procedure addField(caption: string; maxLength: integer); function addButton(caption: string; command: integer): PButton; end; + PMbGeneratorWindow = ^TMbGeneratorWindow; + TMbGeneratorWindow = object(TGeneratorWindow) + constructor Init; + end; + + PFnGeneratorWindow = ^TFnGeneratorWindow; + TFnGeneratorWindow = object(TGeneratorWindow) + constructor Init; + end; + TMSApp = object(TApplication) - GeneratorWindow: PGeneratorWindow; + MbGeneratorWindow: PMbGeneratorWindow; + FnGeneratorWindow: PFnGeneratorWindow; procedure InitStatusLine; virtual; procedure InitMenuBar; virtual; procedure HandleEvent(var Event: TEvent); virtual; - procedure NewWindow(); - procedure GenerateMandelbrotSetImage(); + procedure NewMbWindow(); + procedure NewFnWindow(); + + procedure GenerateMultibrotSetImage(); + procedure GenerateFunctionImage(); + procedure ShowAbout(); procedure ShowSuccess(fname: string; t: extended); end; var MSApp: TMSApp; - GenerateData: TGenerateData; + MbGenerateData: TMbGenerateData; + FnGenerateData: TFnGenerateData; -procedure TMSApp.GenerateMandelbrotSetImage(); +procedure TMSApp.GenerateMultibrotSetImage(); var s: AnsiString; startTime: int64; success: boolean; begin - GeneratorWindow^.GetData(GenerateData); + MbGeneratorWindow^.GetData(MbGenerateData); startTime := getTimestamp(); - GeneratorWindow^.pGeneratingBtn^.show(); - with GenerateData do begin - success := RunCommand('./generator', [ + MbGeneratorWindow^.pGeneratingBtn^.show(); + with MbGenerateData do begin + success := RunCommand('./multibrot_imaging', [ width, height, power, @@ -64,7 +90,42 @@ begin path ], s); end; - GeneratorWindow^.pGeneratingBtn^.hide(); + MbGeneratorWindow^.pGeneratingBtn^.hide(); + + if (success) then begin + ShowSuccess(s, (getTimestamp() - startTime) / 1000); + end else begin + MessageBox( + #3'Error'#13 + + #3'Unable to execute generator!'#13, + nil, + mfInformation or mfOkButton + ); + end; +end; + +procedure TMSApp.GenerateFunctionImage(); +var + s: AnsiString; + startTime: int64; + success: boolean; +begin + FnGeneratorWindow^.GetData(FnGenerateData); + startTime := getTimestamp(); + + FnGeneratorWindow^.pGeneratingBtn^.show(); + with FnGenerateData do begin + success := RunCommand('./function_imaging', [ + width, + height, + fn, + zoom, + centerX, + centerY, + path + ], s); + end; + FnGeneratorWindow^.pGeneratingBtn^.hide(); if (success) then begin ShowSuccess(s, (getTimestamp() - startTime) / 1000); @@ -108,20 +169,20 @@ begin addButton := pbtn; end; -constructor TGeneratorWindow.Init(); +constructor TMbGeneratorWindow.Init(); var r: TRect; begin r.assign(0, 0, 60, 19); - inherited Init(r, 'Mandelbrot generator'); + inherited Init(r, 'Multibrot generator'); Options := Options or ofCentered; HelpCtx := $F000; winRect := r; curY := 2; - with GenerateData do begin - path := './generated_images'; + with MbGenerateData do begin + path := './multibrot_images'; width := '512'; height := '512'; power := '2'; @@ -138,17 +199,60 @@ begin addField('Center X:', 64); addField('Center Y:', 64); - addButton('Generate', cmGenerate); + addButton('Generate', cmMbGenerate); curY := curY - 2; pGeneratingBtn := addButton('Generating...', 0); pGeneratingBtn^.hide(); end; - -procedure TMSApp.NewWindow(); + +constructor TFnGeneratorWindow.Init(); +var + r: TRect; begin - GeneratorWindow := New(PGeneratorWindow, Init); - GeneratorWindow^.SetData(GenerateData); - InsertWindow(GeneratorWindow); + r.assign(0, 0, 60, 19); + inherited Init(r, 'Function generator'); + Options := Options or ofCentered; + HelpCtx := $F000; + + winRect := r; + curY := 2; + + with FnGenerateData do begin + path := './function_images'; + width := '512'; + height := '512'; + fn := 'sin(x*x*x * y*y) = cos(x*x * y*y*y)'; + zoom := '10'; + centerX := '0'; + centerY := '0'; + end; + + addField('Path:', 128); + addField('Width:', 16); + addField('Height:', 16); + addField('Function:', 255); + addField('Zoom:', 64); + addField('Center X:', 64); + addField('Center Y:', 64); + + addButton('Generate', cmFnGenerate); + curY := curY - 2; + pGeneratingBtn := addButton('Generating...', 0); + pGeneratingBtn^.hide(); +end; + +procedure TMSApp.NewFnWindow(); +begin + FnGeneratorWindow := New(PFnGeneratorWindow, Init); + FnGeneratorWindow^.SetData(FnGenerateData); + InsertWindow(FnGeneratorWindow); +end; + +procedure TMSApp.NewMbWindow(); +begin + MbGeneratorWindow := New(PMbGeneratorWindow, Init); + MbGeneratorWindow^.SetData(MbGenerateData); + InsertWindow(MbGeneratorWindow); end; procedure TMSApp.ShowAbout(); @@ -165,7 +269,7 @@ end; procedure TMSApp.ShowSuccess(fname: string; t: extended); begin MessageBox( - #3'Set image was generated!'#13 + + #3'Image was generated!'#13 + 'File name: ' + fname + #13 + 'Time spent: ' + FloatToStr(t) + ' s', nil, @@ -179,16 +283,24 @@ begin if Event.What = evCommand then begin case Event.Command of - cmNew: begin - NewWindow(); + cmMbNew: begin + NewMbWindow(); + ClearEvent(Event); + end; + cmFnNew: begin + NewFnWindow(); ClearEvent(Event); end; cmAbout: begin ShowAbout(); ClearEvent(Event); end; - cmGenerate: begin - GenerateMandelbrotSetImage(); + cmMbGenerate: begin + GenerateMultibrotSetImage(); + ClearEvent(Event); + end; + cmFnGenerate: begin + GenerateFunctionImage(); ClearEvent(Event); end; end; @@ -204,7 +316,7 @@ begin New(StatusLine, Init(r, NewStatusDef(0, $EFFF, NewStatusKey('~Alt+X~ Exit', kbAltX, cmQuit, - NewStatusKey('~F3~ New', kbF3, cmNew, + NewStatusKey('~F3~ New', kbF3, cmMbNew, nil )), nil) )); @@ -217,10 +329,11 @@ begin GetExtent(r); r.b.y := r.a.y + 1; MenuBar := New(PMenuBar, Init(r, NewMenu( - NewItem('~M~andelbrot', '', kbNoKey, cmNew, hcNew, + NewItem('~M~ultibrot', '', kbNoKey, cmMbNew, hcNew, + NewItem('~F~unctions', '', kbNoKey, cmFnNew, hcNew, NewItem('~A~bout', '', kbNoKey, cmAbout, hcNoContext, NewItem('~E~xit', 'Alt+X', kbAltX, cmQuit, hcExit, - nil))))) + nil)))))) ); end; diff --git a/src/mandelbrot_imaging.pas b/src/mandelbrot_imaging.pas index 585e51a..7601759 100644 --- a/src/mandelbrot_imaging.pas +++ b/src/mandelbrot_imaging.pas @@ -1,10 +1,14 @@ uses sysutils, BMPImage, ucomplex, utils; +// todo: generalize get_filename function +// todo: camelCase +// todo: multibrot fraction power + type TArgs = record + power: longint; width: longint; // TryStrToInt wants longint height: longint; - power: longint; zoom: extended; center_x: extended; center_y: extended; @@ -12,23 +16,24 @@ type end; const save_path = './images/generated/'; -const help_text = 'Usage: command '; +const help_text = 'Usage: command '; -{function f(x: integer): integer; +function get_filename(base_name: string; zoom: extended; center_x: extended; center_y: extended): string; +var + z, cx, cy: string; +begin + z := FloatToStr(zoom); + cx := FloatToStr(center_x); + cy := FloatToStr(center_y); + + get_filename := base_name + '_(z=' + z + ';cx=' + cx + ';cy=' + cy + ').bmp' +end; + +function f(x: integer): integer; begin f := round(2 * sin(x) + 3); end; -function check_a(x: integer; y: integer): boolean; -begin - check_a := sqr(x - 50) + sqr(y - 50) - 6 <= 900; -end; - -function fr(x: integer; c: integer): integer; -begin - fr := sqr(x) + c; -end;} - function fc(x: complex; c: complex; n: integer): complex; begin fc := complex_sum(x.power(n), c); @@ -63,9 +68,9 @@ end; procedure setDefaultArgs(var args: TArgs); begin with args do begin + power := 2; width := 512; height := 512; - power := 2; zoom := 1; center_x := 0; center_y := 0; @@ -157,30 +162,6 @@ var out_path, fname: string; px_data: TPxData; begin - {img.init(); - img.open('./images/k.bmp'); - - // draw sin - for i := 0 to img.get_width() - 1 do begin - color.set_color(255, 0, 0); - img.pixel_data[i][f(i)] := color; - end; - // convert color - for i := 0 to img.get_width() - 1 do begin - for j := 0 to img.get_height() - 1 do begin - color := img.pixel_data[i][j]; - new_color.r := color.b; - new_color.g := color.g; - new_color.b := color.r; - img.pixel_data[i][j] := new_color; - end; - end; - - img.set_width(400); - img.set_height(400); - img.save('./images/generated/k_generic.bmp', false); - img.done();} - setDefaultArgs(args); if (getArgs(args)) then begin @@ -195,19 +176,6 @@ begin img.done(); writeln(fname); - - {color.set_color(255, 255, 255); - img.init(100, 100, color); - color.set_color(0, 255, 0); - for i := 0 to img.get_width() - 1 do begin - for j := 0 to img.get_height() - 1 do begin - if (check_a(i, j)) then begin - img.pixel_data[i][j] := color; - end; - end; - end; - img.save('./images/generated/circle.bmp', false); - img.done();} end else begin writeln(help_text); end; diff --git a/src/utils.pas b/src/utils.pas index a5b07dc..2981a53 100644 --- a/src/utils.pas +++ b/src/utils.pas @@ -12,8 +12,6 @@ type procedure update(px: extended; py: extended); end; -function get_filename(base_name: string; zoom: extended; center_x: extended; center_y: extended): string; - implementation procedure Point2D.update(px: extended; py: extended); @@ -22,16 +20,5 @@ begin y := py; end; -function get_filename(base_name: string; zoom: extended; center_x: extended; center_y: extended): string; -var - z, cx, cy: string; -begin - z := FloatToStr(zoom); - cx := FloatToStr(center_x); - cy := FloatToStr(center_y); - - get_filename := base_name + '_(z=' + z + ';cx=' + cx + ';cy=' + cy + ').bmp' -end; - begin end. \ No newline at end of file