
/* "UNTIL", a graphics editor,
   Copyright (C) 1985, 1990 California Institute of Technology.
   Original authors: Glenn Gribble, port by Steve DeWeerth
   Unix Port Maintainer: John Lazzaro
   Maintainers's address: lazzaro@hobiecat.cs.caltech.edu;
                          CB 425 CU Boulder/Boulder CO 91125. 

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation (Version 1, 1989).

This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; see the file COPYING.  If not, write to the
Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
USA. */


/*******************************************************************************/
/*                                                                             */ 
/*  file contains stuff to deal with HPIB pen plotter (should be removed?)     */
/*  cleaned up by steve - 9 May 1990                                           */
/*                                                                             */ 
/*******************************************************************************/

#ifdef mips
#define const
#endif


#include <p2c/p2c.h>

#include "gr_stuff.h"


#ifndef SYSGLOBALS_H
#include <p2c/sysglobals.h>
#endif

#ifndef MYLIB_H
#include <p2c/mylib.h>
#endif

#ifndef MAT_STUFF_H
#include "mat_stuff.h"
#endif

#ifndef SYSDEVS_H
#include <p2c/sysdevs.h>
#endif

#ifndef NEWASM_H
#include <p2c/newasm.h>
#endif



typedef struct rmat_matrix {
  double a, b, c, d;
} rmat_matrix;



/* exported global variables */

gr_outKind gr_outDev;
boolean gr_show_seg;   /* Show arc control segments */
boolean gr_show_control;   /* Show bezier control segments */

long gr_numpt;
long *gr_xs, *gr_ys;
_PROCEDURE ps_out;
_PROCEDURE lbl_out;
fontArrRec *fonts;
char numFonts;
headInfoRec headinfo;
char symbolFontNum;   /* used for displaying characters on screen */
char musicFontNum;   /* used for displaying notes on screen */
char psColorVersion;
boolean hasAlpha;
colorArrRec *colors;
bbrec psBoundingBox;


/* A real identity matrix */

static const rmat_matrix rmat_ident_mat = {
  1.0, 0.0, 0.0, 1.0
};


Static rmat_matrix rctm;
Static long rtx, rty;   /* Translation portion of matrix */

Static long thetaMax;
Static double sinTM, cosTM, tvTM;
Static boolean canFillPath;
Static long ps_x, ps_y, ps_color, ps_rot;
Static m_chpicrec **symbol, **music;


/* Make necessary adjustments */
Static void fixLine(Char *s)
{
  long p;
  Char STR1[256];
  Char STR2[256];

  strcpy(STR1, strltrim(strrtrim(strcpy(STR2, s))));
  strcpy(s, STR1);
  p = strpos2(s, "#", 1);
  if (p != 0 && (p == 1 || s[p - 2] == ' ')) {
    s[p - 1] = '\0';
/* p2c: gr_stuff.text, line 121:
 * Note: Modification of string length may translate incorrectly [146] */
  }
}


long nameToColor(Char *name)
{
  strupper(name, name);
  if (*name == '\0')
    return -1;
  else if (!strcmp(name, "BLACK"))
    return 0;
  else if (!strcmp(name, "RED"))
    return 1;
  else if (!strcmp(name, "GREEN"))
    return 2;
  else if (!strcmp(name, "YELLOW"))
    return 3;
  else if (!strcmp(name, "BLUE"))
    return 4;
  else if (!strcmp(name, "PURPLE"))
    return 5;
  else if (!strcmp(name, "CYAN"))
    return 6;
  else if (!strcmp(name, "WHITE"))
    return 7;
  else if (!strcmp(name, "RED2"))
    return 9;
  else if (!strcmp(name, "GREEN2"))
    return 10;
  else if (!strcmp(name, "YELLOW2"))
    return 11;
  else if (!strcmp(name, "BLUE2"))
    return 12;
  else if (!strcmp(name, "PURPLE2"))
    return 13;
  else if (!strcmp(name, "CYAN2"))
    return 14;
  else if (!strcmp(name, "WHITE2"))
    return 8;
  else if (!strcmp(name, "VERSION"))
    return -3;
  else
    return -2;
}


/* Local variables for readPSColors: */
struct LOC_readPSColors {
  Char where[121];
} ;

