module xyplot;

{

XYPLOT - Generate 2-D Plots of X,Y Data Pairs
	Derived from the FORTRAN IV Subroutine XYPLT in the Book
	"Digital Computations in Basic Circuit Theory" by L.P. Huelsman
	PASCAL/MT+ Coding and Algorithm Enhancements by Richard Conn

Calling Form --
	rcode = xyplot (device, ndata, nsx, nsy, nnp, x, y);

Passed Parameters --
	device	A String (STR) Specifying the Output Device/File; One of:
			CON:	= Console
			LST:	= Printer
			<File>	= Disk File, Like PLOT1.TXT or A:Plot
	ndata	Number of valid data points in the x,y rarrays
	nsx	Maximum Value of X Points
	nsy	Maximum Value of Y Points (Minimum Value = NSY - 100)
	nnp	Range of X Points (NSX - Minimum Value of X Points)
	x, y	rarrays of the X,Y input Point values

Result Codes (Type Integer) Returned --
	0	No Error
	1	Error in Opening Output File
	2	Error in Closing Output File

Special Types --
	See the following TYPE Definition for the types STR for the
	device name and RARRAY for the passed data.

}

const
	max_elt = 200; { Maximum Number of Array Elements Permitted }
	strl = 20; { Maximum Number of Characters in a String Vector STR }
type
	rarray = array [1..max_elt] of real;
	str = string[strl];

function xyplot (device : str; ndata, nsx, nsy, nnp : integer; x, y : rarray) :
	integer;

const
	jn = '-';
	jp = '+';
	ji = 'I';
	jb = ' ';
	jz = '$';
	jx = 'X';
var
	ofile : text;
	line : array [1..101] of char;
	i, j, l, np, dash, index : integer;
	nx, nx_next : integer;
	xns, yns, xnp : real;
	rcode : integer;

procedure clear (jint, jopen : char);
var
	i, j, idx : integer;
begin
	{ Initialize Line Image to Dashes }
	idx := 0;
	for i:=1 to 10 do begin
		idx := idx + 1;
		line[idx] := jint;  { Intersect Char }
		for j:=1 to 9 do begin
			idx := idx + 1;
			line[idx] := jopen;  { Level Char }
		end;
	end;
	line[101] := jint;  { Last Intersect Char }
end;

procedure capitalize (var s : str);
var	i : integer;
begin
	for i:=1 to strl do
		if (s[i] > 'a') and (s[i] <= 'z') then
			s[i] := chr(ord(s[i]) - ord('a') + ord('A'));
end;

procedure clrblank;
begin
	{ Initialize Line Image to Blanks }
	clear (ji, jb);
end;

procedure clrdash;
begin
	{ Initialize Line Image to Dashes }
	clear (jp, jn);
end;

procedure xchg (var a,b : real);
var
	temp : real;
begin
	{ Exchange real numbers A and B }
	temp := a;
	a := b;
	b := temp;
end;

procedure sety (idx : integer);
var
	ny : integer;
begin
	ny := trunc (y[idx] + 101.49999 - yns);
	if ny < 1 then line[1] := jz { Off Scale }
        	  else if ny > 101 then line[101] := jz
                	           else line[ny] := jx;
end;

procedure setx (idx : integer);
begin
	{ Scaled Value of Next X Element }
	nx_next := trunc (x[idx] * 0.6 - xns + xnp + 0.49999);
	if nx_next > np then nx_next := np; { Out of Range }
	if nx_next < 0 then nx_next := 0;   { Out of Range }
end;

procedure printline;
var
	i, nprint : integer;
begin
	if (dash mod 6) = 0 then begin
		nprint := ((dash * 10) div 6) + nsx - nnp;
		write(ofile, nprint:4); end
	else write(ofile, '    ');
	for i:=1 to 101 do write(ofile, line[i]); writeln(ofile);
	dash := dash + 1; { Increment Line Counter }
end;

begin { XYPLOT }

	{ Set Result Code to OK }
	rcode := 0;  { No Error }

	{ Assign Output Device }
	capitalize (device);  { Capitalize Output Device Name }
	assign (ofile, device);  { Assign Device to File Spec }
	rewrite (ofile);  { Rewind Device if Disk File }

	{ Check for Successful Open of Output File and Perform XYPLOT if so }
	if ioresult = 255 then rcode := 1  { Error in Opening File }
	else begin { XYPLOT Function }

	{ Arrange data in ascending order of X }
	for i:=1 to ndata-1 do
		for j:=i+1 to ndata do
			if x[i] > x[j] then begin { Exchange }
				xchg (x[i], x[j]);
				xchg (y[i], y[j]);
			end;

	{ Print Ordinate Scale Figures }
	write(ofile, ' ');  { Leading Space }
	for i:=1 to 11 do begin
		l := 10 * i - 110 + nsy;  { Compute Values }
		write(ofile, l:4, '      ');  { Write Values }
	end;
	writeln(ofile);  { New Line after Ordinate Scale Values }

	{ Initialize Key Values }
	dash := 0; { Initialize dash line indicator }
	np := (nnp div 10) * 6; xnp := np;
	xns := (nsx div 10) * 6; yns := nsy;
	index := 1;
	setx(index);  { Scaled Value of nx_next }

	repeat { Main Loop }
		{ Set up current line }
		if (dash mod 6) = 0 then clrdash else clrblank;

		{ Load Values into current line if X Coordinates Match }
		if dash >= nx_next then
		   repeat { Plot all Y Values which belong to current X }
			nx := nx_next;  { Scaled Value of Current X }

			{ Scaled Value of Current Y }
			sety(index);

			index := index + 1;  { Advance to next data elt }
			setx(index); { Compute Next X }
		   until (nx_next <> nx) or (index = ndata);

		if (index = ndata) and (nx_next = nx) then sety(index);

		printline;  { Print Graph }
	until index = ndata;

	if nx_next <> nx then begin
		sety(index);
		printline;
	end;

	{ Close Output File }
	close(ofile,i);
	if i=255 then rcode := 2;  { Error in Closing File }

	end; { XYPLOT Function }

	xyplot := rcode;  { Setup Return Code }

end; { XYPLOT }
modend.
