[comp.sources.unix] v11i101: Graphics editor for Suns, Part05/06

rsalz@uunet.UU.NET (Rich Salz) (10/07/87)

Submitted-by: steinmetz!sbcs!nyfca1!chan (Douglas Chan)
Posting-number: Volume 11, Issue 101
Archive-name: graphedit/part05

# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Contents:  display.c ps.c pic.c plot.c
 
echo x - display.c
sed 's/^@//' > "display.c" <<'@//E*O*F display.c//'

static char SccsId[] = "@(#)display.c	1.1 6/8/87  Copyright 1987 SBCS-chan";

/*****************************************************************************

          (c) Copyright 1987 

          Lap-Tak Chan
          Computer Science Department
          State University of New York at Stony Brook
          Stony Brook, NY 11794

    This software and its documentation may be used, copied, and
  distributed, provided that this legend appear in all copies and
  that it is not copied or distributed for a profit.

  No representations is made about the suitability of this software
  for any purpose.  It is provided "as is" with no support and no
  express or implied warranty.

*****************************************************************************/

/*****************************************************************************

                             Display List
                             ============

      The display list is used to maintain the data structure used to store
  the segments internally.  Each segment has a display list, which
  consists of its segment number, attributes, and a instruction list.
  Each instruction is composed of an instruction with two arguments.

      The display list is initialized with ge_init_display.  A segment is
  opened with ge_new_segment.  Output primitives are drawn with ge_graph
  which will appending them to the instruction list of the segment.
  ge_delete_segment will delete the segment and free the memory allocated
  to the segment to the internal free lists. ge_display_list will return
  a pointer to the display list of a segment.

      The display list maintains a list of segments, which is
  a simple linked list of segment blocks taken from a fixed array.
  Unused segment blocks in the array are linked on a free list.
  Each segment block stores a pointer to the node of the object, the
  current position of the segment and a instruction list of the segment.
  The instruction list is stored as a circular queue of display items.
  Each display item consists of a display instruction and a coordinate.

      A hash table should be used to reference the segments instead of
  a search on the segment list.

*****************************************************************************/

#include "display.h"
#define GE_DITEM_SIZE 50 /* number of items to obtain from calloc when
                            free list is empty */

struct list ge_open[GE_OPENMAX+1]; /* array of display lists */
static struct list_item *ge_dfree=0; /* free list for display instructions */
static int ge_dsfree; /* free list for segment blocks */
int ge_seglist; /* list of segment blocks */
/* instruction to indicate close of segment */
static struct list_item closeinstr;

/*****************************************************************************

      Display items have to be allocated and freed frequently.  The C
  allocation routines alloc, calloc and free is not efficient.  To increase
  the efficiency, a list of free display items is maintained.  Requests for
  display item are allocated from the free list.  When the free list is
  empty, a number of display items is obtained with calloc and linked to
  the free list.

  ge_get_item will return a display item which is not in use.  It obtains
  the display item from the free list.  When the free list is empty, it
  get a number of display item from the system with calloc and link
  them to the free list.

*****************************************************************************/

struct list_item *ge_get_item()
{
  struct list_item *tmp;
  int count;

  /* free list is empty */
  if ( ! ge_dfree ) {

    /* get space for list items */
    tmp = (struct list_item *)calloc(GE_DITEM_SIZE,sizeof(struct list_item));

    /* link blocks on free list */
    for(count=1;count < GE_DITEM_SIZE;count++)
      tmp[count].next = &(tmp[count-1]);
    ge_dfree = &(tmp[GE_DITEM_SIZE-1]);
  }

  /* get item from free list */
  tmp=ge_dfree;
  ge_dfree=tmp->next;
  return (tmp);

}

/*****************************************************************************

  ge_free_item will release a display item to the free list.

*****************************************************************************/

ge_free_item(ptr)
struct list_item *ptr;
{

  ptr->next = ge_dfree;
  ge_dfree = ptr;

}

/*****************************************************************************

  ge_init_display initializes the display list.  It links the segment blocks
  on the array to form a free list.

*****************************************************************************/

ge_init_display()
{
  int i;

  /* link segment blocks to form free list */
  for(i=0;i<GE_OPENMAX-1;i++)
    ge_open[i].next = i + 1;
  ge_dsfree=1;

  /* empty segment list */
  ge_seglist=0;

  closeinstr.instr=GE_CLOSE;

}

/*****************************************************************************

  ge_index searches for the display list of a segment.

  input
    segno : segment number

  output
    ge_index returns pointer to display list if found

*****************************************************************************/

int ge_index(segno)
int segno;
{
  int i;

  for ( i=ge_seglist; i && ge_open[i].segno != segno; i=ge_open[i].next );
  if ( ge_open[i].segno == segno ){
    return (i);
  }
  else {
    return(-1);
  }
}

/*****************************************************************************

  ge_new_segment is called to create a new segment.

  input
    tree : pointer to node of object

*****************************************************************************/

ge_new_segment(segno)
int segno;
{
  int pos;

  /* remove segment from free list */
  pos=ge_dsfree;
  ge_dsfree=ge_open[pos].next;

  /* add segment to segment list */
  ge_open[pos].next = ge_seglist;
  ge_seglist = pos;

  /* initialize content of list */
  ge_open[pos].d_list = 0;

  ge_open[pos].segno = segno;

}

/*****************************************************************************

  ge_graph will add an output primitive to the display list of a segment.

  input
    segno : segment number
    instr : display instruction of output primitive (polygon, line, move, etc)
    x,y : coordinate of display

*****************************************************************************/

ge_graph(segno,instr,x,y)
int segno, instr;
float x,y;
{
  int pos;
  struct list_item *ptr;

  /* get display list of segment */
  if ( (pos = ge_index(segno))>=0 ) {

    /* get display item */
    ptr = ge_get_item();

    /* store instruction and coordinate in item */
    ptr->instr=instr;
    ptr->x=x;
    ptr->y=y;

    /* add instruction to display list */    
    if ( ge_open[pos].d_list ) {
      ptr->next = ge_open[pos].d_list->next;
      ge_open[pos].d_list->next = ptr;
    }
    else ptr->next = ptr;
    ge_open[pos].d_list = ptr;

  }

} /* ge_graph */

/*****************************************************************************

  ge_delete_segment is called to delete a segment from the display list.

  input
    segno : segment number

*****************************************************************************/

ge_delete_segment(segno)
int segno;
{
  
  int pos,tmpseg;
  struct list_item *ptr,*tmp;

  /* search for display list of segment */
  if ( pos=ge_index(segno) ) {

    /* delete segment block from list of segments */
    if ( pos == ge_seglist ) {
      /* segment at beginning of list */
      ge_seglist = ge_open[pos].next;
    }
    else {
      for (tmpseg=ge_seglist; (ge_open[tmpseg].next != pos);
           tmpseg=ge_open[tmpseg].next) ;
      ge_open[tmpseg].next = ge_open[pos].next;
    }

    /* free display list of segment */
    if ( ge_open[pos].d_list ) {

      /* make the display list a simple linked list */
      ptr=ge_open[pos].d_list->next;
      ge_open[pos].d_list->next = 0;

      /* free items along the list */
      for ( ; ptr; ptr=tmp ) {
        tmp=ptr->next;
        ge_free_item(ptr);
      }

    } /* if */

    /* add segment block to free list */
    ge_open[pos].next = ge_dsfree;
    ge_dsfree = pos; 

  } /* if */

} /* ge_delete_segment */