Local void error(Char *msg, struct LOC_readPSColors *LINK)
{
  printf("%s\007: %s\n", LINK->where, msg);
  printf("\201[enter] to continue>\200");
  scanf("%*[^\n]");
  getchar();
}


void readPSColors(Char *where_)
{
  struct LOC_readPSColors V;
  Char s[101], cname[101];
  FILE *t;
  long i, col, current;
  boolean foundColor;
  Char *TEMP;
  Char *STR1;
  Char STR2[126];
  int TEMP1;
  Char STR3[256], STR4[256];

  strcpy(V.where, where_);
  t = NULL;
  for (i = 0; i <= maxColors; i++) {
    colors[i].cval = -1;
    *colors[i].action = '\0';
  }
  psColorVersion = 0;
  current = 0;
  foundColor = false;

  if (t != NULL)
    t = freopen(V.where, "r", t);
  else
    t = fopen(V.where, "r");
  if (t == NULL)
    _EscIO(FileNotFound);
  while (fgets(s, 101, t) != NULL) {
    TEMP = (char *)strchr(s, '\n');
    if (TEMP != NULL)
      *TEMP = 0;
    fixLine(s);
    strword(s, cname);
    col = nameToColor(cname);

    switch (col) {

    case -1:   /* blank line */
      break;

    case -2:
      sprintf(STR2, "bad command or color \"%s\"", cname);
      error(STR2, &V);
      break;

    case -3:
      if (foundColor)
	error("VERSION command after COLOR command", &V);
      TRY(try1);
	TEMP1 = strtol(s, &STR1, 10);
	psColorVersion = TEMP1;
	i = STR1 - s + 1;
	if ((unsigned)psColorVersion > 1) {
	  sprintf(STR4, "Unknown VERSION %d", psColorVersion);
	  error(STR4, &V);
	  psColorVersion = 0;
	}
      RECOVER(try1);
	error("VERSION command expects integer", &V);
      ENDTRY(try1);
      break;

    default:
      if ((unsigned long)col <= maxColors) {
	foundColor = true;
	switch (psColorVersion) {

	case 0:
	  strcpy(colors[maxColors - col].action, s);
	  colors[maxColors - col].cval = col;
	  break;

	case 1:
	  strcpy(colors[current].action, s);
	  colors[current].cval = col;
	  if (current < maxColors)
	    current++;
	  else {
	    sprintf(STR3, "More than %ld color commands.", maxColors - 2L);
	    error(STR3, &V);
	  }
	  break;
	}
      } else
	error("bad case selector in readPSColors--tell Glenn.", &V);
      break;
    }
  }
  if (t != NULL)
    fclose(t);
  t = NULL;
  if (t != NULL)
    fclose(t);
}


void setPSColor(long c)
{
  colorArrRec *WITH;
  Char STR2[156];
  Char STR3[256];

  WITH = &colors[c];
  ps_color = WITH->cval;

  sprintf(STR3, "%% color #%ld pos %ld", ps_color, c);
  ((void(*)(Char *s, void *_link))ps_out.proc)(STR3, ps_out.link);

  sprintf(STR2, "%s exec", WITH->action);
  ((void(*)(Char *s, void *_link))ps_out.proc)(STR2, ps_out.link);

  ((void(*)(Char *s, void *_link))ps_out.proc)("%", ps_out.link);
}


void gr_dim(void)
{
  m_colorarray r, g, b;
  long i;

  if (!hasAlpha)
    return;
  m_seecolors(r, g, b);
  for (i = 1; i <= 16; i++) {
    r[i] /= 2;
    g[i] /= 2;
    b[i] /= 2;
  }
  m_setcolors(r, g, b);
}


void gr_bright(void)
{
  m_choosecolors(0);
}


Static void ps_null(Char *s, void *temp)
{
  if (*s == '\0')
    return;
  if (s[0] == '%') {
    if (XPOS > 0)
      putchar('\n');
    printf("\213%s\210\n", s);
    return;
  }
  if (XPOS + strlen(s) > 78)
    putchar('\n');
  if (XPOS > 0)
    putchar(' ');
  printf("\215%s\210", s);
}


