{$-V}
{****************************************************************************
 *                                                                          *
 *              PLOT Version 3.3 Compatible Pascal Library                  *
 *                                                                          *
 *                  Copyright 1984 by Thomas E. Speer                       *
 *                       All rights reserved.                               *
 *         Released to the Public Domain for Non-commercial use only        *
 *                                                                          *
 *             This file contains procedures and functions for              *
 *             access to the lowest level graphics functions.               *
 *                                                                          *
 ****************************************************************************}


{-------------------------------------------------------------------------}
{                 global graphics variables and constants                 }
CONST
    BUFFSIZE = 255;
    FIXVAL   = 32767;

TYPE
    filename = STRING[11];
    bufftype = STRING[ BUFFSIZE ];
    textline = STRING[ 80 ];
    vecfile  = TEXT;

VAR
    buffer: bufftype;
    colour, nxchar, nychar, nxline, nbuff : INTEGER;
    xmin,  xmax,  ymin,  ymax, sxleft, sxrt, sybot, sytop: REAL;
    scale: ARRAY [1..4] of REAL;
    chxsz, chysz, chrot, xpos, ypos: REAL;
    vecunit :vecfile;
{                 global graphics variables and constants                 }

{-------------------------------------------------------------------------}
FUNCTION rx( sxi:REAL ):REAL;
{       This function does a linear conversion between the real world
        and screen X coordinates.
        inputs:
                        sxi     screen Y coordinate
        outputs:
                        rx      real world coordinate
}
BEGIN
    rx := scale[1] * (sxi-sxrt) + xmin;
END;

{-------------------------------------------------------------------------}
FUNCTION ry( syi:REAL ):REAL;
{       This function does a linear conversion between the real world
        and screen Y coordinates.
        inputs:
                        syi     screen Y coordinate
        outputs:
                        ry      real world coordinate
}
BEGIN
    ry := scale[2] * (syi-sybot) + ymin;
END;

{-------------------------------------------------------------------------}
FUNCTION sx( rxi:REAL ):REAL;
{       This function does a linear conversion from the real to the
        screen x coordinates.
        inputs:
                rx      real world coordinate
        outputs:
                sx      screen x coordinate
}
BEGIN
    sx := (rxi - xmin)/scale[1] + sxleft;
END;

{-------------------------------------------------------------------------}
FUNCTION sy( ryi:REAL ):REAL;
{       This function does a linear conversion from the real to the
        screen y coordinates.
        inputs:
                ry      real world coordinate
        outputs:
                sy      screen y coordinate
}
BEGIN
    sy := (ryi - ymin)/scale[2] + sybot;
END;

{-------------------------------------------------------------------------}
PROCEDURE swindo ( sxlti,sxrti,syboti,sytopi:REAL );
{       This procedure sets the screen window
        inputs:         sxlti   value at left   edge of window (screen units)
                        sxrti   value at right  edge of window (screen units)
                        syboti  value at bottom edge of window (screen units)
                        sytopi  value at top    edge of window (screen units)
        outputs:
                        none returned
}
VAR
    t:REAL;

BEGIN
    sxleft := sxlti;
    sxrt   := sxrti;
    sybot  := syboti;
    sytop  := sytopi;

    t := sxrt - sxleft;
    IF ( sytop - sybot < t) THEN t := sytop - sybot;
    IF ( t < 0.0001 ) THEN
        Write(CON, 'Screen window too small.  Size =', t )
    ELSE IF ( (xmax - xmin = 0) OR (ymax - ymin = 0) ) THEN
        Write(CON,'Real window has 0 size.  Scale factors not calculated')
    ELSE BEGIN
        scale[1] := (xmax - xmin)/(sxrt - sxleft);
        scale[2] := (ymax - ymin)/(sytop - sybot);
    END
END;
    
{-------------------------------------------------------------------------}
PROCEDURE rwindo( xmini, xmaxi, ymini, ymaxi:REAL );
{       This procedure sets the real world window for scaling purposes
        inputs:
                        xmini   value at left   edge of window (user units)
                        xmaxi   value at right  edge of window (user units)
                        ymini   value at bottom edge of window (user units)
                        ymaxi   value at top    edge of window (user units)
        outputs:
                        none returned
}
BEGIN
    xmin := xmini;
    xmax := xmaxi;
    ymin := ymini;
    ymax := ymaxi;
    
    swindo( sxleft, sxrt, sybot, sytop );