/*****************************************************************************

     ge_display_list will get the display list of a segment.

  Input
    segno - number of segment

  Output
    Return pointer to display list of segment, 0 if segment is not found.

*****************************************************************************/

struct list *ge_display_list(segno)
int segno;
{
  int pos;

  if ((pos=ge_index(segno)) >= 0) {
    return(&(ge_open[pos]));
  }
  else
    return(0);

} /* ge_display_list */
@//E*O*F display.c//
chmod u=r,g=r,o=r display.c
 
echo x - ps.c
sed 's/^@//' > "ps.c" <<'@//E*O*F ps.c//'

static char SccsId[] = "@(#)ps.c	1.2 6/8/87 Copyright 1987 SBCS-chan";

/*****************************************************************************

          (c) Copyright 1987 

          Lap-Tak Chan
          Computer Science Department
          State University of New York at Stony Brook
          Stony Brook, NY 11794

    This software and its documentation may be used, copied, and
  distributed, provided that this legend appear in all copies and
  that it is not copied or distributed for a profit.

  No representations is made about the suitability of this software
  for any purpose.  It is provided "as is" with no support and no
  express or implied warranty.

*****************************************************************************/

/*****************************************************************************

                        PostScript Interface
                        ====================

    This file contains the PostScript interface routines of graphedit
  which can write the display lists in PostScript and rebuild the display
  lists from the written PostScript.

*****************************************************************************/

#include "display.h"
#include <usercore.h>
#include <stdio.h>
#include <string.h>

#define DEGPERRAD 180./PI
#define PSHEADER "ps.head" /* name of the header file */
static FILE *writefile;
static char pshead[80];
struct list *ge_display_list();

/*****************************************************************************

    ps_init do the necessary initialization before writing segments to the
  output file.  It will copy the header file as the beginning of the output
  file, and define how the graphedit coordinate system will be put on
  8.5 x 11 paper according to save_attr.

*****************************************************************************/

ps_init()
{
  float sx, sy, tx, ty;
  FILE *header;
  char buf[512];
  extern struct saveattr save_attr;
  float width, height;

  /* copy header to output file */
  if ( ! (header=fopen(pshead, "r"))) {
    fprintf(writefile, "%% Header file %s should be included here\n",pshead);
  }
  else {

    /* copy the header file to the output file */
    while ( fgets(buf, 512, header) ) {
      fputs(buf, writefile);
    }
    /* close the header file */
    fclose(header);
  }

  if ( save_attr.rotate ) {

    /* rotate picture */
    width = save_attr.height;
    height = save_attr.width;

    /* determine the scale, height and width of picture */
    sx=save_attr.height*7.2/GE_WIN_X;
    sy=save_attr.width*7.2/GE_WIN_Y;
    switch ( save_attr.fix ) {
      case 1:
        sx=sy;
        width=GE_WIN_X/GE_WIN_Y*height;
        break;
      case 2:
        sy=sx;
        height=GE_WIN_Y/GE_WIN_X*width;
        break;
    } /* switch */

    /* determine the offset necessary */
    if ( save_attr.center ) {
      tx = (55./width-0.5)*GE_WIN_X;
      ty = (-42.5/height-0.5)*GE_WIN_Y;
    }
    else {
      tx = ((110.-save_attr.offb)/width-1.)*GE_WIN_X;
      ty = ((-save_attr.offa)/height-1.)*GE_WIN_Y;
    }

    fprintf(writefile, "90. rotate ");

  }

  else {
    width = save_attr.width;
    height = save_attr.height;

    /* determine the scale, height and width of the picture */
    sx=save_attr.width*7.2/GE_WIN_X;
    sy=save_attr.height*7.2/GE_WIN_Y;
    switch (save_attr.fix) {
      case 1:
        sy=sx;
        height=GE_WIN_Y/GE_WIN_X*width;
        break;
      case 2:
        sx=sy;
        width=GE_WIN_X/GE_WIN_Y*height;
        break;
    } /* switch */

    /* determine the offset necessary */
    if ( save_attr.center ) {
      tx = (42.5/width-0.5)*GE_WIN_X;
      ty = (55./height-0.5)*GE_WIN_Y;
    }
    else {
      tx = save_attr.offa/width*GE_WIN_X;
      ty = ((110.-save_attr.offb)/height-1.)*GE_WIN_Y;
    }

  }

  /* define the graphedit coordinate system */
  fprintf(writefile, "%f %f scale %f %f translate\n", sx, sy,
    tx,ty);

  /* header which will be used to find the real beginning of the
     saved segments */
  fprintf(writefile, "%% graphedit.save v%s\n", GE_VERSION);

} /* ps_init */

/*****************************************************************************

    ps_done just do whatever necessary after saving all these segments.

*****************************************************************************/

ps_done()
{
  FILE *tail;
  char buf[512];

  fprintf(writefile, "GE_TERM\n");

} /* ps_done */

/*****************************************************************************

    ps_setfont will generate the change of font in PostScript.

*****************************************************************************/

ps_setfont(font)
int font;
{

  fprintf(writefile, "%d GE_SET_FONT\n", font);

} /* ps_setfont */

/*****************************************************************************

    ps_setlinestyle will generate the change of line style in PostScript.

*****************************************************************************/

ps_setlinestyle(type)
int type;
{

  fprintf(writefile, "%d GE_SET_LINESTYLE\n", type);

} /* ps_setlinestyle */

/*****************************************************************************

    ps_draw_seg will draw a segment in PostScript according to its display
  list.

  Input
    list - display list of segment

*****************************************************************************/

ps_draw_seg(list)
struct list *list;
{
  struct list_item *ptr;
  static struct list_item closeinstr={GE_CLOSE, 0., 0., NULL};

  /* abort if the display list of the segment is empty */
  if ( !(ptr=list->d_list) )
    return;

  /* set the transformation of the segment */
  fprintf(writefile, "GE_NEW_SEG\n%f %f GE_TRANSLATE\n%f GE_ROTATE\n%f %f GE_SCALE\n",
          list->attr.tx, list->attr.ty, list->attr.ang * DEGPERRAD,
          list->attr.sx, list->attr.sy);

  /* set the attribtes of the segment */
  fprintf(writefile, "%d GE_SET_COLOR\n", list->attr.color);
  fprintf(writefile, "%d GE_SET_LINEWIDTH\n",
          list->attr.linewidth);
  fprintf(writefile, "%d GE_SET_CHARSIZE\n", list->attr.charsize);
  ps_setlinestyle(list->attr.linestyle);
  ps_setfont(list->attr.font);

  /* scan the display list and execute the instructions to draw the
     primitives */
  ptr=ptr->next;
  do {
    ps_exec(ptr);
    ptr=ptr->next;
  } while ( ptr != list->d_list->next );

  /* close the segment */
  ps_exec(&closeinstr);
  fprintf(writefile, "GE_END_SEG\n");

} /* ps_draw_seg */

/*****************************************************************************

    ps_exec will execute an instruction and draw it in PostScript.

  Input
    ptr - pointer to instruction

*****************************************************************************/

ps_exec(ptr)
struct list_item *ptr;
{
  static float xbuff[512], ybuff[512];
  static int lastinstr=0;
  static int n=0;
  int i;

