MODULE EPSPlot;

IMPORT Wr, Fmt, Text, Thread;
FROM Stdio IMPORT stderr;

VAR
  Hmin, Hmax, Vmin, Vmax: LONGREAL;
  Xmin, Xmax, Ymin, Ymax: LONGREAL;
  Scale: LONGREAL;

PROCEDURE BeginFile (
    psfile: Wr.T;
    xmin, xmax, ymin, ymax: LONGREAL;
    xn, yn: CARDINAL
  ) =
  <* FATAL Wr.Failure, Thread.Alerted *>
  BEGIN
    WITH
      fxn = FLOAT(xn, LONGREAL),
      fyn = FLOAT(yn, LONGREAL),
      
      scale = MIN(
        7.5d0 * 72.0d0 / (xmax - xmin),
        10.0d0 * 72.0d0 / (ymax - ymin)
      ),
      hsize = scale * (xmax - xmin),
      vsize = scale * (ymax - ymin)
    DO
      Scale := scale;

      Hmin := (4.25d0 * 72.0d0) - hsize/2.0d0;
      Hmax := (4.25d0 * 72.0d0) + hsize/2.0d0;
      Vmin := (5.50d0 * 72.0d0) - vsize/2.0d0;
      Vmax := (5.50d0 * 72.0d0) + vsize/2.0d0;
      
      Xmin := xmin;
      Xmax := xmax;
      Ymin := ymin;
      Ymax := ymax;

      Wr.PutText(psfile, "%!PS-Adobe-2.0\n");
      Wr.PutText(psfile, "%%BoundingBox: ");
      Wr.PutText(psfile, FL(Hmin, 2));
      Wr.PutChar(psfile, ' ');
      Wr.PutText(psfile, FL(Vmin, 2));
      Wr.PutChar(psfile, ' ');
      Wr.PutText(psfile, FL(Hmax, 2));
      Wr.PutChar(psfile, ' ');
      Wr.PutText(psfile, FL(Vmax, 2));
      Wr.PutChar(psfile, '\n');
      Wr.PutText(psfile, "%%Pages: (atend)\n");

      Wr.PutText(psfile, "/$maindict 6400 dict def\n");
      Wr.PutText(psfile, "$maindict begin\n");

      Wr.PutText(psfile, "% Rectangle draw operator:\n");
      Wr.PutText(psfile, "%   /xlo/ /xhi/ /ylo/ /yhi/ recd --> \n");
      Wr.PutText(psfile, "/recd\n");
      Wr.PutText(psfile, "{\n");
      Wr.PutText(psfile, "  gsave\n");
      Wr.PutText(psfile, "  newpath\n");
      Wr.PutText(psfile, "    3 index 2 index moveto\n");
      Wr.PutText(psfile, "    2 index 2 index lineto\n");
      Wr.PutText(psfile, "    2 index 1 index lineto\n");
      Wr.PutText(psfile, "    3 index 1 index lineto\n");
      Wr.PutText(psfile, "    pop pop pop pop\n");
      Wr.PutText(psfile, "    closepath\n");
      Wr.PutText(psfile, "    stroke\n");
      Wr.PutText(psfile, "  grestore\n");
      Wr.PutText(psfile, "} def\n");
      Wr.PutText(psfile, "\n");

      Wr.PutText(psfile, "% Rectangle fill operator:\n");
      Wr.PutText(psfile, "%   /xlo/ /xhi/ /ylo/ /yhi/ /gray/ recf --> \n");
      Wr.PutText(psfile, "/recf\n");
      Wr.PutText(psfile, "{\n");
      Wr.PutText(psfile, "  gsave\n");
      Wr.PutText(psfile, "    setgray\n");
      Wr.PutText(psfile, "    newpath\n");
      Wr.PutText(psfile, "    3 index 2 index moveto\n");
      Wr.PutText(psfile, "    2 index 2 index lineto\n");
      Wr.PutText(psfile, "    2 index 1 index lineto\n");
      Wr.PutText(psfile, "    3 index 1 index lineto\n");
      Wr.PutText(psfile, "    pop pop pop pop\n");
      Wr.PutText(psfile, "    closepath\n");
      Wr.PutText(psfile, "    fill\n");
      Wr.PutText(psfile, "  grestore\n");
      Wr.PutText(psfile, "} def\n");
      Wr.PutText(psfile, "\n");

      Wr.PutText(psfile, "% Rectangle fill and stroke operator:\n");
      Wr.PutText(psfile, "%   /xlo/ /xhi/ /ylo/ /yhi/ /gray/ recfd --> \n");
      Wr.PutText(psfile, "/recfd\n");
      Wr.PutText(psfile, "{\n");
      Wr.PutText(psfile, "  gsave\n");
      Wr.PutText(psfile, "    setgray\n");
      Wr.PutText(psfile, "    newpath\n");
      Wr.PutText(psfile, "    3 index 2 index moveto\n");
      Wr.PutText(psfile, "    2 index 2 index lineto\n");
      Wr.PutText(psfile, "    2 index 1 index lineto\n");
      Wr.PutText(psfile, "    3 index 1 index lineto\n");
      Wr.PutText(psfile, "    pop pop pop pop\n");
      Wr.PutText(psfile, "    closepath\n");
      Wr.PutText(psfile, "    gsave fill grestore\n");
      Wr.PutText(psfile, "    0 setgray\n");
      Wr.PutText(psfile, "    stroke\n");
      Wr.PutText(psfile, "  grestore\n");
      Wr.PutText(psfile, "} def\n");
      Wr.PutText(psfile, "\n");

      Wr.PutText(psfile, "% Cell fill operator:\n");
      Wr.PutText(psfile, "%   /xi/ /yi/ celf --> \n");
      Wr.PutText(psfile, "/celf\n");
      Wr.PutText(psfile, "{\n");
      Wr.PutText(psfile, "  3 1 roll \n");
      Wr.PutText(psfile, "  exch dup \n");
      Wr.PutText(psfile, "  xstep mul xmin add exch 1 add xstep mul xmin add\n");
      Wr.PutText(psfile, "  3 2 roll dup\n");
      Wr.PutText(psfile, "  ystep mul ymin add exch 1 add ystep mul ymin add\n");
      Wr.PutText(psfile, "  5 4 roll \n");
      Wr.PutText(psfile, "  recf\n");
      Wr.PutText(psfile, "} def\n");
      Wr.PutText(psfile, "\n");

      Wr.PutText(psfile, "% Circle fill operator:\n");
      Wr.PutText(psfile, "%   /x/ /y/ /radius/ /gray/ cirf --> \n");
      Wr.PutText(psfile, "/cirf\n");
      Wr.PutText(psfile, "{\n");
      Wr.PutText(psfile, "  gsave\n");
      Wr.PutText(psfile, "    setgray\n");
      Wr.PutText(psfile, "    newpath\n");
      Wr.PutText(psfile, "      0 360 arc\n");
      Wr.PutText(psfile, "      closepath\n");
      Wr.PutText(psfile, "    fill\n");
      Wr.PutText(psfile, "  grestore\n");
      Wr.PutText(psfile, "} def\n");
      Wr.PutText(psfile, "\n");

      Wr.PutText(psfile, "% Circle draw operator:\n");
      Wr.PutText(psfile, "%   /x/ /y/ /radius/ cird --> \n");
      Wr.PutText(psfile, "/cird\n");
      Wr.PutText(psfile, "{\n");
      Wr.PutText(psfile, "  gsave\n");
      Wr.PutText(psfile, "    newpath\n");
      Wr.PutText(psfile, "      0 360 arc\n");
      Wr.PutText(psfile, "      closepath\n");
      Wr.PutText(psfile, "    stroke\n");
      Wr.PutText(psfile, "  grestore\n");
      Wr.PutText(psfile, "} def\n");
      Wr.PutText(psfile, "\n");

      Wr.PutText(psfile, "% Circle fill and draw operator:\n");
      Wr.PutText(psfile, "%   /x/ /y/ /radius/ /gray/ cirfd --> \n");
      Wr.PutText(psfile, "/cirfd\n");
      Wr.PutText(psfile, "{\n");
      Wr.PutText(psfile, "  gsave\n");
      Wr.PutText(psfile, "    4 1 roll\n");
      Wr.PutText(psfile, "    newpath\n");
      Wr.PutText(psfile, "      0 360 arc\n");
      Wr.PutText(psfile, "      closepath\n");
      Wr.PutText(psfile, "      gsave setgray fill grestore\n");
      Wr.PutText(psfile, "    stroke\n");
      Wr.PutText(psfile, "  grestore\n");
      Wr.PutText(psfile, "} def\n");
      Wr.PutText(psfile, "\n");

      Wr.PutText(psfile, "% Segment draw operator:\n");
      Wr.PutText(psfile, "%   /xa/ /ya/ /xb/ /yb/ segd --> \n");
      Wr.PutText(psfile, "/segd\n");
      Wr.PutText(psfile, "{\n");
      Wr.PutText(psfile, "  gsave\n");
      Wr.PutText(psfile, "    newpath\n");
      Wr.PutText(psfile, "    moveto\n");
      Wr.PutText(psfile, "    lineto\n");
      Wr.PutText(psfile, "    stroke\n");
      Wr.PutText(psfile, "  grestore\n");
      Wr.PutText(psfile, "} def\n");
      Wr.PutText(psfile, "\n");

      Wr.PutText(psfile, "% Triangle fill operator:\n");
      Wr.PutText(psfile, "%   /xa/ /ya/ /xb/ /yb/ /xc/ /yc/ /gray/ trif --> \n");
      Wr.PutText(psfile, "/trif\n");
      Wr.PutText(psfile, "{\n");
      Wr.PutText(psfile, "  gsave\n");
      Wr.PutText(psfile, "    setgray\n");
      Wr.PutText(psfile, "    newpath\n");
      Wr.PutText(psfile, "    moveto\n");
      Wr.PutText(psfile, "    lineto\n");
      Wr.PutText(psfile, "    lineto\n");
      Wr.PutText(psfile, "    closepath\n");
      Wr.PutText(psfile, "    fill\n");
      Wr.PutText(psfile, "  grestore\n");
      Wr.PutText(psfile, "} def\n");
      Wr.PutText(psfile, "\n");

      Wr.PutText(psfile, "% Draw an X-value grid line:\n");
      Wr.PutText(psfile, "%   /x/ xgrd --> \n");
      Wr.PutText(psfile, "/xgrd\n");
      Wr.PutText(psfile, "{\n");
      Wr.PutText(psfile, "  gsave\n");
      Wr.PutText(psfile, "  newpath\n");
      Wr.PutText(psfile, "    dup ymin moveto\n");
      Wr.PutText(psfile, "    ymax lineto\n");
      Wr.PutText(psfile, "    stroke\n");
      Wr.PutText(psfile, "  grestore\n");
      Wr.PutText(psfile, "} def\n");
      Wr.PutText(psfile, "\n");

      Wr.PutText(psfile, "% Draw an Y-value grid line:\n");
      Wr.PutText(psfile, "%   /y/ ygrd --> \n");
      Wr.PutText(psfile, "/ygrd\n");
      Wr.PutText(psfile, "{\n");
      Wr.PutText(psfile, "  gsave\n");
      Wr.PutText(psfile, "  newpath\n");
      Wr.PutText(psfile, "    dup xmin exch moveto\n");
      Wr.PutText(psfile, "    xmax exch lineto\n");
      Wr.PutText(psfile, "    stroke\n");
      Wr.PutText(psfile, "  grestore\n");
      Wr.PutText(psfile, "} def\n");
      Wr.PutText(psfile, "\n");

      Wr.PutText(psfile, "% Operator to move to new caption line:\n");
      Wr.PutText(psfile, "%   nl --> \n");
      Wr.PutText(psfile, "/nl\n");
      Wr.PutText(psfile, "{\n");
      Wr.PutText(psfile, "  /ytext ytext dytext sub def\n");
      Wr.PutText(psfile, "  xtext ytext moveto\n");
      Wr.PutText(psfile, "} def\n");
      Wr.PutText(psfile, "\n");

      Wr.PutText(psfile, "% Operator to print string at CP without clipping:\n");
      Wr.PutText(psfile, "%   /s/ shw --> \n");
      Wr.PutText(psfile, "/shw\n");
      Wr.PutText(psfile, "{\n");
      Wr.PutText(psfile, "  gsave initclip show grestore\n");
      Wr.PutText(psfile, "} def\n");
      Wr.PutText(psfile, "\n");

      Wr.PutText(psfile, "end % $maindict\n");
      Wr.PutText(psfile, "%%EndProlog\n");

      Wr.PutText(psfile, "%%Page: 1\n");
      Wr.PutText(psfile, "$maindict begin\n");

      Wr.PutText(psfile, "% Round joints and caps:\n");
      Wr.PutText(psfile, "1 setlinecap 1 setlinejoin\n");
      Wr.PutText(psfile, "\n");

      Wr.PutText(psfile, "% Black thin lines:\n");
      Wr.PutText(psfile, "0 setlinewidth 0 setgray [ ] 0 setdash\n");
      Wr.PutText(psfile, "\n");

      Wr.PutText(psfile, "/xmin " & EL(Hmin) & " def  % min plottable x\n");
      Wr.PutText(psfile, "/xmax " & EL(Hmax) & " def  % max plottable x\n");
      Wr.PutText(psfile, "/xn " & Fmt.Int(xn) & " def  % grid cells along x axis\n");
      Wr.PutText(psfile, "/xstep " & EL((Hmax - Hmin)/fxn) & " def  % x-size of grid cell\n");

      Wr.PutText(psfile, "/ymin " & EL(Vmin) & " def  % min plottable y\n");
      Wr.PutText(psfile, "/ymax " & EL(Vmax) & " def  % max plottable y\n");
      Wr.PutText(psfile, "/yn " & Fmt.Int(yn) & " def  % grid cells along y axis\n");
      Wr.PutText(psfile, "/ystep " & EL((Vmax - Vmin)/fyn) & " def  % y-size of grid cell\n");

      Wr.PutText(psfile, "% Units of measure:\n");
      Wr.PutText(psfile, "/pt 1.0 def\n");
      Wr.PutText(psfile, "/in pt 72.0 mul def \n");
      Wr.PutText(psfile, "/mm pt 72.0 25.4 div mul def\n");
      Wr.PutText(psfile, "\n");

      Wr.PutText(psfile, "% Set clipping path to boundary of plot area:\n");
      Wr.PutText(psfile, "newpath\n");
      Wr.PutText(psfile, "  xmin ymin moveto\n");
      Wr.PutText(psfile, "  xmax ymin lineto\n");
      Wr.PutText(psfile, "  xmax ymax lineto\n");
      Wr.PutText(psfile, "  xmin ymax lineto\n");
      Wr.PutText(psfile, "  xmin ymin lineto\n");
      Wr.PutText(psfile, "clip\n");
      Wr.PutText(psfile, "\n");

      Wr.PutText(psfile, "% Caption text cursor:\n");
      Wr.PutText(psfile, "/xtext xmin def\n");
      Wr.PutText(psfile, "/ytext ymin def\n");
      Wr.PutText(psfile, "/dytext 10 pt mul def\n");
      Wr.PutText(psfile, "\n");

      Wr.PutText(psfile, "% Caption font setup:\n");
      Wr.PutText(psfile, "/Courier findfont\n");
      Wr.PutText(psfile, "dytext scalefont setfont\n");
      Wr.PutText(psfile, "\n");

      Wr.PutText(psfile, "/savedstate save def\n");
      Wr.PutText(psfile, "\n");

      Wr.Flush(psfile);
    END;
  END BeginFile;