END;

{-------------------------------------------------------------------------}
PROCEDURE concat2(VAR strng1:bufftype; nchar1:INTEGER; VAR strng2:bufftype;
                nchar2:INTEGER; VAR strng3:bufftype);
{     This procedure concatenates portions of strng1 and strng2 into strng3
        inputs:
                strng1  string for first postion
                nchar1  number of characters in strng1
                strng2  string for second position
                nchar2  number of characters in strng1
        outputs:
                strng3  concatenated string
}
BEGIN
    strng3 := Concat( Copy(strng1,1,nchar1), Copy(strng2,1,nchar2) );
END;

{------------------------------------------------------------------------}
PROCEDURE buffout(VAR outunit:vecfile; outbuf:bufftype; size:INTEGER);
{       This procedure writes the buffer to the indicated file.
        inputs:
                outunit file pointer for output
                outbuf  string (may contain '/0' characters!)
                size    number of characters to send out
        outputs:
                none returned
}
VAR
    i: INTEGER;
    c: CHAR;

BEGIN
    FOR i := 1 TO size DO BEGIN
        c := Copy( outbuf,i,1 );
        Write( outunit, c);
    END
END;

{-------------------------------------------------------------------------}
PROCEDURE writecmd ( cmd:bufftype; cmdlen:INTEGER );
{       This procedure adds a command to the buffer and writes it if full.
        inputs:
                cmd     input command string
                cmdlen  length of command string
        outputs:
                none returned
}
BEGIN
    IF (cmdlen + nbuff < BUFFSIZE) THEN BEGIN
        concat2 (buffer, nbuff, cmd, cmdlen, buffer);
        nbuff := nbuff + cmdlen;
        END
    ELSE BEGIN
        buffout( vecunit, buffer, nbuff);
        nbuff := 0;
        buffer:= '';
        concat2 (buffer, nbuff, cmd, cmdlen, buffer);
        nbuff := cmdlen;
    END
END;


{------------------------------------------------------------------------}
PROCEDURE erase;
{       This procedure causes the picture to be set to the currently selected
        color.
        inputs:
                none
        outputs:
                none returned
}
BEGIN
    writecmd ( 'E', 1);
END;

{------------------------------------------------------------------------}
PROCEDURE fill( x1, y1, x2, y2, yf:REAL);
{       This procedure fills in a solid area between a line segment and
        a horizontal line.
        inputs:
                x1,y1   coordinates for start of line segment
                x2,y2   coordinates for end   of line segment
                yf      height of horizonal level
        outputs:
                none returned
}
VAR
    fxy: INTEGER;
    cmd: STRING[11];

BEGIN
    cmd := 'F';
    fxy := Trunc( x1 * FIXVAL);                 { convert to fixed point }
    cmd := cmd + Chr(fxy MOD 256);              { load low  byte of integer }
    cmd := cmd + Chr((fxy DIV 256) MOD 256);    { load high byte of integer }
    fxy := Trunc( y1 * FIXVAL);                 { convert to fixed point }
    cmd := cmd + Chr(fxy MOD 256);              { load low  byte of integer }
    cmd := cmd + Chr((fxy DIV 256) MOD 256);    { load high byte of integer }
    fxy := Trunc( x2 * FIXVAL);                 { convert to fixed point }
    cmd := cmd + Chr(fxy MOD 256);              { load low  byte of integer }
    cmd := cmd + Chr((fxy DIV 256) MOD 256);    { load high byte of integer }
    fxy := Trunc( y2 * FIXVAL);                 { convert to fixed point }
    cmd := cmd + Chr(fxy MOD 256);              { load low  byte of integer }
    cmd := cmd + Chr((fxy DIV 256) MOD 256);    { load high byte of integer }
    fxy := Trunc( yf * FIXVAL);                 { convert to fixed point }
    cmd := cmd + Chr(fxy MOD 256);              { load low  byte of integer }
    cmd := cmd + Chr((fxy DIV 256) MOD 256);    { load high byte of integer }

    writecmd( cmd, 11 );
    xpos := x2;
    ypos := y2;