  /* execute last polyline/polygon instruction if instruction indicates
     end of the last instruction */
  if ( lastinstr && ptr->instr != GE_CONT ) {
    switch ( lastinstr ) {

      case GE_POLYLINE :
        fprintf(writefile, "%.4f %.4f GE_POLYLINE\n", xbuff[0], ybuff[0]);
        for (i=1; i<n; i++) {
          fprintf(writefile, "%.4f %.4f GE_CONT\n", xbuff[i], ybuff[i]);
        }
        fprintf(writefile, "GE_END\n");
        break;

      case GE_POLYGON:
        fprintf(writefile, "%.4f %.4f GE_POLYGON\n", xbuff[0], ybuff[0]);
        for (i=1; i<n; i++) {
          fprintf(writefile, "%.4f %.4f GE_CONT\n", xbuff[i], ybuff[i]);
        }
        fprintf(writefile, "GE_END\n");
        break;

      default: ;
    }
    lastinstr = n = 0;
  }

  /* execute new instruction */
  switch (ptr->instr) {

    /* instructions with single coordinate */
    case GE_MOVE:
      fprintf(writefile, "%.4f %.4f GE_MOVE\n", ptr->x, ptr->y);
      break;

    case GE_LINE:
      fprintf(writefile, "%.4f %.4f GE_LINE\n",  ptr->x, ptr->y);
      break;

    case GE_ELLIPSE:
      fprintf(writefile, "%.4f %.4f GE_ELLIPSE\n",  ptr->x, ptr->y);
      break;

    case GE_ARC:
      fprintf(writefile, "%.4f %.4f GE_ARC\n",  ptr->x, ptr->y);
      break;

    case GE_ARCEXT:
      fprintf(writefile, "%.4f GE_ARCEXT\n",  ptr->x);
      break;

    case GE_CIRCLE:
      fprintf(writefile, "%.4f GE_CIRCLE\n", ptr->x);
      break;

    /* instruction with two or more coordinates */
    case GE_POLYLINE:
    case GE_POLYGON:
      /* store instruction */
      lastinstr=ptr->instr;

    case GE_CONT:
      /* save coordinate of polyline/polygon */
      xbuff[n] = ptr->x;
      ybuff[n++] = ptr->y;
      break;

    case GE_TEXT:
      fprintf(writefile, "(%s) GE_TEXT\n", (char *)((int)ptr->x));
      break;

    case GE_SETCOLOR:
      fprintf(writefile, "%d GE_SET_COLOR\n", (int)ptr->x);
      break;

    case GE_SETTYPELINE:
      fprintf(writefile, "%d GE_SET_LINESTYLE\n", (int)ptr->x);
      break;

    case GE_SETLINEWIDTH:
      fprintf(writefile, "%d GE_SET_LINEWIDTH\n", (int)ptr->x);
      break;

    case GE_SETFONT:
      fprintf(writefile, "%d GE_SET_FONT\n", (int)ptr->x);
      break;

    case GE_SETCHARSIZE:
      fprintf(writefile, "%d GE_SET_CHARSIZE\n", (int)ptr->x);
      break;

    case GE_FILL:
      fprintf(writefile, "%d GE_FILL\n", (int)ptr->x);
      break;

    case GE_CLOSE:
      /* do nothing */
      break;

    default :
      /* check that all instructions are properly handled */
      fprintf(stderr,
              "Internal Error-Action for instruction %d missing.\n",
              ptr->instr);
       break;

  } /* switch */

} /* ps_exec */

/*****************************************************************************

    ps_write will write the picture as a complete PostScrip program in
  the already opened outfile.  It will copy a header file to outfile,
  scan the segment list and write every segment (except the sample segments)
  in PostScript.

    The directory where the header file is located will be defined by the
  environment GELIB.  If GELIB is not defined, GE_DEFAULTLIB will be used.

  Input
    outfile - opened output stream to write on

*****************************************************************************/

ps_write(outfile)
FILE *outfile;
{
  extern int ge_seglist;
  extern struct list ge_open[];
  int i;
  char *libdir;

  /* save it so that ps_exec will be able to access it */
  writefile=outfile;

  /* generate the name for the header file */
  if ( !(libdir = (char *)getenv("GELIB")) )
    libdir=GE_DEFAULTLIB;
  (void)strcpy(pshead,libdir);
  (void)strcat(pshead,"/");
  (void)strcat(pshead,PSHEADER);

  /* initialize */
  ps_init();

  /* scan the segment list and write every user segment */
  for ( i=ge_seglist; i; i=ge_open[i].next ) {
    if ( ge_open[i].segno >= GE_MIN_SEG && (! ge_open[i].attr.deleted) ) {
      ps_draw_seg(&(ge_open[i]));
    } /* if */
  } /* for */

  ps_done();

} /* ps_write */

/*****************************************************************************

    ps_write_seg will write one segment in PostScript to outfile.  The
  output file is not a complete PostScrip program and thus cannot be printed.

  Input
    outfile - opened output stream to write on
    segno - segment to write

*****************************************************************************/

ps_write_seg(outfile, segno)
FILE *outfile;
int segno;
{
  struct list *list;

  writefile=outfile;

  /* get display list of segment */
  list=ge_display_list(segno);

  /* write header */
  fprintf(writefile, "%% graphedit.save v%s\n", GE_VERSION);

  /* write segment in PostScript */
  ps_draw_seg(list);

} /* ps_write_seg */

/*****************************************************************************

    ps_load will rebuild the display list of the segments written in infile.
  It will scan the file for the mark of the beginning of data.  Then it
  will read and process each line until the mark for the end of data is
  encountered.

  Input
    infile - opened input stread to read from

*****************************************************************************/

ps_load(infile)
FILE *infile;
{
  char inbuf[512];

  /* skip header until mark of  beginning of data */
  for (; (fgets(inbuf,512,infile)) && 
         (strncmp(inbuf, "% graphedit.save",16)););

  if ( inbuf[0] != '%' ) {
    /* abort if header cannot be found */
    return(1);
  }

  /* read and process each line in infile until the instruction GE_TERM
     marking the end of the file is encountered */
  while ( (fgets(inbuf,512,infile) &&
          (strcmp(inbuf,"GE_TERM")) )) {
    /* process line */
    ps_read(inbuf);
  } /* while */

  return(0);

} /* ps_load */

/*****************************************************************************

    ps_cmd2instr will return the instruction for a command in the PostScript
  input.

  Input
    command - command in PostScript

  Output
    Return internal instruction corresponding to the command.

*****************************************************************************/

ps_cmd2instr(command)
char *command;
{
  struct cmdinstr {
    char *cmd;
    int instr;
  } ;

  /* a map of command to instruction */
  static struct cmdinstr cmdinstr[] = {
    { "GE_SET_FONT", GE_SETFONT },
    { "GE_SET_LINESTYLE", GE_SETTYPELINE },
    { "GE_SET_COLOR", GE_SETCOLOR },
    { "GE_SET_LINEWIDTH", GE_SETLINEWIDTH},
    { "GE_NEW_SEG", GE_NEW },
    { "GE_END_SEG", GE_CLOSE },
    { "GE_TRANSLATE", GE_TRANSLATE },
    { "GE_ROTATE", GE_ROTATE },
    { "GE_SCALE", GE_SCALE },
    { "GE_MOVE", GE_MOVE },
    { "GE_LINE", GE_LINE },
    { "GE_POLYLINE", GE_POLYLINE },
    { "GE_POLYGON", GE_POLYGON },
    { "GE_TEXT", GE_TEXT },
    { "GE_CIRCLE", GE_CIRCLE },
    { "GE_ELLIPSE", GE_ELLIPSE },
    { "GE_CONT", GE_CONT },
    { "GE_SET_CHARSIZE", GE_SETCHARSIZE },
    { "GE_FILL", GE_FILL },
    { "GE_ARC", GE_ARC },
    { "GE_ARCEXT", GE_ARCEXT},
    { "", 0 }
  };

  int i;

  /* scan for the location of the command in the map */
  for ( i=0; cmdinstr[i].instr && strcmp(cmdinstr[i].cmd, command); i++ );

  /* return instruction for command */
  return(cmdinstr[i].instr);

} /* ps_cmd2instr */