PROCEDURE EndFile (psfile: Wr.T) =
  <* FATAL Wr.Failure, Thread.Alerted *>
  BEGIN
    Wr.PutText(psfile, "savedstate restore\n");
    Wr.PutText(psfile, "% Now we are back to the standard coord system.\n");
    Wr.PutText(psfile, "\n");

    Wr.PutText(psfile, "end % $maindict\n");
    Wr.PutText(psfile, "\n");

    Wr.PutText(psfile, "%%Trailer\n");
    Wr.PutText(psfile, "%%Pages: 1\n");
    Wr.Flush(psfile);
  END EndFile;

PROCEDURE FIP(x: INTEGER; w: CARDINAL): TEXT =
  BEGIN
    RETURN Fmt.Pad(Fmt.Int(x), w)
  END FIP;

PROCEDURE FL(x: LONGREAL; d: CARDINAL): TEXT =
  BEGIN
    RETURN Fmt.LongReal(x, d, Fmt.Style.Flo)
  END FL;

PROCEDURE FLP(x: LONGREAL; d, w: CARDINAL): TEXT =
  BEGIN
    RETURN Fmt.Pad(Fmt.LongReal(x, d, Fmt.Style.Flo), w)
  END FLP;

PROCEDURE SubChar(txt: TEXT; a, b: CHAR): TEXT =
  BEGIN
    WITH i = Text.FindChar(txt, a) DO
      IF i = -1 THEN
        RETURN txt
      ELSE
        RETURN 
          Text.Sub(txt, 0, i) & 
          Text.FromChar(b) & 
          Text.Sub(txt, i+1, Text.Length(txt) - i - 1)
      END
    END
  END SubChar;