END;

{------------------------------------------------------------------------}
PROCEDURE segmnt(x1,y1,x2,y2:REAL);
{       This function plots a line segment from (x1,y1) to (x2,y2).
        inputs:
                x1,y1   coordinates for start of segment
                x2,y2   coordiantes for end   of segment
        outputs:
                none returned.
}
VAR
    fxy:INTEGER;
    cmd:STRING[9];

BEGIN
    cmd := 'D';
    fxy := Trunc( x1 * FIXVAL);                 { convert to fixed point }
    cmd := cmd + Chr(fxy MOD 256);              { load low  byte of integer }
    cmd := cmd + Chr((fxy DIV 256) MOD 256);      { load high byte of integer }
    fxy := Trunc( y1 * FIXVAL);                 { convert to fixed point }
    cmd := cmd + Chr(fxy MOD 256);              { load low  byte of integer }
    cmd := cmd + Chr((fxy DIV 256) MOD 256);      { load high byte of integer }
    fxy := Trunc( x2 * FIXVAL);                 { convert to fixed point }
    cmd := cmd + Chr(fxy MOD 256);              { load low  byte of integer }
    cmd := cmd + Chr((fxy DIV 256) MOD 256);      { load high byte of integer }
    fxy := Trunc( y2 * FIXVAL);                 { convert to fixed point }
    cmd := cmd + Chr(fxy MOD 256);              { load low  byte of integer }
    cmd := cmd + Chr((fxy DIV 256) MOD 256);      { load high byte of integer }

    writecmd(cmd, 9);

    xpos := x2;
    ypos := y2;
END;

{-------------------------------------------------------------------------}
PROCEDURE gmove( x,y:REAL );
{       This function moves the present coordinates to a new location
        without plotting.
        inputs:
                x,y     coordinates for new location
        outputs:
                none returned
}
VAR
    fxy: INTEGER;
    cmd: STRING[5];

BEGIN    
    cmd := 'M';
    fxy := Trunc (x * FIXVAL);                  { convert to fixed point }
    cmd := cmd + Chr(fxy MOD 256);              { load low  byte of integer }
    cmd := cmd + Chr((fxy DIV 256) MOD 256);      { load high byte of integer }
    fxy := Trunc( y * FIXVAL);                  { convert to fixed point }
    cmd := cmd + Chr(fxy MOD 256);              { load low  byte of integer }
    cmd := cmd + Chr((fxy DIV 256) MOD 256);      { load high byte of integer }
    
    writecmd( cmd, 5 );
    xpos := x;
    ypos := y;
END;

{------------------------------------------------------------------------}
PROCEDURE vector( x,y:REAL );
{       This procedure plots a line segment from the present position
        to the given coordinates
        inputs:
                x,y     coordinates for end of segment
        outputs:
                none returned
}
VAR
    fxy:INTEGER;
    cmd:STRING[5];
    
BEGIN
    cmd := 'I';
    fxy := Trunc( x * FIXVAL);                { convert to fixed point }
    cmd := cmd + Chr(fxy MOD 256);            { load low  byte of integer }
    cmd := cmd + Chr((fxy DIV 256) MOD 256);  { load high byte of integer }
    fxy := Trunc( y * FIXVAL);                { convert to fixed point }
    cmd := cmd + Chr(fxy MOD 256);            { load low  byte of integer }
    cmd := cmd + Chr((fxy DIV 256) MOD 256);  { load high byte of integer }

    writecmd( cmd, 5 );
    xpos := x;
    ypos := y
END;

{------------------------------------------------------------------------}
PROCEDURE color(code: INTEGER);
{       This procedure sets the color to be used in plotting
        inputs:
                code    new color code
        outputs:
                none returned
}
VAR
    cmd: STRING[2];

BEGIN
    colour := code;
    cmd := 'C' + Chr(code MOD 256);
    
    writecmd ( cmd, 2 );
END;

