(**************************************************************************)
(* This program simulates a graph environment for LaTeX. Using a list of  *)
(* parameters, it generates LaTeX commands to produce a complete graph.   *)
(* see the latexgraph.doc file for detailed documentation.                *)
(* author:Sunil Podar, podar@sbcs.csnet, ...!{allegra,philabs}!sbcs!podar *)
(* Please quote the following date when sending bug reports.		  *)
(* last update: Feb 2, 1987 (fixed bugs: wasn't doings things right when  *)
(*		(0,0) was not on the graph. Also removed the printing of  *)
(*		a few of the margin commands from preamble.		  *)
(* last update: Oct 5, 1986						  *)
(**************************************************************************)
program main (input,output);
const   delim = '@';  (* Latex never uses @ except for \@ for a little *)
		      (* space before a sentence-ending period         *)
	maxchar = 'F';(* maximum types of characters permitted for plotchar*)
		      (* A..F i.e. 6 distinct chars permitted *)
type    wholeline  =  packed array[1..80] of char;
        tenchar    =  packed array[1..10] of char;
        ninechar   =  packed array[1..9] of char;
	twochar    =  packed array[1..2] of char;
	plotstufftype =	record
			  chardef : wholeline;
			  charname : wholeline
			end;
var
	plotstuff : array['A'..maxchar] of plotstufftype;
	error1,itsreal :boolean;
	picwd,picht,pos,Xdeltabar,
	   Ydeltabar,i,Xlegloc,Ylegloc,Xdeltanum,Ydeltanum : integer;
	Xaxisstrg,Yaxisstrg,capstring,blank80,labelname,fontname : wholeline;
  	legendloc,captiontwo,prepost : twochar;
	string,blank10,captiontype,fignumber : tenchar;
	plotchar,c : char;
	string9 : ninechar;
	Xscalegraph,Yscalegraph : integer;
	XGorig,YGorig,XP1orig,YP1orig,XP2orig,YP2orig : integer;
	Xscalereal,Yscalereal,xreality, yreality, xgraph, ygraph,
	unitlngth,textwd,Xorignum,Yorignum,deln,num : real;

procedure strreadline(var commandstr: wholeline);
var charac:char;
    i : integer;
    endoffile, endofline:boolean;
begin
   i:=1;
   endoffile :=false;
   endofline:=false;
   commandstr:=blank80;
   repeat
      read(charac);
      if (i < 80) then commandstr[i] := charac; (* 80th char remains @ *)
      i := i+1;
      if eof then endoffile :=true
	     else if eoln then endofline :=true
   until (endoffile or endofline);
   if (i <= 80) then commandstr[i] := delim;
   if not endoffile then readln
end; (*strreadline*)

procedure strreadword(var string: tenchar; var string9: ninechar);
var charac:char;
    i : integer;
begin
   i:=1;
   string:=blank10;
   read(charac);
   repeat
      string[i] := charac;
      read(charac);
      i := i+1
   until ((charac = '/') or (charac = ' ') or (i > 10) or eoln);
      (* so I'm reading the / without assigning it to string, neat *)
   if (eoln and (i <=10)) then string[i]:= charac;
      (* a kluge, to capture the last charac when using this procedure to *)
      (* read the argument. normally I use it only for parameters. *)
   for i:= 1 to 9 do string9[i] := string[i]
end; (*strreadword*)

procedure strwrite(str: wholeline);
var i : integer;
begin
i := 1;
while (str[i] <> delim) do
   begin
     write(str[i]); i := i+1
   end
end;

procedure legendread;
var i : integer;
    temp : twochar;
begin
   temp := '  ';
   legendloc := '  '; i := 0;
   if (not eoln) then
      repeat
          i := i+1; read(legendloc[i]);
      until  (eoln) or (i >= 2) or (legendloc[i] = '/');
   if (not eoln) then if (legendloc[i] <> '/') then read(temp[1]);
   if (temp[1] = '/') or (legendloc[i] = '/')
      then readln(Xlegloc,Ylegloc)
      else readln
end; (* legendread *)

(* in the following procedures, the boolean var XorY: true => X & false => Y*)
procedure putbars(XorY:boolean; Xpos, Ypos, deltabar, distance: integer);
var times: integer;
begin
   times := distance div abs(deltabar);
   if times > 0 then
     if XorY (* X *)
       then writeln('\multiput(',Xpos:1,',',Ypos:1,')(',
		deltabar:1,',0){',times:1,'}{\line(0,1){2}}')
       else writeln('\multiput(',Xpos:1,',',Ypos:1,')(0,',
		deltabar:1,'){',times:1,'}{\line(1,0){2}}');
end; (* putbars *)

(* see a note about putnumbers in the main program. 		*)
(* because we may have real numbers, we can't use a counter 	*)
(* in conjunction with a \multiput statement.			*)
procedure putnumbers(XorY:boolean; fixedpos, initpos:integer;
	 initnum, deln:real; deltanum:integer; limit:real);
var pos: integer;
    num: real;
begin
   num:=initnum; pos:= initpos;
   if (abs(num) - round(abs(num) - 0.5)) > 0 then itsreal := true;
   while abs(pos) < abs(limit) do
     begin
        if XorY then (* X *)
	   write('\put(',pos:1,',',fixedpos:1,'){\makebox(0,0)[t]{')
	else write('\put(',fixedpos:1,',',pos:1,'){\makebox(0,0)[r]{');
	if itsreal then writeln(num:1:2,'}}')
		   else writeln(trunc(num):1,'}}');
	pos := pos + deltanum;
	num := num + deln
     end;
end; (* putnumbers *)

begin(* main *)
   (*----------------------------------*)
   (* Initializations & default values *)
   (*----------------------------------*)
   error1 := false;
   itsreal:=false;
   for i := 1 to 10 do blank10[i] := ' ';
   for i := 1 to 79 do blank80[i] := ' '; blank80[80] := delim;
   captiontype := blank10; captiontype := 'no        '; (*default 'no' *)
   fignumber := blank10;
   fontname  := blank80; (* just playing it safe *)
   fontname  := '{normalsize}'; fontname[13] := delim;
   labelname := blank80; labelname[1] := 'n';  (* default 'no' *)
   prepost   := 'no';
   unitlngth := 1.0;
   picwd := 100; picht := 100; XP1orig := 0; YP1orig := 0;
   legendloc := 'no';
   Xscalegraph := 10; Xscalereal := 10;
   Yscalegraph := 10; Yscalereal := 10;
   Xdeltabar   := 5;  Xdeltanum  := 10;
   Ydeltabar   := 5;  Ydeltanum  := 10;
   Xorignum    := 0;
   Yorignum    := 0;
   for c := 'A' to maxchar do
     begin
	plotstuff[c].chardef[1] := 'n'; (* default value is 'no' *)
	plotstuff[c].charname := blank80;
	plotstuff[c].charname[1] := c;
	plotstuff[c].charname[2] := delim
     end;
   Xlegloc := -999999;
   Ylegloc := -999999;
   strreadword(string, string9);
   while string <> '%%%%%%%%%%' do
   begin
    if string =  '%pre&post?'  then readln(prepost[1])
    else if string =  '%unitlngth'  then  readln(unitlngth)
    else if string =  '%font-name'  then  strreadline(fontname)
    else if string =  '%picdimens'  then  readln(picwd,picht,XP1orig,YP1orig)
    else if string =  '%??caption'  then begin
				   	   strreadword(captiontype, string9);
					   readln
			        	 end
    else if string =  '%fignumber'  then begin
					   strreadword(fignumber, string9);
					   readln
					  end
    else if string =  '%Xaxisstrg'  then  strreadline(Xaxisstrg)
    else if string =  '%Yaxisstrg'  then  strreadline(Yaxisstrg)
    else if string =  '%capstring'  then  strreadline(capstring)
    else if string =  '%labelname'  then  strreadline(labelname)
    else if string =  '%legendloc'  then  legendread
    else if string =  '%Xdeltab:n'  then  readln(Xdeltabar,Xdeltanum)
    else if string =  '%Xoriginum'  then  readln(Xorignum)
    else if string =  '%Ydeltab:n'  then  readln(Ydeltabar,Ydeltanum)
    else if string =  '%Yoriginum'  then  readln(Yorignum)
    else if string =  '%Xratiog:r'  then  readln(Xscalegraph,Xscalereal)
    else if string =  '%Yratiog:r'  then  readln(Yscalegraph,Yscalereal)
    else if string9 =  '%plotchar'  then
		strreadline(plotstuff[string[10]].chardef)
    else if string9 =  '%plotname'  then
		strreadline(plotstuff[string[10]].charname)
    else begin
	  readln;writeln;
          writeln('**********************************************');
          writeln('error: unknown string: "',string,'"');
	  writeln('**********************************************');
	  error1:=true
	 end;
    string := blank10;
    strreadword(string, string9)
   end; (* while *)
   readln;  (* this readln is to finish reading the %%%%%.. line*)

   (* THE FUN BEGINS HERE *)
   if not error1 then
   begin
   textwd := picwd*unitlngth + 15.0; (* in mm *)
   if (textwd < 170) then textwd := 170;
   if textwd >240
   then begin
	writeln('% *****************************************************');
        writeln('% max-possible-picwd is about 240mm which will have ');
	writeln('% to be in Landscape. You''ll have to reduce scales.');
	writeln('% *****************************************************')
	end
   else if textwd > 170 then
        begin
	  writeln('% *****************************************************');
	  writeln('% THIS TEXT IS A BIT TOO WIDE FOR VERTICAL PAPER MODE.');
	  writeln('% YOU WILL HAVE TO USE LANDSCAPE MODE TO PRINT.');
	  writeln('% *****************************************************')
	end;

   if prepost[1] = 'y' then
   begin
   writeln('\documentstyle{article}');
   writeln('\setlength{\textwidth}{',textwd:1:2,'mm}');
   writeln('\pagestyle{empty}     % => no page number ');
   writeln('\begin{document}');
   writeln;
   end; (* end prepost *)

   write('\newcommand{\xaxis}{'); strwrite(Xaxisstrg);
   writeln('} % the literal for X-axis');
   write('\newcommand{\yaxis}{'); strwrite(Yaxisstrg);
   writeln('} % the literal for Y-axis');
   c := 'A';
   while (plotstuff[c].chardef[1] <> 'n') and (c <= maxchar) do
   begin
	   write('\newcommand{\pchar',c,'}');
	   strwrite(plotstuff[c].chardef); writeln;
	   c := chr(ord(c) + 1)
   end;
   writeln;
   write('\begin'); strwrite(fontname); writeln; (* fontname contains braces*)
   writeln('\begin{figure}[p]     %you might want different options here');

   (* XGorig & YGorig refer to the origin of the graph. *)
   (* XP1orig & YP1orig refer to the bottom-left origin of the picture box.*)
   (* XP2orig & YP2orig refer to the bottom-right corner of the picture box.*)
   (* see if (0,0) is on the graph or not *)
   XGorig := 0; YGorig := 0;
   if XP1orig >= 0 then XGorig := XP1orig
      else if (picwd + XP1orig) < 0 then XGorig := XP1orig + picwd;
   if YP1orig >= 0 then YGorig := YP1orig
      else if (picht + YP1orig) < 0 then YGorig := YP1orig + picht;
   XP2orig := picwd + XP1orig;
   YP2orig := picht + YP1orig;
   writeln('\unitlength = ',unitlngth:1:2,'mm');
   writeln('\begin{center}');
   writeln('\begin{picture}(',(picwd+10):1,',',(picht+10):1,')(',
			(XP1orig-5):1,',',(YP1orig-5):1,')');
   (* need a box of +10 on both axes to account for -10 origins & to *)
   (* get proper centering *)
   writeln('\thicklines');
   (* plot the horizontal axis *)
   if (XP2orig - XGorig) > 0 then
   begin
     writeln('\put(',XGorig:1,',',YGorig:1,'){\vector(1,0){',
					abs(XP2orig-XGorig):1,'}}');
     writeln('\put(',(XP2orig+2):1,',',YGorig:1,'){\makebox(0,0)[l]{X}}')
   end;
   if (XGorig - XP1orig) > 0 then
   begin
     writeln('\put(',XGorig:1,',',YGorig:1,'){\vector(-1,0){',
		abs(XGorig-XP1orig):1,'}}');
     writeln('\put(',(XP1orig-2):1,',',YGorig:1,
		'){\makebox(0,0)[r]{X}}')
   end;
   (* plot the vertical axis *)
   if (YP2orig - YGorig) > 0 then
   begin
     writeln('\put(',XGorig:1,',',YGorig:1,'){\vector(0,1){',
					abs(YP2orig-YGorig):1,'}}');
     writeln('\put(',XGorig:1,',',(YP2orig+2):1,'){\makebox(0,0)[b]{Y}}')
   end;
   if (YGorig - YP1orig) > 0 then
   begin
     writeln('\put(',XGorig:1,',',YGorig:1,'){\vector(0,-1){',
		abs(YGorig-YP1orig):1,'}}');
     writeln('\put(',XGorig:1,',',(YP1orig-2):1,
		'){\makebox(0,0)[t]{Y}}')
   end;
   writeln('\thinlines');

   (*********************************************************************)
   (*the following put numbers & bars along X-axis and Y-axis		*)
   (*********************************************************************)
   putbars(true,XGorig,(YGorig-1),Xdeltabar,abs(XP2orig-XGorig));
   putbars(true,XGorig,(YGorig-1),-Xdeltabar,abs(XGorig-XP1orig));
   putbars(false,(XGorig-1),YGorig,Ydeltabar,abs(YP2orig-YGorig));
   putbars(false,(XGorig-1),YGorig,-Ydeltabar,abs(YGorig-YP1orig));

(* Before invoking putnumbers, we must explicitly check if it needs to
   be invoked at all. This is because in the procedure putnumbers I use
   absolute values as the stopping condition for while loop.
   We use absolute values in the procedure so as to handle plotting
   numbers on both the positive and negative halves of the axes.
   deln below represents the delta graph units corresponding to X|Ydeltanum.
*)
   writeln('% Add a line similar to next one if number at origin desired.');
   deln  := (Xscalereal*Xdeltanum) / Xscalegraph;
   pos := XGorig + Xdeltanum; num:=Xorignum + deln;
   if pos < XP2orig then
   putnumbers(true,(YGorig-2),pos,num,deln,Xdeltanum,XP2orig);
   pos := XGorig - Xdeltanum; num:=Xorignum - deln;
   if pos > XP1orig then
   putnumbers(true,(YGorig-2),pos,num,-deln,-Xdeltanum,XP1orig);

   deln  := ( Yscalereal*Ydeltanum) / Yscalegraph;
   pos := YGorig + Ydeltanum; num:=Yorignum + deln;
   if pos < YP2orig then
   putnumbers(false,(XGorig-2),pos,num,deln,Ydeltanum,YP2orig);
   pos := YGorig - Ydeltanum; num:=Yorignum - deln;
   if pos > YP1orig then
   putnumbers(false,(XGorig-2),pos,num,-deln,-Ydeltanum,YP1orig);

   (***********************)
   (* put the legend box  *)
   (***********************)
   if (legendloc <> 'no') then
   begin   (* and if it is then obviously do nothing *)
   if (Xlegloc = -999999) and (Ylegloc = -999999) then
     begin   (* implies explicit coordinates not specified  *)
             (* if they are then they are set in legendread *)
	Xlegloc := picwd div 2; (* default is center *)
	Ylegloc := picht div 2; (* default is center *)
	if (legendloc[1] = 't') or (legendloc[2] = 't')
	  then if YP2orig > 0 then Ylegloc := picht else Ylegloc := picht-10;
	if (legendloc[1] = 'b') or (legendloc[2] = 'b')
	  then if YP1orig >= 0 then Ylegloc := 10 else Ylegloc := 0;
	if (legendloc[1] = 'l') or (legendloc[2] = 'l')
	  then if XP1orig >= 0 then Xlegloc := 10 else Xlegloc := 0;
	if (legendloc[1] = 'r') or (legendloc[2] = 'r')
	  then if XP2orig > 0 then Xlegloc := picwd else Xlegloc := picwd-10;
	Xlegloc := Xlegloc + XP1orig;
	Ylegloc := Ylegloc + YP1orig
     end;

   write('\put(',Xlegloc:1,',',Ylegloc:1,'){\makebox(0,0)');
   i:= 1; write('[');
   while (i <= 2) do
       begin
         if  (legendloc[i] <> ' ') and (legendloc[i] <> '/') then
 	    write(legendloc[i]);
         i:=i+1
      end;
   write(']');
   writeln('{\fbox{\shortstack[l]{');
   c:='A';
   while (plotstuff[c].chardef[1] <> 'n') and (c <= maxchar) do
   begin
      write(' {\makebox(4,2)[lb]{\put(2,1){\pchar',c,'}}}: ');
      strwrite(plotstuff[c].charname); writeln('\\');
      c := chr(ord(c) + 1)
   end;
   writeln(' {\makebox(4,4)[b]{X}}: \xaxis \\');
   writeln(' {\makebox(4,2)[b]{Y}}: \yaxis');
   writeln(' }}}}')
   end; (* not 'no' of legendloc*)

   (* put the caption if explicit *)
   captiontwo[1]:=capstring[1];
   captiontwo[2]:=capstring[2];
   if captiontype[1] = 'e' then    (* "e"xplicit, anything else other than *)
				   (* "L" for LaTeX => no   *)
   if captiontwo = 'YX' then
   begin
     writeln('% if the caption line is longer than the graphwidth, comment');
     writeln('% out the second line and use first one. you might have to');
     writeln('% fiddle with the width of parbox in the second stmt.');
     write('%\put(',XP1orig:1,',',(YP1orig-18):1,
			'){\makebox(',picwd:1,',0)[tl]{');
     writeln('Figure ',fignumber,'$\!$: \parbox[t]{',(textwd-32):1:1,'mm}{%');
     write('\put(',XP1orig:1,',',(YP1orig-18):1,
			'){\makebox(',picwd:1,',0)[t]{');
     writeln('Figure ',fignumber,'$\!$: {%');
     writeln('%\yaxis\ vs.\ \xaxis}}}')
   end
   else  (* means explicit string is specified *)
   begin
     write('%\put(',XP1orig:1,',',(YP1orig-18):1,
			'){\makebox(',picwd:1,',0)[tl]{');
     writeln('Figure ',fignumber,'$\!$: \parbox[t]{',(textwd-32):1:1,'mm}{%');
     write('\put(',XP1orig:1,',',(YP1orig-18):1,
			'){\makebox(',picwd:1,',0)[t]{');
     writeln('Figure ',fignumber,'$\!$: {%');
     strwrite(capstring); writeln;
     writeln('}}}');
   end;

   writeln('% beginning of data');
   while not eof do
   begin
     readln(plotchar,xreality,yreality);
     xgraph := (xreality*Xscalegraph)/Xscalereal;
     ygraph := (yreality*Yscalegraph)/Yscalereal;
     writeln('\put(',xgraph:1:5,',',ygraph:1:5,'){\pchar',plotchar,'}')
   end;
   writeln('% end of data');
   writeln('\end{picture}');
   writeln('\end{center}');

   (* put the LaTeX \caption if so specified *)
   if captiontype[1] = 'L' then     (* "L"aTeX . if it is not = 'L' or 'e'*)
				    (* then interpreted as 'no'  *)
   begin
     writeln('% if the caption line is longer than the graphwidth,use a');
     writeln('% \parbox[t]{...mm}{.......} like statement for the argument');
     writeln('% with suitable args for parbox to get things centered.');
     if captiontwo = 'YX'
	then begin
	     writeln('% You might want to add a [] to \caption below.');
	     writeln('\caption{\protect\normalsize \yaxis\ vs.\ \xaxis }')
	     end
	   else begin
		writeln('\caption{\protect\normalsize ');
		strwrite(capstring); writeln;
		writeln('}')
		end;
     if labelname[1] = '{' then    (* anything else => 'no' *)
	begin
           write('\label'); strwrite(labelname); writeln
				   (* labelname contains the braces *)
        end
   end;

   writeln('\end{figure}');
   writeln;  (* a blank line is supposedly needed before \end fontname *)
   		      (* so says the latex manual *)
   write('\end'); strwrite(fontname); writeln; (* fontname contains braces *)
   writeln;

   if prepost[1] = 'y' then
   	writeln('\end{document}');
   end (* not error1 *)
end.