Static void lbl_PS(long x, long y, long rot, Char *mode, Char *s, void *temp);

void gr_init(void)
{
  ps_out.proc = ps_null;
  ps_out.link = NULL;
  lbl_out.proc = lbl_PS;
  lbl_out.link = NULL;

  gr_outDev = gr_m;
  colors = Malloc(sizeof(colorArr));
  fonts = Malloc(sizeof(fontArr));
  gr_xs = Malloc(sizeof(gr_intArray));
  gr_ys = Malloc(sizeof(gr_intArray));
  ellipseSegs(3);
  gr_show_seg = false;
  gr_show_control = false;
  m_init_graphics_nopen();
  TRY(try2);
/*    m_loadfont(&symbol, "/LIB/UNTIL/SYMBOL");  procedure not available */
  RECOVER(try2);
    if (P_escapecode != -10)
      _Escape(P_escapecode);
    perror("\007Unable to load /LIB/UNTIL/SYMBOL");
    symbol = NULL;
  ENDTRY(try2);
  TRY(try3);
/*    m_loadfont(&music, "/LIB/UNTIL/MUSICNOT");  procedure not available */
  RECOVER(try3);
    if (P_escapecode != -10)
      _Escape(P_escapecode);
    perror("\007Unable to load /LIB/UNTIL/MUSICNOT");
    music = NULL;
  ENDTRY(try3);
  hasAlpha = (CURRENTCRT == ALPHATYPE);
}


Static void ps_nopnt(void)
{
  ps_x = LONG_MAX;
  ps_y = LONG_MAX;
}


void gr_reInit(gr_outKind kind)
{
  long i;
  Char STR1[256];
  Char STR2[256], STR3[256];

  gr_outDev = kind;
  if (gr_outDev == gr_m)
    return;
  if (gr_outDev != gr_ps)
    return;

  ((void(*)(Char *s, void *_link))ps_out.proc)("%!Not-Quite-PS-Adobe-1.0", ps_out.link);

  /*3/7/90 Calvin: prologue info is stashed in main for here*/

  sprintf(STR3, "%%%%BeginFile: %s %s", strrtrim(strcpy(STR1, headinfo.fnm)), 
                                        strrtrim(strcpy(STR2, headinfo.fignm)));
  ((void(*)(Char *s, void *_link))ps_out.proc)(STR3, ps_out.link);

  ((void(*)(Char *s, void *_link))ps_out.proc)("%%Creator: Until", ps_out.link);

  i = strpos2(headinfo.whowhn, " by ", 1);
  if (i > 0) {
    sprintf(STR3, "%%%%CreationDate: %.*s", (int)(i - 1), headinfo.whowhn);
    ((void(*)(Char *s, void *_link))ps_out.proc)(STR3, ps_out.link);

    sprintf(STR3, "%%%%For: %.*s", (int)(strlen(headinfo.whowhn) - i - 3), headinfo.whowhn + i + 3);
    ((void(*)(Char *s, void *_link))ps_out.proc)(STR3, ps_out.link);
  }

  sprintf(STR3, "%%%%BoundingBox: %ld %ld %ld %ld",
                 headinfo.bbxl, headinfo.bbyl, headinfo.bbxh, headinfo.bbyh);
  ((void(*)(Char *s, void *_link))ps_out.proc)(STR3, ps_out.link);

 ((void(*)(Char *s, void *_link))ps_out.proc)
          ("% $ Include /lib/laserwriter/untilstuff.ps", ps_out.link); 

  ((void(*)(Char *s, void *_link))ps_out.proc)("%%EndProlog", ps_out.link);

  sprintf(STR2, "{/TeX-MBB [%ld %ld %ld %ld] def} Not-In-TeX",
                 psBoundingBox.xl, psBoundingBox.yl, psBoundingBox.xh, psBoundingBox.yh);
  ((void(*)(Char *s, void *_link))ps_out.proc)(STR2, ps_out.link);

  ps_color = -1;

  ps_nopnt();
}