/*****************************************************************************

    ps_read will process a command in a line of input.  Each command may
  be composed of 1 to 3 fields.  The last field corresponds to an attribute
  or instruction while the others are parameters to the commands.

  Input
    inbuf - input buffer containing a line of input from the input file

*****************************************************************************/

ps_read(inbuf)
char *inbuf;
{
  static int ps_segno;
  static struct list *seginfo;
  char buf1[80], buf2[80], buf3[80], *command;
  int fields, instr, inexec;
  float x,y;

  /* handle GE_TEXT */
  if ( inbuf[0] == '(' ) {

    char *ptr;

    /* get the end of the text string */
    if ( ptr=(char *)rindex(inbuf,')') ) {

      *ptr='\0';
      strcpy((ptr=(char *)malloc(strlen(inbuf))),&(inbuf[1]));
      ge_graph(ps_segno, GE_TEXT, (float)((int)ptr),0.);

    }
    return;
  }

  /* separate the command into up to three fields */
  if ( !(fields=sscanf(inbuf, "%s%s%s", buf1, buf2, buf3)) ) {
    return;
  }

  /* command is the last field */
  switch ( fields ) {
    case 1: command=buf1;
            break;
    case 2: command=buf2;
            break;
    case 3: command=buf3;
            break;
    default: break;
  } /* switch */

  /* get instruction corresponding to command */
  if ( instr=ps_cmd2instr(command) ) {

    switch ( instr ) {
      /* instructions with two parameters */
      case GE_MOVE:
      case GE_LINE:
      case GE_POLYLINE:
      case GE_POLYGON:
      case GE_CONT:
      case GE_ELLIPSE:
      case GE_ARC:
        sscanf(buf1,"%f",&x); sscanf(buf2,"%f",&y);
        ge_graph(ps_segno, instr, x, y);
        inexec=1;
        break;

      /* instructions with one parameter */
      case GE_CIRCLE:
      case GE_ARCEXT:
      case GE_FILL:
        sscanf(buf1, "%f",&x);
        ge_graph(ps_segno, instr, x, 0.);
        inexec=1;
        break;

      /* initial attributes or change of attributes */
      case GE_SETCOLOR:
      case GE_SETTYPELINE:
      case GE_SETLINEWIDTH:
      case GE_SETFONT:
      case GE_SETCHARSIZE:
        sscanf(buf1,"%f",&x);

        if ( ! inexec )
          /* set initial attribues */
          switch (instr) {
            case GE_SETCOLOR:
              seginfo->attr.color = (int)x;
              break;
            case GE_SETTYPELINE:
              seginfo->attr.linestyle = (int)x;
              break;
            case GE_SETLINEWIDTH:
              seginfo->attr.linewidth = (int)x;
              break;
            case GE_SETFONT:
              seginfo->attr.font = (int) x;
              break;
            case GE_SETCHARSIZE:
              seginfo->attr.charsize = (int) x;
          }

        else {
          /* change attribute */
          ge_graph(ps_segno, instr, x, 0.);
        }
        break;

      /* trnasformations */
      case GE_TRANSLATE:
        sscanf(buf1,"%f",&(seginfo->attr.tx));
        sscanf(buf2,"%f",&(seginfo->attr.ty));
        break;
      case GE_ROTATE:
        sscanf(buf1,"%f",&(seginfo->attr.ang));
        seginfo->attr.ang /= DEGPERRAD;
        break;
      case GE_SCALE:
        sscanf(buf1,"%f",&(seginfo->attr.sx));
        sscanf(buf2,"%f",&(seginfo->attr.sy));
        break;

      /* start new segment */
      case GE_NEW:
        /* create new segment */
        ps_segno=make_new_seg(0);
        seginfo=(struct list *)ge_display_list(ps_segno);
        inexec=0;
        break;

      /* close segment */
      case GE_CLOSE:
        seginfo=0;
        /* draw segment */
        ge_draw(ps_segno);
        break;

      default:
        /* check that all instructions are properly handled */
        fprintf(stderr, "Internal Error-Action for instruction %d missing.\n",
          instr);
        break;

    } /* switch */

  } /* if */

} /* ps_read */

@//E*O*F ps.c//
chmod u=r,g=r,o=r ps.c
 
echo x - pic.c
sed 's/^@//' > "pic.c" <<'@//E*O*F pic.c//'

static char SccsId[] = "@(#)pic.c	1.1 6/8/87 Copyright 1987 SBCS-chan";

/*****************************************************************************

          (c) Copyright 1987 

          Lap-Tak Chan
          Computer Science Department
          State University of New York at Stony Brook
          Stony Brook, NY 11794

    This software and its documentation may be used, copied, and
  distributed, provided that this legend appear in all copies and
  that it is not copied or distributed for a profit.

  No representations is made about the suitability of this software
  for any purpose.  It is provided "as is" with no support and no
  express or implied warranty.

*****************************************************************************/

/*****************************************************************************

                          Pic Output
                          ==========

    This file contains routines which write the diagram in format suitable
  as input to pic(1), which is a picture drawing preprocessor to troff.

*****************************************************************************/

#include "display.h"
#include <stdio.h>
#include <string.h>
#include <math.h>

#define DEGPERRAD 180./PI
static FILE *writefile; /* file to write diagram to */
struct list *ge_display_list();
static struct attr pic_attr; /* current attributes used for current segment */
static float sina, cosa;
static float pic_rms; /* root mean square of x and y scale of current segment */

/* mapping of graphedit fonts to troff fonts */
static char *pic_font[]=
  { "R", "R", "S", "I", "B" };

/*****************************************************************************

    pic_init will do the necessary initialization for the writing.

*****************************************************************************/

pic_init()
{
  float sx, sy;
  FILE *header;
  char buf[512];
  extern struct saveattr save_attr;

  /* indicate beginning of pic input */
  fprintf(writefile, ".PS %.2f\n", save_attr.width/10.);

  /* to make pic using full graphedit coordinates */
  fprintf(writefile, "move to (%.4f,%.4f)\n", GE_WIN_X, GE_WIN_Y);

} /* pic_init */

/*****************************************************************************

    pic_done do anything necessary to clean up after the writing.

*****************************************************************************/

pic_done()
{
  FILE *tail;
  char buf[512];

  /* indicate end of pic input */
  fprintf(writefile, ".PE\n");

} /* pic_done */

/*****************************************************************************

    pic_xform transforms a coordinate of the current segment to the world
  coordiante.

  Input
    ix, iy - coordinate in space of current segment

  Output
    ox, oy - corresponding world coordinate of (ix,iy) according to
             transformatio of current segment.

*****************************************************************************/

