2020-05-12 13:52:09 +03:00
|
|
|
{$MODE OBJFPC}
|
|
|
|
|
|
|
|
uses fpexprpars, sysutils,
|
|
|
|
BMPImage, utils;
|
|
|
|
|
|
|
|
type
|
|
|
|
TArgs = record
|
|
|
|
width: longint;
|
|
|
|
height: longint;
|
|
|
|
f: string;
|
|
|
|
zoom: extended;
|
|
|
|
centerX: extended;
|
|
|
|
centerY: extended;
|
|
|
|
outPath: string;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure setDefaultArgs(var args: TArgs);
|
|
|
|
begin
|
|
|
|
with args do begin
|
|
|
|
width := 512;
|
|
|
|
height := 512;
|
|
|
|
f := 'sin(x) - y';
|
|
|
|
zoom := 10;
|
|
|
|
centerX := 0;
|
|
|
|
centerY := 0;
|
|
|
|
outPath := './function_images';
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure getArgs(var args: TArgs);
|
|
|
|
var
|
|
|
|
param: string;
|
|
|
|
begin
|
|
|
|
try
|
|
|
|
args.width := strToInt(ParamStr(1));
|
|
|
|
args.height := strToInt(ParamStr(2));
|
|
|
|
args.f := ParamStr(3);
|
2020-05-14 18:17:36 +03:00
|
|
|
args.zoom := StrToFloat(ParamStr(4));
|
2020-05-12 13:52:09 +03:00
|
|
|
args.centerX := StrToFloat(ParamStr(5));
|
|
|
|
args.centerY := StrToFloat(ParamStr(6));
|
|
|
|
|
|
|
|
param := ParamStr(7);
|
|
|
|
if (length(param) > 0) then begin
|
|
|
|
args.outPath := param;
|
|
|
|
end;
|
|
|
|
except
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function getFilename(args: TArgs): string;
|
|
|
|
var
|
|
|
|
fname: string;
|
|
|
|
fn: string;
|
|
|
|
begin
|
|
|
|
fname := '{f}_(z={z},cx={cx},cy={cy},WxH={w}x{h}).bmp';
|
|
|
|
|
|
|
|
fn := stringReplace(args.f, ' ', '', [rfReplaceAll]);
|
|
|
|
fname := StringReplace(fname, '{f}', fn, []);
|
|
|
|
fname := StringReplace(fname, '{z}', floatToStr(args.zoom), []);
|
|
|
|
fname := StringReplace(fname, '{cx}', floatToStr(args.centerX), []);
|
|
|
|
fname := StringReplace(fname, '{cy}', floatToStr(args.centerY), []);
|
|
|
|
fname := StringReplace(fname, '{w}', intToStr(args.width), []);
|
|
|
|
fname := StringReplace(fname, '{h}', intToStr(args.height), []);
|
|
|
|
|
|
|
|
getFilename := fname;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function getPxData(args: TArgs): TPxData;
|
|
|
|
var
|
|
|
|
pxData: TPxData;
|
|
|
|
fParser: TFPExpressionParser;
|
|
|
|
argX, argY: TFPExprIdentifierDef;
|
|
|
|
x, y, intense: integer;
|
2020-05-14 18:17:36 +03:00
|
|
|
convX, convY, fRes1, fRes2, pixelRadius: extended;
|
|
|
|
dfDx, dfDy, delta, gradLen: extended;
|
2020-05-12 13:52:09 +03:00
|
|
|
ineq: boolean;
|
|
|
|
begin
|
|
|
|
setLength(pxData, args.width, args.height);
|
|
|
|
fParser := TFPExpressionParser.create(nil);
|
2020-05-14 18:17:36 +03:00
|
|
|
pixelRadius := 1 / args.zoom;
|
2020-05-12 13:52:09 +03:00
|
|
|
|
|
|
|
try
|
|
|
|
fParser.builtIns := [bcMath];
|
|
|
|
argX := fParser.identifiers.addFloatVariable('x', 0.0);
|
|
|
|
argY := fParser.identifiers.addFloatVariable('y', 0.0);
|
|
|
|
//fParser.expression := 'sin(x*x*x * y*y) - cos(x*x * y*y*y)';
|
|
|
|
fParser.expression := args.f;
|
|
|
|
|
|
|
|
ineq := (pos('>', fParser.expression) <> 0) or (pos('>=', fParser.expression) <> 0) or (pos('<', fParser.expression) <> 0) or (pos('<=', fParser.expression) <> 0);
|
|
|
|
|
|
|
|
for y := 0 to args.height - 1 do begin
|
|
|
|
for x := 0 to args.width - 1 do begin
|
2020-05-14 18:17:36 +03:00
|
|
|
convX := (x - args.width / 2) / args.zoom + args.centerX;
|
2020-05-12 13:52:09 +03:00
|
|
|
convY := -(y - args.height / 2) / args.zoom + args.centerY;
|
|
|
|
|
|
|
|
if (abs(convX) < (1 / args.zoom)) or (abs(convY) < (1 / args.zoom)) then begin
|
|
|
|
pxData[x][y].set_color(0, 255, 0);
|
|
|
|
end else begin
|
|
|
|
try
|
|
|
|
argX.asFloat := convX;
|
|
|
|
argY.asFloat := convY;
|
|
|
|
if (ineq) then begin
|
|
|
|
if (fParser.evaluate().resBoolean) then begin
|
|
|
|
pxData[x][y].set_color(0, 0, 0);
|
|
|
|
end else begin
|
|
|
|
pxData[x][y].set_color(255, 255, 255);
|
|
|
|
end;
|
|
|
|
end else begin
|
2020-05-22 18:18:34 +03:00
|
|
|
fParser.expression := stringReplace(fParser.expression, '=', '-', []);
|
2020-05-14 18:17:36 +03:00
|
|
|
fRes1 := fParser.evaluate().resFloat;
|
2020-05-12 13:52:09 +03:00
|
|
|
|
2020-05-14 18:17:36 +03:00
|
|
|
// finding derivative of a function at this point (numeric)
|
|
|
|
delta := 0.00001;
|
|
|
|
|
|
|
|
argX.asFloat := convX + delta;
|
|
|
|
argY.asFloat := convY;
|
|
|
|
fRes2 := fParser.evaluate().resFloat;
|
|
|
|
dfDx := (fRes2 - fRes1) / delta;
|
|
|
|
|
|
|
|
argX.asFloat := convX;
|
|
|
|
argY.asFloat := convY + delta;
|
|
|
|
fRes2 := fParser.evaluate().resFloat;
|
|
|
|
dfDy := (fRes2 - fRes1) / delta;
|
|
|
|
|
|
|
|
// calculating length of a gradient at this point
|
|
|
|
gradLen := sqrt(sqr(dfDx) + sqr(dfDy));
|
|
|
|
|
|
|
|
if (abs(fRes1) > (gradLen * pixelRadius)) then begin
|
2020-05-12 13:52:09 +03:00
|
|
|
pxData[x][y].set_color(255, 255, 255);
|
|
|
|
end else begin
|
2020-05-14 18:17:36 +03:00
|
|
|
intense := round(abs(fRes1) / (gradLen * pixelRadius));
|
2020-05-12 13:52:09 +03:00
|
|
|
pxData[x][y].set_color(intense, intense, intense);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
except
|
|
|
|
pxData[x][y].set_color(255, 255, 255);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
finally
|
|
|
|
fParser.free();
|
|
|
|
end;
|
|
|
|
|
|
|
|
getPxData := pxData;
|
|
|
|
end;
|
|
|
|
|
|
|
|
var
|
|
|
|
args: TArgs;
|
|
|
|
outPath: string;
|
|
|
|
filename: string;
|
|
|
|
|
|
|
|
img: TBMPImage;
|
|
|
|
color: TColor;
|
|
|
|
pxData: TPxData;
|
|
|
|
begin
|
|
|
|
setDefaultArgs(args);
|
|
|
|
getArgs(args);
|
|
|
|
|
|
|
|
pxData := getPxData(args);
|
|
|
|
|
|
|
|
color.set_color(255, 255, 255);
|
|
|
|
img.init(args.width, args.height, color);
|
|
|
|
img.set_pixel_data(pxData);
|
|
|
|
|
|
|
|
filename := getFilename(args);
|
|
|
|
outPath := args.outPath + '/' + filename;
|
|
|
|
img.save(outPath);
|
|
|
|
|
|
|
|
writeln('Image generated and saved at ' + outPath);
|
|
|
|
end.
|