PROCEDURE EL(x: LONGREAL; d: CARDINAL := 6): TEXT =
  BEGIN
    RETURN SubChar(Fmt.LongReal(x, d, Fmt.Style.Sci), 'D', 'E')
  END EL;

PROCEDURE FR(x: REAL; d: CARDINAL): TEXT =
  BEGIN
    RETURN Fmt.Real(x, d, Fmt.Style.Flo)
  END FR;

PROCEDURE FRP(x: REAL; d, w: CARDINAL): TEXT =
  BEGIN
    RETURN Fmt.Pad(Fmt.Real(x, d, Fmt.Style.Flo), w)
  END FRP;

PROCEDURE AddCaption (psfile: Wr.T; txt: TEXT) =
  <* FATAL Wr.Failure, Thread.Alerted *>
  BEGIN
    Wr.PutText(psfile, "nl ");
    PSPutText(psfile, txt, ") shw\nnl (");
    Wr.PutText(psfile, " shw\n");
    Wr.Flush(psfile);
  END AddCaption;

PROCEDURE BeginSection (psfile: Wr.T; title: TEXT) =
  <* FATAL Wr.Failure, Thread.Alerted *>
  BEGIN
    Wr.PutText(psfile, "% " & title & "\n");
    Wr.PutText(stderr, "[" & title & "]\n");
    Wr.Flush(psfile);
  END BeginSection;