pic_xform(ix, iy, ox, oy)
float ix, iy, *ox, *oy;
{
  *ox = cosa * ix * pic_attr.sx - sina * iy * pic_attr.sy + pic_attr.tx;
  *oy = sina * ix * pic_attr.sx + cosa * iy * pic_attr.sy + pic_attr.ty;

} /* pic_xform */

/*****************************************************************************

     pic_charsize set a new character size in pic.  The actual character
  size is determined from the character size of the current segment, the
  width of the picture, and the scale of the current segment.

*****************************************************************************/

pic_charsize()
{
  float charsize;

  charsize = pic_attr.charsize * pic_rms * save_attr.width / 42.;
  return((int)charsize);

} /* pic_charsize */

/*****************************************************************************

    pic_linestyle set a new line style according to the line style of the
  current segment.

*****************************************************************************/

pic_linestyle()
{
  /* mapping of graphedit line style to pic line style */
  static char *pic_style[] =
    { 0, "dotted 3", "dashed 6", "dashed 2" };

  if ( pic_style[pic_attr.linestyle] )
    fprintf(writefile, " %s", pic_style[pic_attr.linestyle]);

} /* pic_linestyle */

/*****************************************************************************

    pic_linewidth determine the line width in pic according to the line
  width of the current segment.

  Output
    Return the line width determined.

*****************************************************************************/

pic_linewidth()
{
  /* this formula may need adjustment */
  return ((pic_attr.linewidth)*12+2);

} /* pic_linewidth */

/* set the line width in pic */
#define pic_set_linewidth() fprintf(writefile,".ps %d\n",\
   pic_linewidth())

/*****************************************************************************

    pic_sim_ellipse will simulate the drawing of an ellipse by dividing
  the ellipse into a number of segments and draw each segment with an
  arc.  This is necessary because pic cannot draw rotated ellipse.

    Coordinates of points on the ellipse are determined by transformation
  of points on a circle at (0,0) with radius 1.

  Input
    currx, curry - current world cordinate
    a,b - half of width and height of ellipse

*****************************************************************************/

pic_sim_ellipse(currx, curry, a, b)
float currx, curry, a, b;
{

#define PLT_CIRINT 20 /* number of segments to divide into */
#define PLT_CIRSIZE PLT_CIRINT*2+1 /* size of array necessary */

  static int pic_mdl_make = 0;
  static float pic_cir_x[PLT_CIRSIZE];
  static float pic_cir_y[PLT_CIRSIZE];
  extern float ge_radius(); /* from misc.c */
  float radius; /* radius of arc */
  float x1, y1, x2, y2, x3, y3; /* start, mid and end points of arc */
  int i;

  /* put the coordinates of points on a circle with center at (0,0)
     and radius 1 in pic_cir_x and pic_cir_y at the first time */
  if ( ! pic_mdl_make ) {
    int count;
    float intv;

    intv = PI / PLT_CIRINT;
    for (count=0; count < PLT_CIRSIZE; count ++ ) {
      pic_cir_x[count]=cos(intv*count);
      pic_cir_y[count]=sin(intv*count);
    } /* for */
    pic_mdl_make = 1;
  } /* if */

  /* determine the world coordinate of the first point and move there */
  pic_xform(pic_cir_x[0]*a, pic_cir_y[0]*b, &x3, &y3);
  fprintf(writefile, "move to  (%.4f,%.4f)\n",
          x3+currx-pic_attr.tx, y3+curry-pic_attr.ty);

  /* draw the arcs */
  for ( i=0; i<PLT_CIRSIZE-1; i+=2 ) {

    /* make the end point of the last arc the start point */
    x1=x3; y1=y3;

    /* determine the world coordinate of the mid point and the end point */
    pic_xform(pic_cir_x[i+1]*a,pic_cir_y[i+1]*b, &x2, &y2);
    pic_xform(pic_cir_x[i+2]*a,pic_cir_y[i+2]*b, &x3, &y3);

    /* determine the radius of the arc passing the start, mid and end points */
    radius = ge_radius(x1,y1,x2,y2,x3,y3);

     /* draw the arc */
    fprintf(writefile, "arc to (%.4f,%.4f) radius %.4f\n",
            x3+currx-pic_attr.tx, y3+curry-pic_attr.ty, radius);

  } /* draw ellipse */

} /* pic_sim_ellipse */

/*****************************************************************************

    pic_sim_arc will simulate an arc which is not a real arc after
  transformation of the current segment with a number of small arcs.

  Input
    currx, curry - center of arc in world coordinate
    ang1 - start angle in radian, 0 <= ang1 <= 2*PI
    ang2 - end angle in radian, 0 <= ang2 < = 2*PI
    arcradius - radius of arc

*****************************************************************************/

pic_sim_arc(currx, curry, ang1, ang2, arcradius)
float currx, curry, ang1, ang2, arcradius;
{
  /* draw arc from smalll arcs */
  float intv, radius;
  extern float ge_radius();
  int count;
  float x1,y1,x2,y2,x3,y3,ang;

  /* ensure ang1 < ang2 */
  if ( ang1 > ang2 )
    ang1 -= 2*PI;

  /* determine number of segmnets to divide into with a minimum of 3 */
  intv = PI / PLT_CIRINT;
  count = (int)((ang2-ang1)/(2*intv));
  if ( count < 3 ) count = 3;

  intv = (ang2-ang1)/(count*2);

  /* determine the world coordinate of the first point and move there */
  pic_xform(cos(ang1)*radius,sin(ang1)*arcradius,&x3,&y3);
  fprintf(writefile, "move to (%.4f,%.4f)\n",
    x3+currx-pic_attr.tx,y3+curry-pic_attr.ty);

  /* draw the arcs */
  for ( ang = ang1; ang< (ang2-2*intv); ang += 2*intv) {

    /* make the end point of the last arc the start point */
    x1=x3; y1=y3;

    /* determine the world coordinate of the mid point and the end point */
    pic_xform(cos(ang+intv)*arcradius,
              sin(ang+intv)*arcradius, &x2, &y2);
    pic_xform(cos(ang+2*intv)*arcradius,
              sin(ang+2*intv)*arcradius, &x3, &y3);

    /* determine the radius of the arc passing the start, mid and end point */
    radius=ge_radius(x1,y1,x2,y2,x3,y3);

    /* draw the arc */
    fprintf(writefile, "arc to (%.4f,%.4f) radius %.4f\n",
      x3+currx-pic_attr.tx, y3+curry-pic_attr.ty, radius);

  } /* for */

}  /* draw arcs */

/*****************************************************************************

    plt_exec will execute an instruction.

  Input
    ptr - pointer to instruction

*****************************************************************************/

pic_exec(ptr)
struct list_item *ptr;
{
  static float xbuff[512], ybuff[512];
  static int lastinstr=0;
  static int n=0; /* number of points in polygon/polyline */
  static float currx, curry; /* current point */
  int i;