void gr_finish(void)
{
  if (gr_outDev == gr_m)
    return;

  if (gr_outDev != gr_ps)
    return;

  ((void(*)(Char *s, void *_link))ps_out.proc)("{showpage} Not-In-TeX", ps_out.link);

  ((void(*)(Char *s, void *_link))ps_out.proc)("UNTILFINISH", ps_out.link);

  ((void(*)(Char *s, void *_link))ps_out.proc)("%%EndFile", ps_out.link);

  ps_out.proc = ps_null;
  ps_out.link = NULL;
  lbl_out.proc = lbl_PS;
  lbl_out.link = NULL;
  ps_nopnt();
}


void newpath(boolean canFill)
{
  canFillPath = canFill;

  if (gr_outDev == gr_m)
    return;

  if (gr_outDev != gr_ps)
    return;
  if (m_curcolor() != ps_color) {
    printf("\213\007\201Help, I am color-confused.  \200 m_curcolor=%ld ps_color=%ld\n",
           m_curcolor(), ps_color);
    printf("Be sure to tell Glenn...     press return>");
    scanf("%*[^\n]");
    getchar();
    ps_color = m_curcolor();   /* Stop further complaints */
  }

  ((void(*)(Char *s, void *_link))ps_out.proc)("NP", ps_out.link);

  ps_nopnt();
}


void finpath(void)
{
  if (gr_outDev == gr_m)
    return;

  if (gr_outDev != gr_ps)
    return;
  if (canFillPath) {   /* Stroke path */
    ((void(*)(Char *s, void *_link))ps_out.proc)("CP", ps_out.link);
    ((void(*)(Char *s, void *_link))ps_out.proc)("FP", ps_out.link);  /* Fill path */
  } 
  else
    ((void(*)(Char *s, void *_link))ps_out.proc)("SP", ps_out.link);

  ps_nopnt();
}


/* What is the current matrix??? */
Static void rmat_print(void)
{
  printf("|%14.6f%14.6f|\n", rctm.a, rctm.b);
  printf("|%14.6f%14.6f|\n", rctm.c, rctm.d);
}


Static void rmat_scale(double x, double y)
{
  rctm.a *= x;
  rctm.b *= x;
  rctm.c *= y;
  rctm.d *= y;
}


Static void rmat_rot(double c, double s)
{
  /* cosine, sine of rotation */
  rmat_matrix new_ctm;

  new_ctm.a = c * rctm.a + s * rctm.c;
  new_ctm.b = c * rctm.b + s * rctm.d;
  new_ctm.c = c * rctm.c - s * rctm.a;
  new_ctm.d = c * rctm.d - s * rctm.b;
  rctm = new_ctm;
}


/* This is the magic function to decide how long the bezier segments are. */
/* No, I am not going to say why.  You will have to derive it yourself. */
Static double t(long theta)
{
  double sinTh;

  sinTh = sinD(theta);
  if (fabs(sinTh) < 0.000000001)
    return 0.0;
  else
    return ((8.0 * cosD(theta / 2) - 4.0 - 4.0 * cosD(theta)) / (3 * sinTh));
}


void ellipseSegs(long numSegs)
{
  if (numSegs < 3)
    numSegs = 3;
  else if (numSegs > 360)
    numSegs = 360;

  /* These are pseudo-constants, but I want to be able to change them. */
  thetaMax = 360 / numSegs;
  sinTM = sinD(thetaMax);
  cosTM = cosD(thetaMax);
  tvTM = t(thetaMax);
}


Static void addPnt(double x, double y)
{
  gr_numpt++;
  gr_xs[gr_numpt - 1] = (long)floor(x * rctm.a + y * rctm.c + 0.5) + rtx;
  gr_ys[gr_numpt - 1] = (long)floor(x * rctm.b + y * rctm.d + 0.5) + rty;
}