PROCEDURE EndSection (psfile: Wr.T) =
  <* FATAL Wr.Failure, Thread.Alerted *>
  BEGIN
    Wr.PutText(psfile, "\n");
    Wr.Flush(psfile);
  END EndSection;

PROCEDURE DrawFrame (psfile: Wr.T) =
  <* FATAL Wr.Failure, Thread.Alerted *>
  BEGIN
    BeginSection (psfile, "Draw frame around plot area");
    Wr.PutText(psfile, "gsave\n");
    Wr.PutText(psfile, "% Assumes xmax, xmin, ymax, ymin are defined.\n");
    Wr.PutText(psfile, "  initclip\n");
    Wr.PutText(psfile, "  newpath\n");
    Wr.PutText(psfile, "  xmin ymin moveto\n");
    Wr.PutText(psfile, "  xmax ymin lineto\n");
    Wr.PutText(psfile, "  xmax ymax lineto\n");
    Wr.PutText(psfile, "  xmin ymax lineto\n");
    Wr.PutText(psfile, "  xmin ymin lineto\n");
    Wr.PutText(psfile, "  closepath stroke\n");
    Wr.PutText(psfile, "grestore\n");
    EndSection (psfile);
  END DrawFrame;

PROCEDURE SetPen (
    psfile: Wr.T;
    gray: REAL;
    width: REAL;
    dashlength: REAL;
    dashspace: REAL
  ) =
  <* FATAL Wr.Failure, Thread.Alerted *>
  BEGIN
    Wr.PutText(psfile, FR(gray, 3) & " setgray\n");
    Wr.PutText(psfile, "mm " & FR(width, 3) & " mul setlinewidth\n");
    IF dashlength = 0.0 OR dashspace = 0.0 THEN
      Wr.PutText(psfile, "[ ] 0 setdash\n")
    ELSE
      Wr.PutText(psfile,
        "[ mm " & FR(dashlength, 6) & " mul " & 
        "  mm " & FR(dashspace, 6) & " mul ] 0 setdash\n"
      );
    END;
    Wr.PutText(psfile, "\n");
    Wr.Flush(psfile);
  END SetPen;