  /* execute last polyline/polygon instruction if instruction indicates
     end of the last instruction */
  if ( lastinstr && ptr->instr != GE_CONT ) {
    switch ( lastinstr ) {
      case GE_POLYLINE :
        /* draw polyline */
        for (i=0; i<n; i++) {
          fprintf(writefile, "line to (%.4f,%.4f)", xbuff[i], ybuff[i]);
          pic_linestyle(); /* specify line style */
          fprintf(writefile, "\n");
        } /* for */

        /* move to new current point */
        currx = xbuff[n-1];
        curry = ybuff[n-1];
        break;

      case GE_POLYGON:
        /* draw polygon */
        fprintf(writefile, "move to (%.4f,%.4f)\n", xbuff[0], ybuff[0]);
        for (i=1; i<n; i++) {
          fprintf(writefile, "line to (%.4f,%.4f)", xbuff[i], ybuff[i]);
          pic_linestyle(); /* specify line style */
          fprintf(writefile, "\n");
        } /* for */
        /* draw last line */
        fprintf(writefile, "line to (%.4f,%.4f)", xbuff[0], ybuff[0]);
        pic_linestyle();
        fprintf(writefile, "\n");

        /* update current position */
        currx = xbuff[0];
        curry = ybuff[0];
        break;
      default: ;
    }
    lastinstr = n = 0;
  }

  /* execute new instruction */
  switch (ptr->instr) {

    /* instructions with single coordinate */
    case GE_MOVE:
      /* move */
      pic_xform(ptr->x, ptr->y, &currx, &curry);
      fprintf(writefile, "move to (%.4f,%.4f)\n", currx, curry);
      break;

    case GE_LINE:
      /* line */
      pic_xform(ptr->x, ptr->y, &currx, &curry);
      fprintf(writefile, "line to (%.4f,%.4f)", currx, curry);
      pic_linestyle(); /* specify line style */
      fprintf(writefile, "\n");
      break;

    case GE_CIRCLE:
      {
        float radius;

        if ( pic_attr.sx == pic_attr.sy ) {
          /* draw a circle if it is a circle after transformation */
          fprintf(writefile, "circle radius %.4f at (%.4f,%.4f)\n",
            ptr->x*pic_attr.sx, currx, curry);
        }
        else {
          if ( pic_attr.ang == 0. ) {
            /* draw ellipse if rotation is 0 */
            fprintf(writefile, "ellipse at (%.4f, %.4f) ht %.4f wid %.4f\n",
              currx, curry, ptr->x*pic_attr.sy*2, ptr->x*pic_attr.sx*2);
          }
          else {
            /* simulate the ellipse */
            pic_sim_ellipse(currx,curry,ptr->x,ptr->x);
          }
        }
      }
      break;

    case GE_ELLIPSE:
      {
        float radius;

        if ( pic_attr.ang == 0. ) {
          /* draw ellipse if there is no rotation */
          fprintf(writefile, "ellipse at (%.4f, %.4f) ht %.4f wid %.4f\n",
            currx, curry, ptr->y*pic_attr.sy*2, ptr->x*pic_attr.sx*2);
        }
        else {
          /* simulate the ellipse */
          pic_sim_ellipse(currx,curry,ptr->x,ptr->y);
        }
      }
      break;

    case GE_ARC:
    case GE_ARCEXT:
      {
        static float ang1, ang2;

        if ( ptr->instr == GE_ARC ) {
          ang1 = ptr->x;
          ang2 = ptr->y;
        }
        else {
          if ( pic_attr.sx == pic_attr.sy ) {
            /* draw arc */
            float x1,y1,x2,y2;

            pic_xform(cos(ang1)*ptr->x,sin(ang1)*ptr->x,&x1,&y1);
            pic_xform(cos(ang2)*ptr->x,sin(ang2)*ptr->x,&x2,&y2);
            fprintf(writefile, "arc from (%.4f,%.4f) to (%.4f,%.4f) radius %.4f\n",
                    x1+currx-pic_attr.tx,y1+curry-pic_attr.ty,
                    x2+currx-pic_attr.tx,y2+curry-pic_attr.ty,
                    ptr->x*pic_attr.sx);
          }
          else {
            /* simulate the arc */
            pic_sim_arc(currx,curry,ang1,ang2,ptr->x);
          }
        }
      } /* GE_ARC, GE_ARCEXT */
      break;
            
    /* instruction with two or more coordinates */
    case GE_POLYLINE:
    case GE_POLYGON:
      lastinstr=ptr->instr; /* store instruction */

    case GE_CONT:
      /* save coordinate of polyline/polygon */
      pic_xform(ptr->x, ptr->y, &xbuff[n], &ybuff[n]);
      n++;
      break;

    case GE_TEXT:
      /* draw text */
      fprintf(writefile, ".ft %s\n.ps %d\n\"%s\" ljust at (%.4f,%.4f)\n.ps %d\n",
        pic_font[pic_attr.font],
        pic_charsize(),
        (char *)((int)ptr->x),
        currx, curry,
        pic_linewidth());
      break;

    case GE_SETTYPELINE:
      /* update line style */
      pic_attr.linestyle = (int)ptr->x;
      break;

    case GE_SETLINEWIDTH:
      /* update line width */
      pic_attr.linewidth = (int)ptr->x;
      pic_set_linewidth();
      break;

    case GE_SETFONT:
      /* update font */
      pic_attr.font = (int)ptr->x;
      break;

    case GE_SETCHARSIZE:
      /* update character size */
      pic_attr.charsize = (int)ptr->x;
      break;

    /* ignore these instructions */
    case GE_SETCOLOR:
    case GE_FILL:
    case GE_CLOSE:
      break;

    /* check whether all insturctions are properly handled */
    default :
      fprintf(stderr,
              "Internal Error-Action for instruction %d missing.\n",
              ptr->instr);
       break;

  } /* switch */

} /* pic_exec */

/*****************************************************************************

    pic_draw_seg will draw a segment according to its display list.  It
  just setup the attributes, and scan and execute the instructions on the
  instruction list.

  Input
    list - display list of segment

*****************************************************************************/

pic_draw_seg(list)
struct list *list;
{
  struct list_item *ptr;
  /* instruction to indicate end of segment */
  static struct list_item closeinstr={GE_CLOSE, 0., 0., NULL};

  /* ignore segment if the instruction list is empty */
  if ( !(ptr=list->d_list) )
    return;

  /* setup the initial attributes */
  pic_attr=list->attr;
  pic_rms = sqrt((double)((pic_attr.sx*pic_attr.sx+
                           pic_attr.sy*pic_attr.sy)/2));
  sina = (double)sin((double)pic_attr.ang);
  cosa = (double)cos((double)pic_attr.ang);

  /* initialize the line width */
  pic_set_linewidth();

  /* point at beginning of instruction list */
  ptr=ptr->next;

  /* scan instruction list */
  do {
    /* execution instruction */
    pic_exec(ptr);
    ptr=ptr->next;

  } while ( ptr != list->d_list->next );

  /* end of segment */
  pic_exec(&closeinstr);

} /* pic_draw_seg */

/*****************************************************************************

    pic_write will write the diagram in pic.  It scans the segment list and
  write every user segment not deleted.

  Input
    outfile - opened output stream

*****************************************************************************/

pic_write(outfile)
FILE *outfile;
{
  extern int ge_seglist;
  extern struct list ge_open[];
  int i;

  writefile=outfile;

  /* initialize pic */
  pic_init();

  /* scan the segment list */
  for ( i=ge_seglist; i; i=ge_open[i].next ) {

    /* write undeleted user segment */
    if ( ge_open[i].segno >= GE_MIN_SEG && (! ge_open[i].attr.deleted) ) {
      pic_draw_seg(&(ge_open[i]));
    } /* if */
  } /* for */

  /* terminate pic */
  pic_done();
 
} /* pic_write */
@//E*O*F pic.c//
chmod u=r,g=r,o=r pic.c
 