{------------------------------------------------------------------------}
PROCEDURE gprint;
{       This procedure causes the picture to be printed
        inputs:
                none
        outputs:
                none returned
}
BEGIN
    writecmd ( 'O', 1 );
END;

{-------------------------------------------------------------------------}
PROCEDURE grfini;
{       This procedure terminates the plot and closes the file.
        inputs:
                none
        outputs:
                none returned
}
BEGIN
    IF (nbuff > BUFFSIZE-2) THEN BEGIN
        buffout( vecunit, buffer, nbuff);
        nbuff := 0;
        buffer := ''
    END;
    nbuff := nbuff + 1;
    buffer := buffer + 'O';
    nbuff := nbuff + 1;
    buffer := buffer + 'Q';
    buffout( vecunit, buffer, nbuff);
    Close( vecunit);
END;

{------------------------------------------------------------------------}
PROCEDURE grinit (name: filename);
{       This function initializes the plot package.
        inputs:
                name    name of disk file for output of vector commands
        outputs:
                a '0' is returned if unsuccessful in opening file name.
}
VAR
    cmd: STRING[5];

BEGIN
    Assign( vecunit, name );
    Rewrite( vecunit );
    nbuff := 0;
    buffer:= '';
    
    { output command stream to initialize memory map }
    cmd := 'C' + Chr(0)+ 'EC' + Chr(127);
    
    writecmd (cmd, 5);

    xmin   := 0.0;
    xmax   := 1.0;
    ymin   := 0.0;
    ymax   := 1.0;
    sxleft := 0.0;
    sxrt   := 1.0;
    sybot  := 0.0;
    sytop  := 1.0;
    chxsz  := 0.0125;
    chysz  := 0.02;
    chrot  := 0.0;
    xpos   := 0.0;
    ypos   := 0.0;
    scale[1]:=1.0;
    scale[2]:=1.0;
    scale[3]:=1.0;
    scale[4]:=0.0;
    nxchar  := 0;
    nychar  := 0;
    nxline  := 1;
END;

{-------------------------------------------------------------------------}
PROCEDURE gstrng(x, y:REAL; strng:textline);
{       This procedure will plot a string of hardware generated characters
        inputs:
                        x,y     starting coordinates for string
                        strng   string to be plotted
        outputs:
                        none returned
}
VAR
    fx,fy,nchar:INTEGER;
    cmd: STRING[86];

BEGIN
    nchar := Length(strng);

    IF (nchar > 0) THEN BEGIN
        fx := Trunc (x * FIXVAL);
        fy := Trunc( (y * FIXVAL));
        cmd := 'S';
        cmd := cmd + Chr(fx MOD 256);           { load low  byte of integer }
        cmd := cmd + Chr((fx DIV 256) MOD 256); { load high byte of integer }
        cmd := cmd + Chr(fy MOD 256);           { load low  byte of integer }
        cmd := cmd + Chr((fy DIV 256) MOD 256); { load high byte of integer }

        cmd := cmd + strng + Chr(13);
        nchar := nchar + 6;
        writecmd ( cmd, nchar );
    END
END;

{-------------------------------------------------------------------------}
PROCEDURE point( x,y:REAL );
{       This function plots a point at the given coordinates
        inputs:
                x,y     coordinates of point
        outputs:
                none returned
}
VAR
    fxy:INTEGER;
    cmd:STRING[5];
    
BEGIN
    IF ((Abs(x) <= 1.0) AND (Abs(y) <= 1.0 )) THEN BEGIN
        cmd :='P';
        fxy := Trunc( x * FIXVAL);                  { convert to fixed point }
        cmd := cmd + Chr(fxy MOD 256);              { load low  byte of integer }
        cmd := cmd + Chr((fxy DIV 256) MOD 256);    { load high byte of integer }
        fxy := Trunc( y * FIXVAL);                  { convert to fixed point }
        cmd := cmd + Chr(fxy MOD 256);              { load low  byte of integer }
        cmd := cmd + Chr((fxy DIV 256) MOD 256);    { load high byte of integer }

        writecmd( cmd, 5 );
        xpos := x;
        ypos := y
    END
END;