PROCEDURE DrawSegment (psfile: Wr.T; xa, ya, xb, yb: LONGREAL) =
  <* FATAL Wr.Failure, Thread.Alerted *>
  BEGIN
    WITH
      psxa = Hmin + Scale * (xa - Xmin),
      psya = Vmin + Scale * (ya - Ymin),
      psxb = Hmin + Scale * (xb - Xmin),
      psyb = Vmin + Scale * (yb - Ymin)
    DO
      Wr.PutText(psfile,
        FLP(psxa, 2, 6) & " " & FLP(psya, 2, 6) & "  " & 
        FLP(psxb, 2, 6) & " " & FLP(psyb, 2, 6) & " segd\n"
      );
    END;
    Wr.Flush(psfile);
  END DrawSegment;  

PROCEDURE AuxDrawRectangle(
    psfile: Wr.T; 
    xlo, xhi, ylo, yhi: LONGREAL;
    gray: REAL;
    operator: TEXT
  ) =
  <* FATAL Wr.Failure, Thread.Alerted *>
  BEGIN
    WITH
      psxlo = Hmin + Scale * (xlo - Xmin),
      psxhi = Hmin + Scale * (xhi - Xmin),
      psylo = Vmin + Scale * (ylo - Ymin),
      psyhi = Vmin + Scale * (yhi - Ymin)
    DO
      Wr.PutText(psfile, 
        FLP(psxlo, 2, 6) & " " & FLP(psxhi, 2, 6) & "  " & 
        FLP(psylo, 2, 6) & " " & FLP(psyhi, 2, 6)
      );
      IF gray >= 0.0 THEN Wr.PutText(psfile, "  " & FR(gray, 4)) END;
      Wr.PutText(psfile, " " & operator & "\n");
      Wr.Flush(psfile);
    END
  END AuxDrawRectangle;