echo x - plot.c
sed 's/^@//' > "plot.c" <<'@//E*O*F plot.c//'

static char SccsId[] = "@(#)plot.c	1.1 6/8/87 Copyright 1987 SBCS-achan";

/*****************************************************************************

          (c) Copyright 1987 

          Lap-Tak Chan
          Computer Science Department
          State University of New York at Stony Brook
          Stony Brook, NY 11794

    This software and its documentation may be used, copied, and
  distributed, provided that this legend appear in all copies and
  that it is not copied or distributed for a profit.

  No representations is made about the suitability of this software
  for any purpose.  It is provided "as is" with no support and no
  express or implied warranty.

*****************************************************************************/

/*****************************************************************************

                        Plot Output
                        ===========

    This file contains routines which write the diagram in plot(5) format.
  The output file is actually written by routines in the plot(3) library.

  Filters for many graphic display terminal, laser printers, plotters
  and even regular terminals are available for files in plot(5) format.

*****************************************************************************/

#include "display.h"
#include <stdio.h>
#include <math.h>

/* scale used to maintain significance of coordinates when floating
   point coordinates are convert to integer coordinates */
#define SCALE 10

static double sina, cosa; /* sin and cos of rotation of current segment */

/* mapping of graphedit line styles to plot line styles */
static char *plt_linestyle[] =
  {  "solid", "dotted", "shortdashed", "dotdashed" };

static struct attr curr_attr; /* attributes of current segment */

/* from display.c */
extern int ge_seglist; /* segment list */
extern struct list ge_open[]; /* display list of segments */

/*****************************************************************************

    plt_write will write the diagram with routines in plot(3).  It
  scans the segment list and write every user segment not deleted.

*****************************************************************************/

plt_write()
{
  int i;

  /* initialize plot and define the coordiante space */
  openpl();
  space(0,0,(int)GE_WIN_X*SCALE, (int)GE_WIN_Y*SCALE);

  /* scan the segment list */
  for ( i=ge_seglist; i; i=ge_open[i].next ) {

    /* write undeleted user segment */
    if ( ge_open[i].segno >= GE_MIN_SEG && (! ge_open[i].attr.deleted) ) {
      plt_draw_seg(&(ge_open[i]));
    } /* if */

  } /* for */

  /* close plot */
  closepl();

} /* plt_write */

/*****************************************************************************

    plt_draw_seg will draw a segment according to its display list.  It
  just setup the attributes, and scan and execute the instructions on the
  instruction list.

  Input
    list - display list of segment

*****************************************************************************/

plt_draw_seg(list)
struct list *list;       
{
  struct list_item *ptr;
  /* instruction to indicate end of segment */
  static struct list_item closeinstr={GE_CLOSE, 0., 0., NULL};

  /* ignore segment if the instruction list is empty */
  if ( !(ptr=list->d_list) )
    return;

  /* setup the initial attributes */
  curr_attr = list->attr;

  /* initialize the line style */
  linemod(plt_linestyle[curr_attr.linestyle]);

  sina = (double)sin((double)curr_attr.ang);
  cosa = (double)cos((double)curr_attr.ang);

  /* point to beginning of instruction list */
  ptr=ptr->next;

  /* scan instruction list */
  do {
     /* execution instruction */
     plt_exec(ptr);
     ptr=ptr->next;

  } while ( ptr !=list->d_list->next );

  /* end of segment */
  plt_exec(&closeinstr);

} /* plt_draw_seg */

/*****************************************************************************

    plt_xform will transform a coordinate of the current segment to world
  coordinate and adjust its significance.

  Input
    fx, fy - coordinate in coordinate system of current segment

  Output
    ix, iy - world coordinate

*****************************************************************************/

plt_xform(fx,fy,ix,iy)
float fx, fy;
int *ix, *iy;
{
  float x, y;

  /* transform coordinate */
  x = cosa * fx * curr_attr.sx - sina * fy * curr_attr.sy + curr_attr.tx;
  y = sina * fx * curr_attr.sx + cosa * fy * curr_attr.sy + curr_attr.ty;

  /* adjust significance */
  *ix = (int)(x * SCALE + 0.5);
  *iy = (int)(y * SCALE + 0.5);

} /* plt_xform */

/*****************************************************************************

    plt_sim_ellipse will simulate the drawing of an ellipse by dividing
  the ellipse into a number of segments and draw each segment with an
  arc.  This is necessary because plot(3) cannot draw ellipse.

    Coordinates of points on the ellipse are determined by transformation
  of points on a circle at (0,0) with radius 1.

  Input
    xcur, ycur - current world coordinate
    a,b - half of width and height of ellipse

*****************************************************************************/

plt_sim_ellipse(xcur, ycur, a, b)
int xcur, ycur;
float a, b;
{

#define PLT_CIRINT 20 /* number of segments to divide into */
#define PLT_CIRSIZE PLT_CIRINT*2+1 /* size of array necessary */

  static int plt_mdl_make = 0;
  static float plt_cir_x[PLT_CIRSIZE];
  static float plt_cir_y[PLT_CIRSIZE];
  extern float ge_radius2(); /* from misc.c */
  float radius; /* radius of arc */
  int x1, y1, x2, y2, x3, y3;
  float x,y;
  int i;

  /* put the coordinates of points on a circle with center at (0,0)
     and radius 1 in plt_cir_x and plt_cir_y at the first time */
  if ( ! plt_mdl_make ) {
    int count;
    float intv;

    intv = PI / PLT_CIRINT;
    for (count=0; count < PLT_CIRSIZE; count ++ ) {
      plt_cir_x[count]=cos(intv*count);
      plt_cir_y[count]=sin(intv*count);
    }
    plt_mdl_make = 1;

  } /* if */

  /* determine the world coordinate of the first point */
  plt_xform(plt_cir_x[0]*a, plt_cir_y[0]*b, &x3, &y3);

  /* draw the arcs */
  for ( i=0; i<PLT_CIRSIZE-1; i+=2 ) {

    /* make the end point of the last arc the start point */
    x1=x3; y1=y3;

    /* determine the world coordinate of the mid point and the end point */
    plt_xform(plt_cir_x[i+1]*a,plt_cir_y[i+1]*b, &x2, &y2);
    plt_xform(plt_cir_x[i+2]*a,plt_cir_y[i+2]*b, &x3, &y3);

    /* determine the center of the arc passing the start point, midpoint and
       end point */
    (void) ge_radius2((float)x1,(float)y1,(float)x2,(float)y2,
                      (float)x3,(float)y3,&x,&y);

    /* draw the arc */
    arc((int)(x+xcur-curr_attr.tx*SCALE),(int)(y+ycur-curr_attr.ty*SCALE),
        (int)(x1+xcur-curr_attr.tx*SCALE),(int)(y1+ycur-curr_attr.ty*SCALE),
        (int)(x3+xcur-curr_attr.tx*SCALE),(int)(y3+ycur-curr_attr.ty*SCALE));

  } /* for */

} /* plt_sim_ellipse */

