(*$N+*) program BGIGrapher; uses Crt, Dos, Graph; Const MaxData = 600; Type Data = array [1..MaxData] of Extended; GraphContents = Record X,Y:Data; XMin,XMax,YMin,YMax:Extended; AbsXMax,AbsYMax:Extended; end; var FileName,Labels: string; Graphs:GraphContents; (* Some important info. on data *) NoOfData,i,Starting,Ending:integer; GraphDriver : integer; (* The Graphics device driver *) GraphMode : integer; (* The Graphics mode value *) MaxX, MaxY : word; (* The maximum resolution of the screen *) ErrorCode : integer; (* Reports any graphics errors *) MaxColor : word; (* The maximum color value available *) OldExitProc : Pointer; (* Saves exit procedure address *) (* Display help screen *) procedure HelpScreen; begin Writeln ('FreeWare Experimental Grapher '); Writeln ('(C)opyright TakaPuna 1991 Version 1.1'); Writeln ('Portions of the codes are (C)opyrighted by Borland International '); Writeln; Writeln ('Command Line Options:'); Writeln (' FileName [All Labels] [Starting Ending Labels] '); Writeln; Writeln (' FileName : Data file from a text file '); Writeln (' Starting : Starting index to view (integer) '); Writeln (' Ending : Ending index to view (integer) '); Writeln (' Labels : Axis labels '); Writeln; Writeln ('Example:'); Writeln ('- To display all points and label the axis as'); Writeln (' "X vs Y" >: Grapher FileName All X vs Y '); Writeln ('- To display points #10 to #20 and label the axis as'); Writeln (' "X vs Y" >: Grapher FileName 10 20 X vs Y '); Writeln ('- All parameter must appear in order !!!!'); Halt (1); end; (*$F+*) (* Trap run time errors *) procedure UserExitProc; begin ExitProc := OldExitProc; (* Restore exit procedure address *) CloseGraph; end; (* UserExitProc *) (*$F-*) procedure Initialize; (* Initialize graphics and report any errors that may occur *) var InGraphicsMode : boolean; (* Flags initialization of graphics mode *) PathToDriver : string; (* Stores the DOS path to *.BGI & *.CHR *) begin (* when using Crt and graphics, turn off Crt's memory-mapped writes *) DirectVideo := False; OldExitProc := ExitProc; (* save previous exit proc *) ExitProc := @UserExitProc; (* insert our exit proc in chain *) PathToDriver := ''; repeat (*$IFDEF Use8514*) (* check for Use8514 $DEFINE *) GraphDriver := IBM8514; GraphMode := IBM8514Hi; (*$ELSE*) GraphDriver := Detect; (* use autodetection *) (*$ENDIF*) InitGraph(GraphDriver, GraphMode, PathToDriver); ErrorCode := GraphResult; (* preserve error return *) if ErrorCode <> grOK then (* error? *) begin Writeln('Graphics error: ', GraphErrorMsg(ErrorCode)); if ErrorCode = grFileNotFound then (* Can't find driver file *) begin Writeln('Enter full path to BGI driver or type to quit:'); Readln(PathToDriver); end else Halt(1); (* Some other error: terminate *) end; until ErrorCode = grOK; MaxColor := GetMaxColor; (* Get the maximum allowable drawing color *) MaxX := GetMaxX; (* Get screen resolution values *) MaxY := GetMaxY; end; (* Initialize *) (* Returns true if file exists *) function FileExist (FileName:string):boolean; Var F:Text; begin (*$I-*) Assign (F,FileName); Reset (F); FileExist:= IOResult = 0; (*$I+*) end; function Int2Str(L : LongInt) : string; (* Converts integer to string *) var S : string; begin Str(L, S); Int2Str := S; end; (* Int2Str *) function Str2Int(S:string):integer; (* Converts string to integer *) var L,Code:integer; begin Val(S,L,Code); if Code <> 0 then begin Writeln ('Integer values expected as parameters.'); Halt(1); end else Str2Int:=L; end; function Real2Str(L : Extended) : string; (* Converts Extended numbers to string *) var S : string; begin Str(L:0, S); Real2Str := S; end; (* Real2Str *) (* Check if the Parameter is equal to the Switch *) function IsEqual(Parameter,Switch:String):boolean; var Quit:boolean; begin Quit:=false; i:=0; While not Quit do begin Inc(i); Quit:=(Upcase(Switch[i])<>Upcase(Parameter[i])) or (i=Length(Switch)); end; if i=Length(Switch) then IsEqual:=true else IsEqual:=false; end; procedure DefaultColors; (* Select the maximum color in the Palette for the drawing color *) begin SetColor(MaxColor); end; (* DefaultColors *) procedure FullPort; (* Set the view port to the entire screen *) begin SetViewPort(0, 0, MaxX, MaxY, ClipOff); end; (* FullPort *) procedure MainWindow(Header : string); (* Make a default window and view port for demos *) begin DefaultColors; (* Reset the colors *) SetTextStyle(SmallFont, HorizDir, 5); SetTextJustify(CenterText, TopText); (* Left justify text *) FullPort; (* Full screen view port *) OutTextXY(MaxX div 2,0, Header); (* Draw the header *) (* Draw main window *) SetViewPort (Round(0.2*MaxX),Round(0.1*MaxY),Round(0.9*MaxX),Round(0.7*MaxY), ClipOff); end; (* MainWindow *) procedure WaitToGo; (* Wait for the user to abort the program or continue *) const Esc = #27; var Ch : char; begin repeat until KeyPressed; Ch := ReadKey; if ch = #0 then ch := readkey; (* trap function keys *) if Ch = Esc then Halt(0) (* terminate program *) else ClearDevice; (* clear screen *) end; (* WaitToGo *) (* Initialize the Graph Record *) procedure InitGlobal (UserGivenFile:string); var FileName:text; j:integer; a,b:Extended; TXmax,TXmin,TYmax,TYmin:Extended; Quit:boolean; begin NoOfData:=0; j:=1; i:=1; ClrScr; Quit:=false; Assign (FileName,UserGivenFile); Reset (FileName); While not Quit do begin (*$I-*) Readln (FileName,a,b); (*$I+*) if IOResult = 0 then begin if ParamCount > 2 then begin if (j>=Starting) and (j<=Ending) then begin Graphs.X[i]:=a; Graphs.Y[i]:=b; Inc (NoOfData); Inc (i); end; end else begin Graphs.X[i]:=a; Graphs.Y[i]:=b; Inc (NoOfData); Inc(i); end; Inc(j); end else Writeln ('Some Invalid entries skipped '); Quit:=(NoOfData = MaxData) or (j=Ending) or EOF(FileName); end; (* While not Quit *) Close (FileName); if (NoOfData = MaxData) then begin Writeln ('Too many data .....Aborting program. Maximum data = ',MaxData); Halt(1); end else begin TXmax:=Graphs.X[1]; (* find the maximum and the minimum of data *) TXMin:=Graphs.X[1]; TYMax:=Graphs.Y[1]; TYMin:=Graphs.Y[1]; for i:=1 to NoOfData do begin if Graphs.X[i] > TXMax then TXMax:=Graphs.X[i]; if Graphs.X[i] < TXMin then TXMin:=Graphs.X[i]; if Graphs.Y[i] > TYMax then TYMax:=Graphs.Y[i]; if Graphs.Y[i] < TYMin then TYMin:=Graphs.Y[i]; end; Graphs.XMax:=TXMax; Graphs.XMin:=TXMin; Graphs.YMax:=TYMax; Graphs.YMin:=TYMin; if Graphs.XMax= Graphs.XMin then begin Writeln ('Data does not make sense.'); Halt(1); end; if (Abs(TXmin) > Abs(TXMax)) then Graphs.AbsXMax:=Abs(TXMin) else Graphs.AbsXMax:=Abs(TXMax); if (Abs(TYmin) > Abs(TYMax)) then Graphs.AbsYMax:=Abs(TYMin) else Graphs.AbsYMax:=Abs(TYMax); end; end; (* InitGlobal *) procedure Status(Msg : string); (* report the status of graph *) begin FullPort; DefaultColors; SetTextJustify(CenterText, TopText); SetLineStyle(SolidLn, 0, NormWidth); SetFillStyle(EmptyFill, 0); OutTextXY(MaxX div 2,MaxY-(TextHeight('M')+20),Msg); (* Draw main window back again *) SetViewPort (Round(0.2*MaxX),Round(0.1*MaxY),Round(0.9*MaxX),Round(0.7*MaxY), ClipOff); end; (* Status *) procedure DrawBorder; (* Draw a border around the current view port and labels the axis *) var ViewPort : ViewPortType; IncX,IncY,Start:Extended; Mult:Extended; begin if (Graphs.XMax > 0) and (Graphs.XMin >= 0) then IncX:=(Graphs.XMax-Graphs.XMin)/4; if (Graphs.XMax < 0) and (Graphs.XMin < 0) then IncX:=(-Abs(Graphs.XMax)+Abs(Graphs.XMin))/4; if (Graphs.XMax >= 0) and (Graphs.XMin < 0) then IncX:=(Abs(Graphs.XMax)+Abs(Graphs.XMin))/4; if (Graphs.YMax=Graphs.YMin) then IncY:=Abs(Graphs.YMax/4) else begin If (Graphs.YMax > 0) and (Graphs.YMin >= 0) then IncY:=(Graphs.YMax-Graphs.YMin)/4; If (Graphs.YMax < 0) and (Graphs.YMin < 0) then IncY:=(-Abs(Graphs.YMax)+Abs(Graphs.YMin))/4; if (Graphs.YMax >= 0) and (Graphs.YMin < 0) then IncY:=(Abs(Graphs.YMax)+Abs(Graphs.YMin))/4; end; Status ('Step size X = '+Real2Str(IncX)+ ' Step size Y ='+Real2Str(IncY)); DefaultColors; SetLineStyle(SolidLn,0, ThickWidth); GetViewSettings(ViewPort); SetTextStyle(SmallFont, HorizDir, 5); with ViewPort do begin Rectangle(0, 0, x2-x1, y2-y1); (* Rectangle edges *) Line (X2-X1+4,0,X2-X1-1,0); Line (0,-4,0,1); Line (0,Y2-Y1+4,0,Y2-Y1-1); Line (-4,0,1,0); (* Draw ticks on Y axis *) Mult:=0.25; for i:=1 to 4 do begin Line (X2-X1+4,Round(Mult*(Y2-Y1)),X2-X1-1,Round(Mult*(Y2-Y1))); Line (-4,Round(Mult*(Y2-Y1)),1,Round(Mult*(Y2-Y1))); Mult:=Mult+0.25; end; (* Label the Y Axis *) if (Graphs.YMax=Graphs.YMin) then Start:=Graphs.YMax-(2*IncY) else Start:=Graphs.YMin; Mult:=1; for i:=1 to 5 do begin OutTextXY (-4-TextWidth(Real2Str(Start)),Round(Mult*(Y2-Y1))-TextHeight(Real2Str(Start)), Real2Str(Start)); Mult:=Mult-0.25; Start:=Start+IncY; end; (* Draw ticks on X axis *) Mult:=0.25; for i:=1 to 4 do begin Line (Round(Mult*(X2-X1)),-4,Round(Mult*(X2-X1)),1); Line (Round(Mult*(X2-X1)),Y2-Y1+4,Round(Mult*(X2-X1)),Y2-Y1-1); Mult:=Mult+0.25; end; (* Label the X axis *) Mult:=0; Start:=Graphs.Xmin; for i:=1 to 5 do begin OutTextXY (Round(Mult*(X2-X1))-TextWidth(Real2Str(Start)) div 4,Y2-Y1+TextHeight(Real2Str(Start)), Real2Str(Start)); Mult:=Mult+0.25; Start:=Start+IncX; end; end; (* with ViewPort *) end; (* DrawBorder *) procedure ScaleData; (* Scale the data such that it will fall inside the viewport *) var ShiftX,ShiftY:integer; Xscale,YScale:Extended; ViewPort:ViewPortType; begin GetViewSettings(ViewPort); With ViewPort do begin (* Put some conditions on X *) if (Graphs.XMax > 0 ) and (Graphs.XMin > 0) then (* XMax > 0 *) begin (* XMin > 0 *) XScale:=(X2-X1)/(Graphs.XMax-Graphs.XMin); ShiftX:=-Round(Graphs.XMax*XScale-X2+X1); end else begin if Graphs.XMax > 0 then (* absolutely no zero *) begin ShiftX:=Round((1-(Graphs.XMax/(Graphs.XMax + Abs(Graphs.Xmin))))*(X2-X1)); XScale:=(X2-(ShiftX+X1))/(Graphs.XMax); if XScale = 0 then XScale:=(X2-X1)/(Graphs.AbsXMax) end else begin XScale:=(X2-X1)/(Graphs.XMax-Graphs.XMin); ShiftX:=-Round(Graphs.XMax*XScale-X2+X1); end; end; (* Put Some condition on Y *) if (Graphs.YMax=Graphs.YMin) then begin for i:=1 to NoOfData do begin Graphs.X[i]:=Graphs.X[i]*XScale+ShiftX; Graphs.Y[i]:=0.5*(Y2-Y1); end; end else begin if (Graphs.YMax > 0 ) and (Graphs.YMin > 0) then (* YMax > 0 *) begin (* YMin > 0 *) YScale:=(Y2-Y1)/(Graphs.YMax-Graphs.YMin); ShiftY:=-Round(Graphs.YMax*YScale-y2+y1); end else begin if (Graphs.YMax > 0) then begin ShiftY:=Round((1-(Graphs.YMax/(Graphs.YMax + Abs(Graphs.Ymin))))*(Y2-Y1)); YScale:=(Y2-(ShiftY+Y1))/Graphs.YMax; if YScale= 0 then YScale:=(Y2-Y1)/(Graphs.AbsYMax); end else begin YScale:=(Y2-Y1)/(Graphs.YMax-Graphs.YMin); ShiftY:=-Round(Graphs.YMax*YScale-y2+y1); end; end; for i:=1 to NoOfData do begin Graphs.X[i]:=Graphs.X[i]*XScale+ShiftX; Graphs.Y[i]:=Graphs.Y[i]*YScale+ShiftY; end; end; end; end; (* Scale Data *) procedure Plot; (* plot the given data in the array *) var ViewPort:ViewPortType; begin SetLineStyle(SolidLn, 0, NormWidth); GetViewSettings(ViewPort); With ViewPort do begin MoveTo (Round(Graphs.X[1]), (Y2-Y1)-Round(Graphs.Y[1])); for i:=2 to NoOfData do LineTo (Round(Graphs.X[i]), (Y2-Y1)-Round(Graphs.Y[i])); end; end; (* Plot *) (* Handles command line input *) procedure CommandLine; begin if ParamCount = 0 then HelpScreen else begin Labels:=''; FileName:=ParamStr(1); if NOT FileExist(FileName) then begin Writeln ('File ',FileName,' does not exist.'); Halt(1); end; if ParamCount > 2 then begin if Not (IsEqual(ParamStr(2),'All')) then begin Starting:=Str2Int(ParamStr(2)); Ending:=Str2Int(ParamStr(3)); if (Starting > Ending) then begin Writeln ('Starting index must be less than ending index. '); Halt(1); end; for i:=4 to ParamCount do Labels:=Labels +' '+ ParamStr(i); end else begin Starting:=1; Ending:=MaxData; for i:=3 to ParamCount do Labels:=Labels +' '+ ParamStr(i); end; end; end; end; begin (* program body *) ClrScr; CommandLine; InitGlobal (FileName); Initialize; MainWindow (Labels); ScaleData; DrawBorder; Plot; WaitToGo; end.