{$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 := 'y = sin(x)'; 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); args.zoom := StrToFloat(ParamStr(4)); 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'; // replacing illegal characters for the filename fn := stringReplace(args.f, ' ', '', [rfReplaceAll]); fn := stringReplace(fn, '*', '', [rfReplaceAll]); fn := stringReplace(fn, '/', ' div ', [rfReplaceAll]); fn := stringReplace(fn, '<', ' lt ', [rfReplaceAll]); fn := stringReplace(fn, '<=', ' le ', [rfReplaceAll]); fn := stringReplace(fn, '>', ' gt ', [rfReplaceAll]); fn := stringReplace(fn, '>=', ' ge ', [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; fStr: string; x, y, intense: integer; convX, convY, fRes1, fRes2, pixelRadius: extended; dfDx, dfDy, delta, gradLen: extended; isInequality, hasEqualSign: boolean; begin setLength(pxData, args.width, args.height); fStr := args.f; pixelRadius := 1 / args.zoom; delta := 0.00001; isInequality := (pos('>', fStr) <> 0) or (pos('>=', fStr) <> 0) or (pos('<', fStr) <> 0) or (pos('<=', fStr) <> 0); // line is drawn in point (x, y) if f(x, y) ~= 0 // so we changing y = x - 10 to y - (x - 10) hasEqualSign := pos('=', fStr) <> 0; if (hasEqualSign) then begin fStr := stringReplace(fStr, '=', '-(', []); fStr := fStr + ')'; end; try fParser := TFPExpressionParser.create(nil); fParser.builtIns := [bcMath]; argX := fParser.identifiers.addFloatVariable('x', 0.0); argY := fParser.identifiers.addFloatVariable('y', 0.0); fParser.expression := fStr; for y := 0 to args.height - 1 do begin for x := 0 to args.width - 1 do begin convX := (x - args.width / 2) / args.zoom + args.centerX; 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 (isInequality) 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 fRes1 := fParser.evaluate().resFloat; // calculating df(convX, convY)/dx argX.asFloat := convX + delta; argY.asFloat := convY; fRes2 := fParser.evaluate().resFloat; dfDx := (fRes2 - fRes1) / delta; // calculating df(convX, convY)/dy argX.asFloat := convX; argY.asFloat := convY + delta; fRes2 := fParser.evaluate().resFloat; dfDy := (fRes2 - fRes1) / delta; // calculating length of a gradient at (convX, convY) gradLen := sqrt(sqr(dfDx) + sqr(dfDy)); if (abs(fRes1) > (gradLen * pixelRadius)) then begin pxData[x][y].set_color(255, 255, 255); end else begin intense := round(abs(fRes1) / (gradLen * pixelRadius)); 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.