void makeEllipseArc(long cx, long cy, long rx, long ry, long start,
		    long length, long rotate)
{
  double tv, sinT, cosT, sinS, cosS;
  boolean done, first;

  rmat_matrix conMat;

  /* Initialize some global stuff */
  rtx = cx;
  rty = cy;
  gr_numpt = 0;

  /* For each curve generated in canonical Quadrant I at 1.0 radius, we */
  /* need to transform it to get the right stuff. */

  /* We always need to scale by rx,ry; rotate by rotate; and translate by cx,cy */
  /* For each time, we also need to rotate by start which changes each time. */
  /* So, we build the constant transforms here. */

  rctm = rmat_ident_mat;
  rmat_rot(cosD(rotate), sinD(rotate));
  rmat_scale(rx, ry);
  conMat = rctm;

  /* Force things to be in right range */
  if (length < 0) {
    start += length;
    length = -length;
  }

  done = false;
  first = true;
  while (!done) {
    /* Compute these before we change START */
    cosS = cosD(start);
    sinS = sinD(start);

    if (length > thetaMax) {
      sinT = sinTM;
      cosT = cosTM;
      tv = tvTM;
      length -= thetaMax;
      start += thetaMax;
    } else {
      done = true;
      sinT = sinD(length);
      cosT = cosD(length);
      tv = t(length);
    }

    /* Finish the matrix by rotating by start */
    rctm = conMat;
    rmat_rot(cosS, sinS);

    if (first) {
      addPnt(1.0, 0.0);
      first = false;
    }
    addPnt(1.0, tv);
    addPnt(cosT + tv * sinT, sinT - tv * cosT);
    addPnt(cosT, sinT);
  }
}


void drawPoly(long n, long *x, long *y)
{
  long i;

  move_(x[n - 1], y[n - 1]);
  for (i = 0; i < n; i++)
    draw(x[i], y[i]);
}


/* An absolute point, update current point. */
Static void ps_pnt(long x, long y)
{
  Char s[21];
  long i;

  sprintf(s, "%ld %ld", x, y);
  i = strlen(s) + 1;

  ((void(*)(Char *s, void *_link))ps_out.proc)(s, ps_out.link);
}


/* A point relative to the current point, update current point. */
Static void ps_rpnt(long x, long y)
{
  Char s[21];
  long i;

  if (ps_x == LONG_MAX) {
    printf("\007ps_rpnt: no current point\n");
    return;
  }
  sprintf(s, "%ld %ld", x - ps_x, y - ps_y);
  i = strlen(s) + 1;

  ((void(*)(Char *s, void *_link))ps_out.proc)(s, ps_out.link);
}


Static void ps_int(long x)
{
  Char s[21];
  long i;

  sprintf(s, "%ld", x);
  i = strlen(s) + 1;

  ((void(*)(Char *s, void *_link))ps_out.proc)(s, ps_out.link);
}


Static void ps_move(long x, long y)
{
  if (x == ps_x && y == ps_y)
    return;
  ps_pnt(x, y);

  ((void(*)(Char *s, void *_link))ps_out.proc)("M", ps_out.link);

  ps_x = x;
  ps_y = y;
}


Static void ps_draw(long x, long y)
{
  ps_rpnt(x, y);

  ((void(*)(Char *s, void *_link))ps_out.proc)("R", ps_out.link);

  ps_x = x;
  ps_y = y;
}


void bezier(long x1, long y1, long x2, long y2, long x3, long y3, long x4,
	    long y4)
{
  mat_transform(x1, y1, &x1, &y1);
  mat_transform(x2, y2, &x2, &y2);
  mat_transform(x3, y3, &x3, &y3);
  mat_transform(x4, y4, &x4, &y4);
  if (gr_outDev == gr_m) {
    m_bezier(x1, y1, x2, y2, x3, y3, x4, y4);
    if (!gr_show_control)
      return;
    m_move(x1, y1);
    m_draw(x2, y2);
    m_draw(x3, y3);
    m_draw(x4, y4);

    m_fillrect(x1 - 1, y1 - 1, x1 + 1, y1 + 1);
    m_fillrect(x2 - 1, y2 - 1, x2 + 1, y2 + 1);
    m_fillrect(x3 - 1, y3 - 1, x3 + 1, y3 + 1);
    m_fillrect(x4 - 1, y4 - 1, x4 + 1, y4 + 1);
    return;
  }

  if (gr_outDev != gr_ps)
    return;
  ps_move(x1, y1);
  ps_rpnt(x2, y2);
  ps_rpnt(x3, y3);
  ps_rpnt(x4, y4);
  ps_x = x4;
  ps_y = y4;

  ((void(*)(Char *s, void *_link))ps_out.proc)("CT", ps_out.link);
}