PROCEDURE DrawRectangle (psfile: Wr.T; xlo, xhi, ylo, yhi: LONGREAL) =
  <* FATAL Wr.Failure, Thread.Alerted *>
  BEGIN
    AuxDrawRectangle (psfile, xlo, xhi, ylo, yhi, -1.0, "recd");
  END DrawRectangle;

PROCEDURE FillRectangle (
    psfile: Wr.T; 
    xlo, xhi, ylo, yhi: LONGREAL;
    gray: REAL
  ) =
  <* FATAL Wr.Failure, Thread.Alerted *>
  BEGIN
    AuxDrawRectangle (psfile, xlo, xhi, ylo, yhi, gray, "recf");
  END FillRectangle;

PROCEDURE FillAndDrawRectangle (
    psfile: Wr.T;
    xlo, xhi, ylo, yhi: LONGREAL;
    gray: REAL
  ) =
  <* FATAL Wr.Failure, Thread.Alerted *>
  BEGIN
    AuxDrawRectangle (psfile, xlo, xhi, ylo, yhi, gray, "recfd");
  END FillAndDrawRectangle;

PROCEDURE FillTriangle (
    psfile: Wr.T;
    xa, ya, xb, yb, xc, yc: LONGREAL;
    gray: REAL
  ) =
  <* FATAL Wr.Failure, Thread.Alerted *>
  BEGIN
    WITH
      psxa = Hmin + Scale * (xa - Xmin),
      psya = Vmin + Scale * (ya - Ymin),
      psxb = Hmin + Scale * (xb - Xmin),
      psyb = Vmin + Scale * (yb - Ymin),
      psxc = Hmin + Scale * (xc - Xmin),
      psyc = Vmin + Scale * (yc - Ymin)
    DO
      Wr.PutText(psfile,
        FLP(psxa, 2, 6) & " " & FLP(psya, 2, 6) & "  " & 
        FLP(psxb, 2, 6) & " " & FLP(psyb, 2, 6) & "  " & 
        FLP(psxc, 2, 6) & " " & FLP(psyc, 2, 6) & "  " &
        FRP(gray, 2, 6) & " trif\n"
      );
      Wr.Flush(psfile);
    END
  END FillTriangle;

PROCEDURE AuxDrawCircle (
    psfile: Wr.T;
    xc, yc, radius: LONGREAL;
    gray: REAL;
    operator: TEXT;
  ) =
  <* FATAL Wr.Failure, Thread.Alerted *>
  BEGIN
    WITH
      psxc = Hmin + Scale * (xc - Xmin),
      psyc = Vmin + Scale * (yc - Ymin),
      psradius = Scale * radius
    DO
      Wr.PutText(psfile, 
        FLP(psxc, 2, 6) & " " & FLP(psyc, 2, 6) & "  " & 
        FLP(psradius, 2, 6)
      );
      IF gray >= 0.0 THEN Wr.PutText(psfile, "  " & FR(gray, 4)) END;
      Wr.PutText(psfile, " " & operator & "\n");
      Wr.Flush(psfile);
    END
  END AuxDrawCircle;

