home *** CD-ROM | disk | FTP | other *** search
- { 3dF --- Simple viewer for real functions of form F(x,y). }
- { Parse an expression to form a syntax tree. Walk the tree }
- { to evaluate the expression on a grid over the x-y plane. }
- { Draw orthographic projection with hidden lines removed, }
- { view angle, etc. adjustable by user with cursor controls. }
- { Disclaimer: This code is a mess. It was written in a }
- { very short time to prove that expensive hardware was not }
- { required for a specific purpose. Let the user beware. }
-
- { Compile with TP 5.0. }
-
- Program p3dF;
- Uses Crt, Parse;
-
- { Assembly language assist. According to tprof, most
- opportunity for getting faster is in DrawLine,
- especially Plot (on my 4.77Mhz 8088 w/o NCP). }
- procedure SetVideo(mode: Byte); external;
- procedure ClearScreen; external;
- procedure SetPallette(pal: Byte); external;
- procedure SetBackGround(color: Byte); external;
- procedure MovePen(x, y: Word); external;
- procedure DrawLine(x, y: Word); external;
- procedure InitCloud; external;
- procedure UpdateCloud; external;
- procedure ClearImage; external;
- procedure ShowImage; external;
- {$ifdef profile}
- procedure initx; external;
- procedure inity; external;
- procedure initXinc; external;
- procedure initYinc; external;
- procedure plot; external;
- {$endif}
- {$L 3df.obj}
-
- const
- nGrids = 15; { make me 10 for faster displays of fewer grids }
- DevP = 192; { vertical device size in pixels }
- StepsInQuad = 18;
- IntScale = 32;
- ScaleBits = 5;
- ScaleBitsM1 = 4; { scale bits minus 1 (accounts for 2/1 aspect of pixels) }
- pi = 3.1415926535897932385; { Save fun call time. }
- var
- eData: array[-nGrids..nGrids, -nGrids..nGrids] of Real;
- eMin, eMax: Real;
- yData: array[0..3, -nGrids..nGrids, -nGrids..nGrids] of Integer;
- scrImg: array[0..199, 0..79] of Byte;
- baseXTbl,
- baseYTbl,
- deltaXiTbl,
- deltaYiTbl,
- deltaXjTbl, { how much to inc x for step in j }
- deltaYjTbl: array[0..StepsInQuad] of Integer; { how much to inc y for step in j }
-
- procedure FillGrid(size, exag: Real; phi: Integer);
- var
- e, de, i, j: Integer;
- sPhi, cPhi,
- imageHt, imageWd,
- unitsToDev, eScale,
- theta, dTheta, sTheta, cTheta, sizeP, scaleFac: Real;
- begin
- sPhi := Sin(phi*pi/180);
- cPhi := Cos(phi*pi/180);
- { 1.5 is really sqrt(2)*fudge to _ensure_ images lie in screen. }
- imageHt := sPhi*size*1.5+cPhi*(eMax-eMin)*exag;
- imageWd := size*1.5;
- if imageHt > imageWd/1.6 then
- unitsToDev := DevP/imageHt
- else
- unitsToDev := DevP*1.6/imageWd;
- sizeP := size*unitsToDev;
- eScale := exag*unitsToDev*cPhi*IntScale;
- for i := -nGrids to nGrids do
- for j := -nGrids to nGrids do begin
- e := Round((eData[i,j]-eMin)*eScale);
- yData[0, i, j] := e;
- yData[1, j,-i] := e;
- yData[2, -i,-j] := e;
- yData[3, -j, i] := e;
- end;
- theta := 0;
- dTheta := (pi/2)/StepsInQuad;
- scaleFac := sizeP/(nGrids+nGrids)*IntScale;
- for i := 0 to stepsInQuad-1 do begin
- sTheta := scaleFac*Sin(theta);
- cTheta := scaleFac*Cos(theta);
- deltaXiTbl[i] := Round(sTheta);
- deltaYiTbl[i] := Round(cTheta*sPhi);
- deltaXjTbl[i] := Round(cTheta);
- deltaYjTbl[i] := Round(sTheta*sPhi);
- baseXtbl[i] := Round((sizeP*0.707*Cos(theta+pi/4)+160)*IntScale);
- baseYtbl[i] := Round((DevP-sizeP*0.707*(1-Sin(theta+pi/4))*sPhi)*IntScale);
- theta := theta+dTheta;
- end;
- end;
-
- procedure DrawIt(cx, cy, size: Real);
- const
- initStep = 4;
- initDstep = 1;
- initQuad = 1;
- initPhi = 20;
- initExag = 1;
- var
- exag: Real;
- i, j, phi,
- step, quad, dStep, x00, y00, x0, y0, x, y, tx, ty,
- px, py0, py, lastPx, lastPy0, dxi, dyi, dxj, dyj: Integer;
- lastCutX, lastCutY: array[-nGrids..nGrids] of Integer;
- ch: Char;
-
- procedure WritePhi;
- begin
- GoToXY(19, 25);
- Write(phi:2);
- end;
-
- procedure WriteExag;
- begin
- GoToXY(36, 25);
- Write(exag:3:1);
- end;
-
- begin
- DirectVideo := False;
- dStep := initDstep;
- step := initStep;
- quad := initQuad;
- phi := initPhi;
- exag := initExag;
- FillGrid(size, exag, phi);
- SetVideo(6); { 640 x 200 }
- ClearScreen;
- GoToXY(1, 25);
- Write(
- 'eXit '#27#26'rot '#24#25'elev(__) PgUp/Dn exag(___) ',
- 'x ',cx:0:2, ' y ', cy:0:2, ' sz ', size:0:2,
- ' f ', eMin:0:2, '/', eMax:0:2);
- WritePhi;
- WriteExag;
- repeat
- ClearImage;
- InitCloud;
- x0 := baseXtbl[step];
- x00 := x0;
- y0 := baseYtbl[step];
- y00 := y0;
- dxi := deltaXiTbl[step];
- dyi := deltaYiTbl[step];
- dxj := deltaXjTbl[step];
- dyj := deltaYjTbl[step];
- { draw first cut }
- y := y0;
- x := x0;
- px := x shr ScaleBitsM1;
- py0 := y shr ScaleBits;
- py := (y-yData[quad, -nGrids,-nGrids]) shr ScaleBits;
- MovePen(px, py0);
- DrawLine(px, py);
- lastPx := px;
- lastPy0 := py0;
- lastCutX[-nGrids] := px;
- lastCutY[-nGrids] := py;
- for j := -nGrids+1 to nGrids do begin
- x := x - dxj;
- y := y - dyj;
- px := x shr ScaleBitsM1;
- py0 := y shr ScaleBits;
- py := (y-yData[quad, -nGrids, j]) shr ScaleBits;
- DrawLine(px, py);
- DrawLine(px, py0);
- MovePen(lastPx, lastPy0);
- DrawLine(px, py0);
- if j = 0 then
- if Odd(quad) then begin
- tx := x-dxi*3;
- ty := y+dyi*3;
- DrawLine(tx shr ScaleBitsM1, ty shr ScaleBits);
- if quad = 1 then begin
- DrawLine((tx-dxi+dxj) shr ScaleBitsM1,
- (ty+dyi+dyj) shr ScaleBits);
- MovePen(tx shr ScaleBitsM1, ty shr ScaleBits);
- DrawLine((tx-dxi-dxj) shr ScaleBitsM1,
- (ty+dyi-dyj) shr ScaleBits);
- end
- else begin
- DrawLine((tx+dxi+dxj) shr ScaleBitsM1,
- (ty-dyi+dyj) shr ScaleBits);
- MovePen(tx shr ScaleBitsM1, ty shr ScaleBits);
- DrawLine((tx+dxi-dxj) shr ScaleBitsM1,
- (ty-dyi-dyj) shr ScaleBits);
- end;
- end;
- MovePen(px, py);
- lastPx := px;
- lastPy0 := py0;
- lastCutX[j] := px;
- lastCutY[j] := py;
- end;
- UpdateCloud;
- lastPx := x00 shr ScaleBitsM1;
- lastPy0 := y00 shr ScaleBits;
- for i := -nGrids+1 to nGrids do begin
- x0 := x0+dxi;
- y0 := y0-dyi;
- y := y0;
- x := x0;
- px := x shr ScaleBitsM1;
- py0 := y shr ScaleBits;
- py := (y-yData[quad, i,-nGrids]) shr ScaleBits;
- MovePen(lastPx, lastPy0);
- DrawLine(px, py0);
- lastPx := px;
- lastPy0 := py0;
- if (i = 0) and (quad and 1 = 0) then begin
- tx := x+dxj*3;
- ty := y+dyj*3;
- MovePen(tx shr ScaleBitsM1, ty shr ScaleBits);
- if quad = 0 then begin
- DrawLine((tx+dxj-dxi) shr ScaleBitsM1,
- (ty+dyj+dyi) shr ScaleBits);
- MovePen(tx shr ScaleBitsM1, ty shr ScaleBits);
- DrawLine((tx+dxj+dxi) shr ScaleBitsM1,
- (ty+dyj-dyi) shr ScaleBits);
- end
- else begin
- DrawLine((tx-dxj-dxi) shr ScaleBitsM1,
- (ty-dyj+dyi) shr ScaleBits);
- MovePen(tx shr ScaleBitsM1, ty shr ScaleBits);
- DrawLine((tx-dxj+dxi) shr ScaleBitsM1,
- (ty-dyj-dyi) shr ScaleBits);
- end;
- MovePen(tx shr ScaleBitsM1, ty shr ScaleBits);
- DrawLine(px, py0);
- end;
- DrawLine(px, py);
- DrawLine(lastCutX[-nGrids], lastCutY[-nGrids]);
- UpdateCloud;
- MovePen(px, py);
- lastCutX[-nGrids] := px;
- lastCutY[-nGrids] := py;
- for j := -nGrids+1 to nGrids do begin
- x := x - dxj;
- y := y - dyj;
- px := x shr ScaleBitsM1;
- py := (y-yData[quad, i, j]) shr ScaleBits;
- DrawLine(px, py);
- DrawLine(lastCutX[j], lastCutY[j]);
- MovePen(px, py);
- lastCutX[j] := px;
- lastCutY[j] := py;
- UpdateCloud;
- end;
- end;
- if KeyPressed then begin
- ch := ReadKey;
- if ch = #0 then begin
- ch := ReadKey;
- case ch of
- #77: if dStep > -3 then { right arrow }
- dStep := dStep-1;
- #75: if dStep < 3 then { left arrow }
- dStep := dStep+1;
- #73: if exag < 9.5 then begin { pg up }
- if exag < 1 then
- exag := exag+0.1
- else
- exag := exag+0.5;
- FillGrid(size, exag, phi);
- WriteExag;
- end;
- #81: if exag > 0.1 then begin { pg dn }
- if exag > 1 then
- exag := exag-0.5
- else
- exag := exag-0.1;
- FillGrid(size, exag, phi);
- WriteExag;
- end;
- #72: if phi < 60 then begin { up arrow }
- phi := phi + 5;
- FillGrid(size, exag, phi);
- WritePhi;
- end;
- #80: if phi > 5 then begin { dn arrow }
- phi := phi - 5;
- FillGrid(size, exag, phi);
- WritePhi;
- end;
- #71: dStep := initDstep; { home }
- #79: begin { end }
- phi := initPhi;
- exag := initExag;
- FillGrid(size, exag, phi);
- WriteExag;
- WritePhi;
- end;
- end;
- end;
- end
- else ch := ' ';
- while KeyPressed do ch := ReadKey; { purge keyboard buffer }
- step := step+dStep;
- if step >= stepsInQuad then begin
- step := step mod stepsInQuad;
- quad := quad+1;
- if quad > 3 then
- quad := 0;
- end;
- if step < 0 then begin
- step := stepsInQuad+step mod stepsInQuad;
- quad := quad-1;
- if quad < 0 then
- quad := 3;
- end;
- ShowImage;
- until ch in [#27, 'X', 'x'];
- SetVideo(CO80);
- end;
-
- procedure ComputeFun(var expr: String; cx, cy, size: Real);
- var
- tr: Tree;
- i, j, n: Integer;
- x, y, halfSize, e: Real;
- begin
- tr := ParseExpr(expr);
- halfSize := size/2;
- eMax := -1e30; eMin := 1e30;
- n := (nGrids + nGrids + 1);
- n := n*n;
- Write('Computing F (');
- for j := -nGrids to nGrids do begin
- Write(n:4, ' left).'#8#8#8#8#8#8#8#8#8#8#8);
- x := cX + j/nGrids*halfSize;
- for i := -nGrids to nGrids do begin
- y := cY + i/nGrids*halfSize;
- e := Eval(tr, x, y);
- if e < eMin then eMin := e;
- if e > eMax then eMax := e;
- eData[i,j] := e;
- end;
- Dec(n, nGrids+nGrids+1);
- end;
- WriteLn(n:4);
- end;
-
- procedure Usage(msg: String);
- const
- nLines= 4;
- verbiage: array[1..nLines] of string = (
- 'Usage: 3df [[options] expression]'#13#10+
- ' options:'#13#10+
- ' /x<real> -- x of domain center (default 0)'#13#10+
- ' /y<real> -- y of domain center (default 0)'#13#10+
- ' /s<posReal> -- x and y size of domain (default 1)'#13#10+
- ' /? -- This help.'#13#10,
- ' expression: Optionally "quoted" function of x and y.'#13#10+
- ' Operators by precedence:'#13#10+
- ' a^b - a to b power, b >= 0 or int < 0 right associative'#13#10+
- ' -a - negate a'#13#10+
- ' a*b - a times b a/b - a divided by b left associative'#13#10,
- ' a+b - a plus b a-b - a minus b left associative'#13#10+
- ' Functions:'#13#10+
- ' Sin(a), Cos(a), Ln(a), Exp(a), Sqrt(a), Atan(a), Abs(a),'#13#10+
- ' Min(a,b), Max(a,b). Case is ignored.'#13#10+
- 'Example:'#13#10+
- ' 3df /x1 /y1.2 /s2 min(2,sqrt(abs(1 - x^2-y^2)))'#13#10#10,
- 'This is freeware. I ask only for credit and bug reports.'#13#10+
- ' MAJ Gene Ressler (ressler@cs.cornell.edu)'#13#10+
- ' 124 Pine Tree Rd, Ithaca, NY 14580'#13#10);
- var
- i: Integer;
- begin
- WriteLn(msg+'.');
- for i := 1 to nLines do Write(verbiage[i]);
- Halt;
- end;
-
- function GetRealParam(name: String; var i: Integer): Real;
- var
- x: Real;
- code: Integer;
- param: String;
- begin
- param := Copy(ParamStr(i), 3, 255);
- if Length(param) > 0 then
- Val(param, x, code)
- else begin
- inc(i); if i > ParamCount then Usage('Missing '+name);
- Val(ParamStr(i), x, code);
- end;
- if code <> 0 then Usage('Bad '+name);
- GetRealParam := x;
- end;
-
- procedure GetParams(var expr: String; var cx, cy, size: Real);
- var
- i, n, code: Integer;
- param: String[2];
- begin
- n := ParamCount;
-
- { Demo case. }
- if n = 0 then begin
- expr := '0.25*cos(28*x^2 + 20*y^2) / (9*x^2 + 6*y^2 + 1)';
- Writeln('/? for help. Running demo F: ', expr);
- cx := 0.3;
- cy := 0.2;
- size := 1;
- Exit;
- end;
-
- expr := '';
- cx := 0;
- cy := 0;
- size := 1;
-
- i := 1;
- repeat
- param := Copy(ParamStr(i), 1, 2);
- if (param[1] = '/') then
- if Length(param) > 1 then
- case param[2] of
- '?': Usage('Help..');
- 's': begin
- size := GetRealParam('size', i);
- if size <= 0 then Usage('Non-positive size');
- end;
- 'x': cx := GetRealParam('x', i);
- 'y': cy := GetRealParam('y', i);
- else Usage('Unknown switch '+param);
- end
- else Usage('Missing switch character')
- else begin
- repeat
- if Length(expr) > 0 then expr := expr+' ';
- expr := expr + ParamStr(i);
- inc(i);
- until i > n;
- if expr[1] = '"' then begin
- expr := Copy(expr, 2, 255);
- if expr[Length(expr)] = '"' then
- dec(expr[0])
- else
- Usage('Missing "');
- end;
- Exit; { Normal exit. }
- end;
- inc(i);
- until i > n;
- Usage('Missing expression');
- end;
-
- procedure Main;
- var
- cx, cy, size: Real;
- expr: String;
- begin
- WriteLn('3dF v0.1 MAJ Gene Ressler April 91');
- GetParams(expr, cx, cy, size);
- ComputeFun(expr, cx, cy, size);
- DrawIt(cx, cy, size);
- end;
-
- begin
- Main;
- end.