void ellipseArc(long cx, long cy, long rx, long ry, long start, long length,
		long rotate, boolean pie)
{
  long i;

  makeEllipseArc(cx, cy, rx, ry, start, length, rotate);
  /* Now draw the curves */

  if (gr_show_seg && gr_numpt > 0) {
    gr_xs[gr_numpt] = cx;
    gr_ys[gr_numpt] = cy;
    /*m_fillpoly(gr_numpt+1,gr_xs^,gr_ys^);*/
    drawPoly(gr_numpt, gr_xs, gr_ys);
    /*m_move(gr_xs^[1],gr_ys^[1]);*/
    /*m_draw(gr_xs^[gr_numpt],gr_ys^[gr_numpt]);*/
  }

  if (pie) {
    move_(gr_xs[gr_numpt - 1], gr_ys[gr_numpt - 1]);
    draw(cx, cy);
    draw(gr_xs[0], gr_ys[0]);
  }
  i = 1;
  while (i < gr_numpt) {
    bezier(gr_xs[i - 1], gr_ys[i - 1], gr_xs[i], gr_ys[i], gr_xs[i + 1],
	   gr_ys[i + 1], gr_xs[i + 2], gr_ys[i + 2]);
    i += 3;
  }
}


void ellipseArcBB(long cx, long cy, long rx, long ry, long start, long length,
		  long rotate, boolean pie, bbrec *bb)
{
  long i, FORLIM;

  makeEllipseArc(cx, cy, rx, ry, start, length, rotate);

  if (pie) {
    gr_numpt++;
    gr_xs[gr_numpt - 1] = cx;
    gr_ys[gr_numpt - 1] = cy;
  }

  emptybb(bb);
  FORLIM = gr_numpt;
  for (i = 1; i < FORLIM; i++)
    addbbpnt(bb, gr_xs[i], gr_ys[i]);
}


void showPoint(long x, long y)
{
  mat_transform(x, y, &x, &y);
  m_fillrect(x - 1, y - 1, x + 1, y + 1);
  m_drawrect(x - 3, y - 3, x + 3, y + 3);
}


void chooseFont(long font, long scale, long rot, long slant)
{
  Char out[101];
  long i;

  if (font <= 0)
    font = 1;
  else if (font > numFonts)
    font = numFonts;

  rot = (rot + mat_findRot()) % 360;
/* p2c: gr_stuff.text, line 734:
 * Note: Using % for possibly-negative arguments [317] */
  if (gr_outDev == gr_m) {
    if (font == symbolFontNum) {
/*      m_setfont(symbol);  procedure not available */
      return;
    }
    if (font == musicFontNum) {
/*      m_setfont(music);  procedure not available */
      return;
    }
    if (labs(rot - 90) < 45) {
      m_choosefont(2);
      return;
    }
    if (labs(rot - 270) < 45)
      m_choosefont(3);
    else
      m_choosefont(1);
    return;
  }

  if (gr_outDev != gr_ps)  /* Dig up the font and then forget it later. */
    return;
  /* scale:shortInt;   Scale (in points) */
  /* rot  :shortInt;   Rotation Angle*/
  /* slant:shortInt;   Slant Angle   */
  /* font :shortInt;   Which text font */
  ps_rot = rot;
  sprintf(out, "/FTMP %ld /%s DF FTMP SF", scale, fonts[font - 1].psName);
  i = strlen(out) + 1;
  if (lbl_out.proc != lbl_PS)
    return;

  ((void(*)(Char *s, void *_link))ps_out.proc)(out, ps_out.link);
}