PROCEDURE FillCircle (
    psfile: Wr.T;
    xc, yc, radius: LONGREAL;
    gray: REAL
  ) =
  BEGIN
    AuxDrawCircle (psfile, xc, yc, radius, gray, "cirf");
  END FillCircle;
  
PROCEDURE DrawCircle (
    psfile: Wr.T;
    xc, yc, radius: LONGREAL;
  ) =
  BEGIN
    AuxDrawCircle (psfile, xc, yc, radius, -1.0, "cird");
  END DrawCircle;
  
PROCEDURE FillAndDrawCircle (
    psfile: Wr.T;
    xc, yc, radius: LONGREAL;
    gray: REAL;
  ) =
  BEGIN
    AuxDrawCircle (psfile, xc, yc, radius, gray, "cirfd");
  END FillAndDrawCircle;
  
PROCEDURE FillGridCell (psfile: Wr.T; xi, yi: CARDINAL; gray: REAL) =
  <* FATAL Wr.Failure, Thread.Alerted *>
  BEGIN
    Wr.PutText(psfile, 
      FIP(xi, 3) & " " & FIP(yi, 3) & "  " & FRP(gray, 3, 4) & " celf\n"
    );
    Wr.Flush(psfile);
  END FillGridCell;

PROCEDURE DrawCoordLine (psfile: Wr.T; axis: Axis; coord: LONGREAL) =
  VAR pscoord: LONGREAL;
      op: TEXT;
  <* FATAL Wr.Failure, Thread.Alerted *>
  BEGIN
    IF axis = Axis.X THEN
      pscoord := Hmin + Scale * (coord - Xmin); op := "xgrd"
    ELSE
      pscoord := Vmin + Scale * (coord - Ymin); op := "ygrd"
    END;
    Wr.PutText(psfile, FLP(pscoord, 2, 6) & " " & op & "\n");
  END DrawCoordLine;

PROCEDURE DrawGridLines (psfile: Wr.T) = 
  <* FATAL Wr.Failure, Thread.Alerted *>
  BEGIN
    Wr.PutText(psfile, "% Grid lines:\n");
    Wr.PutText(psfile, "gsave\n");
    Wr.PutText(psfile, "  initclip\n");
    Wr.PutText(psfile, "  0 1 xn {\n");
    Wr.PutText(psfile, "    xstep mul xmin add xgrd\n");
    Wr.PutText(psfile, "  } for\n");
    Wr.PutText(psfile, "  0 1 yn {\n");
    Wr.PutText(psfile, "    ystep mul ymin add ygrd\n");
    Wr.PutText(psfile, "  } for\n");
    Wr.PutText(psfile, "grestore\n");
    Wr.PutText(psfile, "\n");
    Wr.Flush(psfile);
  END DrawGridLines;
  
PROCEDURE PSPutText (psfile: Wr.T; text: TEXT; newline: TEXT) =
  <* FATAL Wr.Failure, Thread.Alerted *>
  BEGIN
    Wr.PutChar(psfile, '(');
    FOR i := 0 TO Text.Length(text) - 1 DO
      WITH c = Text.GetChar(text, i) DO
        IF c = '\n' THEN
          Wr.PutText(psfile, newline)
        ELSIF c = '(' THEN
          Wr.PutChar(psfile, '\\'); Wr.PutChar(psfile, '(');
        ELSIF c = ')' THEN
          Wr.PutChar(psfile, '\\'); Wr.PutChar(psfile, ')');
        ELSIF c =  '\t' THEN
          Wr.PutChar(psfile, ' '); Wr.PutChar(psfile, ' ');
        ELSIF c =  '\\' THEN
          Wr.PutChar(psfile, '\\'); Wr.PutChar(psfile, '\\');
        ELSIF c < ' ' OR c > '~' THEN
          Wr.PutText(psfile, "\\" & Fmt.Pad(Fmt.Int(ORD(c), base := 8), 3, '0'));
        ELSE
          Wr.PutChar(psfile, c)
        END
      END
    END;
    Wr.PutText(psfile, ")");
  END PSPutText;

BEGIN
END EPSPlot.