/*****************************************************************************

    plt_sim_arc will simulate an arc which is not a real arc after
  transformation of the current segment with a number of small arcs.

  Input
    cx, cy - center of arc in world coordinate
    ang1 - start angle in radian, 0 <= ang1 <= 2*PI
    ang2 - end angle in radian, 0 <= ang2 <= 2*PI
    arcradius - radius of arc 

*****************************************************************************/

plt_sim_arc(cx,cy,ang1,ang2,arcradius)
int cx,cy;
float ang1,ang2,arcradius;
{
  float intv, radius;
  extern float ge_radius2();
  int count;
  int x1,y1,x2,y2,x3,y3; /* start, mid and end point of small arc */
  float ang,x,y;

  intv = PI / PLT_CIRINT;

  /* ensure ang1 is less than ang2 */
  if ( ang1 > ang2 )
  ang1 -= 2*PI;

  /* determine how many small arcs it will be divided into,
     the minimum is 3 */
  count = (int)((ang2-ang1)/(2*intv));
  if ( count < 3 ) count = 3;

  intv = (ang2-ang1)/(count*2);

  /* transform the first point */
  plt_xform(cos(ang1)*arcradius,sin(ang1)*arcradius,&x3,&y3);

  /* draw the arcs */
  for ( ang = ang1; ang< (ang2-2*intv); ang += 2*intv) {

    /* make end point of last arc the start point */
    x1=x3; y1=y3;

    /* transform mid point */
    plt_xform(cos(ang+intv)*arcradius,
              sin(ang+intv)*arcradius, &x2, &y2);

    /* transform end point */
    plt_xform(cos(ang+2*intv)*arcradius,
              sin(ang+2*intv)*arcradius, &x3, &y3);

    /* determine radius of arc */
    (void)ge_radius2((float)x1,(float)y1,(float)x2,(float)y2,
                     (float)x3,(float)y3,&x,&y);

    /* draw small arc */
    arc((int)(x+cx-curr_attr.tx*SCALE+0.5),
        (int)(y+cy-curr_attr.ty*SCALE+0.5),
        (int)(x1+cx-curr_attr.tx*SCALE),
        (int)(y1+cy-curr_attr.ty*SCALE),
        (int)(x3+cx-curr_attr.tx*SCALE),
        (int)(y3+cy-curr_attr.ty*SCALE));

  } /* for */

}  /* plt_sim_arc */

/*****************************************************************************

    plt_exec will execute an instruction.  It call routines in plot(3) to
  actually do the drawing.

  Input
    ptr - pointer to instruction

*****************************************************************************/

plt_exec(ptr)
struct list_item *ptr;
{
    static int xbuff[512], ybuff[512];
    static int lastinstr=0;
    static int n=0; /* number of points in polygon/polyline */
    static int xcur, ycur; /* current point */
    int i;

 /* check whether the list of points for polygon/polyline end */
 if ( lastinstr && ptr->instr !=GE_CONT ) {
   switch ( lastinstr ) {
     case GE_POLYLINE:
       /* draw polyline */
       move (xcur, ycur);
       for (i=0; i<n; i++) { 
         cont(xbuff[i], ybuff[i]);
       }
       xcur=xbuff[n-1];
       ycur=ybuff[n-1];
       break;
     
     case GE_POLYGON:
       /* draw polygon */
       move(xbuff[0],ybuff[0]);
       for ( i=1; i<n; i++ ) {
         cont(xbuff[i], ybuff[i]);
       }
       cont(xbuff[0],ybuff[0]);
       xcur=xbuff[0];
       ycur=ybuff[0];
       break;
     
    } /* switch */
    lastinstr = n = 0; 
  } /* if */

  switch (ptr->instr) {

    case GE_MOVE:
      plt_xform(ptr->x, ptr->y, &xcur, &ycur);
      move(xcur, ycur);
      break;
   
    case GE_LINE:
      {
        int tmpx, tmpy;
        plt_xform(ptr->x, ptr->y, &tmpx, &tmpy);
        line(xcur, ycur, tmpx, tmpy);
        xcur=tmpx;
        ycur=tmpy;
      }
      break;
      
    case GE_CIRCLE:
      if ( curr_attr.sx == curr_attr.sy ) {
        /* draw it as a circle if it is still a circle after
           transformation of current segment */
        int radius;

        radius = (int)(ptr->x*SCALE+0.5);
        circle(xcur,ycur,radius);
      }
      else {
        plt_sim_ellipse(xcur,ycur,ptr->x,ptr->x);
      }
      break;   

    case GE_ELLIPSE:
      /* draw ellipse */
      plt_sim_ellipse(xcur,ycur,ptr->x,ptr->y);
      break;

    case GE_ARC:
    case GE_ARCEXT:
      {
        static float ang1, ang2;

        if ( ptr->instr == GE_ARC ) {
          /* save angles of arc */
          ang1 = ptr->x;
          ang2 = ptr->y;
        } /* GE_ARC */

        else {

          if ( curr_attr.sx == curr_attr.sy ) {
            /* draw arc as one arc if it is a real arc after
               transformation of current segment */
            int x1,y1,x2,y2;

            /* transform start point and end point of arc */
            plt_xform(cos(ang1)*ptr->x,sin(ang1)*ptr->x,&x1,&y1);
            plt_xform(cos(ang2)*ptr->x,sin(ang2)*ptr->x,&x2,&y2);

            /* draw arc */
            arc(xcur,ycur,
                (int)(x1+xcur-curr_attr.tx*SCALE),
                (int)(y1+ycur-curr_attr.ty*SCALE),
                (int)(x2+xcur-curr_attr.tx*SCALE),
                (int)(y2+ycur-curr_attr.ty*SCALE));
          }
          else {
            /* draw 'arc' with a number of small arcs */
            plt_sim_arc(xcur,ycur,ang1,ang2,ptr->x);
          }

        } /* GE_ARCEXT */

      } /* case GE_ARC, GE_ARCEXT */
      break;

    case GE_POLYLINE:
    case GE_POLYGON:
      lastinstr=ptr->instr;
   
    case GE_CONT:
      plt_xform(ptr->x, ptr->y, &xbuff[n], &ybuff[n]);
      n++;
      break;

    case GE_SETTYPELINE:
      /* change the line style */
      linemod(plt_linestyle[(int)ptr->x]);
      break;

    case GE_TEXT:
      /* draw text */
      label((char *)((int)ptr->x));
      break;

    /* ignore these instructions */
    case GE_SETCOLOR:
    case GE_SETFONT:
    case GE_SETCHARSIZE:
    case GE_SETLINEWIDTH:
    case GE_FILL:
    case GE_CLOSE:
      break;

    /* check that all instructions are handled properly */
    default:
      fprintf(stderr,
              "Internal Error-Action for instruction %d missing.\n",
              ptr->instr);
      break;

  } /* switch */
     	  
} /* plt_exec */
@//E*O*F plot.c//
chmod u=r,g=r,o=r plot.c
 
echo Inspecting for damage in transit...
temp=/tmp/shar$$; dtemp=/tmp/.shar$$
trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
cat > $temp <<\!!!
     314    1039    8695 display.c
     689    1901   18053 ps.c
     579    1887   16700 pic.c
     464    1461   12850 plot.c
    2046    6288   56298 total
!!!
wc  display.c ps.c pic.c plot.c | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
if [ -s $dtemp ]
then echo "Ouch [diff of wc output]:" ; cat $dtemp
else echo "No problems found."
fi
exit 0