Static void lbl_PS(long x, long y, long rot, Char *mode_, Char *s_, void *temp)
{
  Char mode[256], s[256];
  long i;
  Char out[81];
  Char STR1[256], STR2[256];

  strcpy(mode, mode_);
  strcpy(s, s_);
  i = 1;
  while (i <= strlen(s)) {
    if (s[i - 1] == '\\' || s[i - 1] == ')' || s[i - 1] == '(') {
      sprintf(STR1, "\\%s", s + i - 1);
      strcpy(s + i - 1, STR1);
      i++;
    }
    i++;
  }
  if (rot != 0) {
    sprintf(out, "gsave %ld %ld translate %ld rotate", x, y, rot);
    i = strlen(out) + 1;

    ((void(*)(Char *s, void *_link))ps_out.proc)(out, ps_out.link);

    sprintf(STR2, "(%s)", s);
    ((void(*)(Char *s, void *_link))ps_out.proc)(STR2, ps_out.link);

    sprintf(STR1, "0 0 %s grestore", mode);
    ((void(*)(Char *s, void *_link))ps_out.proc)(STR1, ps_out.link);

    return;
  }

  sprintf(STR2, "(%s)", s);
  ((void(*)(Char *s, void *_link))ps_out.proc)(STR2, ps_out.link);

  ps_pnt(x, y);

  ((void(*)(Char *s, void *_link))ps_out.proc)(mode, ps_out.link);
}


Static Char *centerType_NAMES[] = {
  "TUL", "TUC", "TUR", "TCL", "TCC", "TCR", "TLL", "TLC", "TLR"
} ;


void drawText(long x, long y, centerType orig, Char *s)
{
  Char t[11];
  long i;

  mat_transform(x, y, &x, &y);
  if (gr_outDev == gr_m) {
    switch (orig) {

    case TLL:
      m_drawstr(x, y + 7, NULL, s);
      break;

    case TLC:
      m_centerstr(x, y + 7, NULL, s);
      break;

    case TLR:
      m_rightstr(x, y + 7, NULL, s);
      break;

    case TCL:
      m_drawstr(x, y + 3, NULL, s);
      break;

    case TCC:
      m_centerstr(x, y + 3, NULL, s);
      break;

    case TCR:
      m_rightstr(x, y + 3, NULL, s);
      break;

    case TUL:
      m_drawstr(x, y, NULL, s);
      break;

    case TUC:
      m_centerstr(x, y, NULL, s);
      break;

    case TUR:
      m_rightstr(x, y, NULL, s);
      break;

    }
    return;
  }

  if (gr_outDev != gr_ps)
    return;

  strcpy(t, centerType_NAMES[(long)orig]);
  i = strlen(t) + 1;

  ((void(*)(long x, long y, long rot, Char *mode, Char *s, void *_link))lbl_out.proc)
             (x, y, ps_rot, t, s, lbl_out.link);
}


void circle(long px, long py, long pr)
{
  long x, y, r;

  r = mat_newLength(pr);

  mat_transform(px, py, &x, &y);
  if (gr_outDev == gr_m) {
    m_circle(x, y, r);
    return;
  }

  if (gr_outDev != gr_ps)
    return;
  ps_pnt(x, y);
  ps_int(r);

  ((void(*)(Char *s, void *_link))ps_out.proc)("CI", ps_out.link);
}


Static void oellipse(long x, long y, long a, long b)
{
  long c;

  c = m_curcolor();
  m_color(m_trans);   /* Don't fill... */
  mat_transform(x, y, &x, &y);   /* Adjust coordinates */
  mat_rtransform(a, b, &a, &b);   /* Adjust axis */
  m_ellipse(x, y, a, b, c);
  m_color(c);
}


void ellipse(long x, long y, long a, long b)
{
  ellipseArc(x, y, a, b, 0, 360, 0, false);
}


void drawDot(long x, long y)
{
  mat_transform(x, y, &x, &y);
  if (gr_outDev == gr_m) {
    m_fillrect(x - 1, y - 1, x + 1, y + 1);
    return;
  }

  if (gr_outDev != gr_ps)
    return;
  ps_pnt(x, y);

  ((void(*)(Char *s, void *_link))ps_out.proc)("DOT", ps_out.link);
}


void move_(long x, long y)
{
  mat_transform(x, y, &x, &y);

  if (gr_outDev == gr_m)
    m_move(x, y);
  else if (gr_outDev == gr_ps)
    ps_move(x, y);
}


void draw(long x, long y)
{
  mat_transform(x, y, &x, &y);

  if (gr_outDev == gr_m)
    m_draw(x, y);
  else if (gr_outDev == gr_ps)
    ps_draw(x, y);
}


void drawbox(long x1, long y1, long x2, long y2)
{
  move_(x1, y1);
  draw(x1, y2);
  draw(x2, y2);
  draw(x2, y1);
  draw(x1, y1);
}
