/*************************************************************************
  hpout.c

  Copyright 1986  by Stephen Vermeulen

  Licence for non-comercial use is granted so long as this program is
  not sold or packaged with anything that is sold.  Use by any government
  (of any ideology) or agency or agent thereof is strictly prohibited!

  This program acts as a filter converting a file containing Vdraw audit
  commands into HPGL plotter commands so that Vdraw can be used to prepare
  high quality graphics on XY plotters.  Note: some audit commands are
  not implemented at all, these include area filling and the various bit
  map manipulations like magnify, cut, paste and move, conceivably a very
  ambitious user might attempt to implement these as well.  Good luck!

  Syntax is:

     hpout  plot.guide  vdraw.audit  hpgl.out

  where:

     plot.guide      is a small file containing some parameters of interest
                     to the program about the plotter and how large the
                     drawing should be made.

                     magfactor  (magnifing factor, typically 10)
                     fmag       (font magnifing factor)
                     p1x p1y    (location of 0, 0 on the plotter)
                     plotter    (model number of plotter)

     vdraw.audit     is the file containg the audit commands output by
                     Vdraw.

     hpgl.out        is the file containing the hpgl approximation of the
                     Vdraw drawing.

**************************************************************************/
  
#include "stdio.h"
#include "math.h"

#define AREAN1 30
#define AREAN2 30
#define AREAM 8
#define AREAPOWER 3
#define AUDITVERSION 1
#define LINEN 16
#define CIRCLE 0x1
#define ARC    0x2
#define PIE_PIECE 0x4
#define FINISHED  0x10
#define AUDIT_UNDERLINE 1
#define AUDIT_BOLD      2
#define AUDIT_ITALIC    4
#define AUDIT_EXTENDED  8
#define AUDIT_J1       0
#define AUDIT_J2       1
#define AUDIT_COMPLEMENT 2
#define TRUE 1
#define FALSE 0
#define ARROW_HOLLOW 0
#define ARROW_SOLID  1
#define ARROW_START  2
#define ARROW_END    4

char buf[80];

char command[20];
char *audit_commands[] =
{
  "aspect", "bitmap", "move", "draw", "select", "pen",
  "box", "circle", "arc", "fill", "text", "erase", "font",
  "version","line","pixel","cut","paste","invert","copy",
  "arrow","erasebox"
};
#define NCOMMANDS 22
FILE *hp_out;

main(argc, argv)
int argc;
char *argv[];
{
  FILE *audit_in, *guide_in;
  int i, j, x, y, x1, y1, x2, y2;
  int version, mode, mode1, mode2, found;
  int pen_no, line_type, ubie, j12c;
  int invflag, colour, arrowflag, fontheight;
  int p1x, p1y, plotterno, arrowlength, arrowwidth;
  int bm_width, bm_height, bm_depth;
  double aspect, r, a1, a2, mag;
  double deg_to_rad, fmag, theta, da, a;
  double ts, tc, t1, t2, t3, t4;
  double p2x, p2y, p3x, p3y;
  static char fontname[80];

  fontheight = 10;              /* this will be overridden later */
  deg_to_rad = atan(1.0)/45.0;
  if (argc != 4)
  {
    fprintf(stderr, "Syntax:  %s plot.guide vdraw.audit hpgl.out\n", argv[0]);
    exit(0);
  }
  guide_in = fopen(argv[1], "r");
  if (guide_in == NULL)
  {
    fprintf(stderr, "Cannot open %s for read\n", argv[1]);
    exit(1);
  }
  audit_in = fopen(argv[2], "r");
  if (audit_in == NULL)
  {
    fprintf(stderr, "Cannot open %s for read\n", argv[2]);
    exit(2);
  }
  hp_out = fopen(argv[3], "w");
  if (hp_out == NULL)
  {
    fprintf(stderr, "Cannot open %s for write\n", argv[3]);
    exit(3);
  }
  fscanf(guide_in, "%lf", &mag);
  fscanf(guide_in, "%lf", &fmag);
  fscanf(guide_in, "%d %d", &p1x, &p1y);
  fscanf(guide_in, "%d", &plotterno);
  arrowlength = 12.0;
  arrowwidth  =  4.0;
  /*****************************************************************

    Plotter initialization and initial setup routines follow

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

  fprintf(hp_out, "IN;SP2;\n");  /* pick default pen up */

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

     Now loop through the Vdraw audit file and convert to HPGL

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

  version = 1;
  while( fgets(buf, 80, audit_in) )
  {
    sscanf(buf, "%s", command);
    found = 0;
    for(i = 0; i<NCOMMANDS; ++i)
    {
      if (!strcmp(command, audit_commands[i]))
      {
        found = 1;
        break;
      }
    }
    if (found) /* command exists so perform it */
    {
      switch(i)
      {
        case 0:  /* aspect */
          sscanf(buf, "%s %lf", command, &aspect);
          break;
        case 1:  /* bit map --- might use this to scale plot  */
                 /* use the bm_height to correct the y axis   */
          sscanf(buf, "%s %d %d %d", command, &bm_width, 
                                     &bm_height, &bm_depth);
          break;
        case 2:  /* move */
          sscanf(buf, "%s %d %d", command, &x, &y);
          x = x * mag/aspect + p1x + 0.5;
          y = (bm_height - y) * mag + p1y + 0.5;
          fprintf(hp_out, "PU;PA %d, %d;\n", x, y);
          break;
        case 3:  /* draw */
          sscanf(buf, "%s %d %d", command, &x, &y);
          x = x * mag/aspect + p1x + 0.5;
          y = (bm_height - y) * mag + p1y +0.5;
          fprintf(hp_out, "PD;PA %d, %d;\n", x, y);
          break;
        case 4:  /* select */
          sscanf(buf, "%s %d %d", command, &x, &y);
          if (x == 1)  /* set line pattern */
          {
            SetLine(y);
            line_type = y;
          }
          if (x == 2) /* set area fill pattern */
          {
          }
          break;
        case 5:  /* pen */
          sscanf(buf, "%s %d", command, &pen_no);
          fprintf(hp_out, "PU;SP %d;\n", pen_no+1);  /* no pen 0 on HP plotter */
          break;
        case 6:  /* box */
          sscanf(buf, "%s %d %d %d %d %d", command, &mode1, &x1, &y1, &x2, &y2);
          x1 = x1 * mag/aspect + p1x + 0.5;
          y1 = (bm_height - y1) * mag + p1y + 0.5;
          x2 = x2 * mag/aspect + p1x + 0.5;
          y2 = (bm_height - y2) * mag + p1y + 0.5;
          fprintf(hp_out, "PU;PA %d, %d;PD;PA %d, %d, %d, %d, %d, %d, %d, %d;PU;\n",
                           x1, y1,   x1, y2,  x2, y2,  x2, y1,  x1, y1);
          break;
        case 7:  /* circle */
          sscanf(buf, "%s %d %d %lf %d", command, &x, &y, &r, &mode1);
          x = x * mag/aspect + p1x + 0.5;
          y = (bm_height - y) * mag + p1y + 0.5;
          fprintf(hp_out, "PU;PA %d, %d;PD;\n", (int) (x + r * mag), y);
          switch (plotterno)
          {
            case 7220:
            case 7475:
              fprintf(hp_out, "PU;PA %d, %d;CI %d, 1;\n", x, y,
                              (int) (r * mag) );
              break;
            
            default:
              fprintf(hp_out, "PU;PA %d, %d;PD;\n", (int) (x + r * mag), y);
          for (i = 0; i < 361; ++i)
          {
            x1 = x + r * mag * cos(i*deg_to_rad);
            y1 = y + r * mag * sin(i*deg_to_rad);
            fprintf(hp_out, "PA %d, %d;\n", x1, y1);
          }
              break;
          }
          fprintf(hp_out, "PU;\n");
          if (mode1)   /* draw the centre mark */
          {
            x1 = 5 * mag + 0.5;
            y1 = 5 * mag + 0.5;
            fprintf(hp_out, "PA %d, %d;PD;PA %d, %d;PU;\n",
                             x - x1, y,      x + x1, y);
            fprintf(hp_out, "PA %d, %d;PD;PA %d, %d;PU;\n",
                                x, y - y1,   x, y + y1);
          }
          break;
        case 8:  /* arc */
          sscanf(buf, "%s %d %d %lf %lf %lf %d %d", command,
                      &x, &y, &r, &a1, &a2, &mode1, &mode2 );
          x = x * mag/aspect + p1x + 0.5;
          y = (bm_height - y) * mag + p1y + 0.5;
          fprintf(hp_out, "PU;PA %d, %d;\n", x, y);
          switch(plotterno)
          {
            case 7475:
            case 7220:
              if (mode2)
                fprintf(hp_out,"PD;\n");
              x1 = x + r * mag * cos(a1*deg_to_rad);
              y1 = y - r * mag * sin(a1*deg_to_rad);
              fprintf(hp_out, "PA %d, %d;\n", x1, y1);
              fprintf(hp_out, "AA %d, %d, %d, 1;\n", x, y,
                              (int) (a1 - a2 + 0.5) );
              if (mode2)
                fprintf(hp_out, "PA %d, %d;PU;\n", x, y);
              else
                fprintf(hp_out, "PU;\n");
              break;

            default:
          x2 = 1;                   /* temp flag */
          da = (a2 - a1)/360.0;
          for (a = a1; a < a2; a += da)
          {
            x1 = x + r * mag * cos(a*deg_to_rad);
            y1 = y - r * mag * sin(a*deg_to_rad);
            if (x2)  /* first point so move */
            {
              if (mode2)   /* draw the sector line */
                fprintf(hp_out, "PD;\n");
              fprintf(hp_out, "PA %d, %d;PD;\n", x1, y1);
              x2 = 0;
            }
            else
            {
              fprintf(hp_out, "PA %d, %d;\n", x1, y1);
            }
          }
          if (mode2)                   /* draw the other sector line */
            fprintf(hp_out, "PA %d, %d;\n", x, y);
          fprintf(hp_out, "PU;\n");
              break;
          }
          if (mode1)   /* draw the centre mark */
          {
            x1 = 5 * mag + 0.5;
            y1 = 5 * mag + 0.5;
            fprintf(hp_out, "PA %d, %d;PD;PA %d, %d;PU;\n",
                             x - x1, y,      x + x1, y);
            fprintf(hp_out, "PA %d, %d;PD;PA %d, %d;PU;\n",
                                x, y - y1,   x, y + y1);
          }
          break;
        case 9:  /* fill --- this driver ignores this */
          sscanf(buf, "%s %d %d", command, &x, &y);
          break;
        case 10: /* text */
          sscanf(buf, "%s %d %d %d", command, &ubie, &j12c, &invflag);

          /*************************
            Currently only italics
            and extended mode are
            emulated, it would also
            be possible to do bold
            facing and underline.
          *************************/

          if (ubie & AUDIT_ITALIC)
            fprintf(hp_out, "SL 0.36;\n");  /* 20 degree foward slant */
          else
            fprintf(hp_out, "SL 0;\n");

          /**********************
            Turn on extended mode
            by making characters
            wider than before
          ***********************/

          if (ubie & AUDIT_EXTENDED)
            fprintf(hp_out, "SI %f, %f;\n", fontheight * fmag/1.5,
                                              fontheight * fmag );
          fgets(buf, 80, audit_in);
          fprintf(hp_out, "LB%s\003", buf);

          /**************************
            If underline mode is on \
            then return to start of
            the text line and then 
            put pen down and draw a
            line underneath it by 
            doing CP pen moves.
          ***************************/

          if (ubie & AUDIT_UNDERLINE)
          {
          SetLine(0);
          fprintf(hp_out, "PU;PA %d, %d;CP 0, -.2;PD;\n", x, y);
          for (j = 0; j < (strlen(buf) - 1); ++j)
            fprintf(hp_out, "CP 1,0;");
          fprintf(hp_out, "PU;\n");
          SetLine(line_type);
          }


          /***********************
            If in Bold Faced mode
            move back to the start
            do a small CP and then 
            retype the text.
          ************************/

          if (ubie & AUDIT_BOLD)
          {
            fprintf(hp_out, "PU;PA %d, %d;CP .1, 0;\n", x, y);
            fprintf(hp_out, "LB%s\003\n", buf);
          }
          /*****************
            If in extended
            mode turn it off
          ******************/

          if (ubie & AUDIT_EXTENDED)
            fprintf(hp_out, "SI %f, %f;\n", fontheight * fmag/2.0,
                                              fontheight * fmag );
          break;
        case 11: /* erase */
          break;
        case 12: /* font */
          sscanf(buf, "%s %s %d", command, fontname, &fontheight);

          /***********************
            Ignore the name of the
            font for now, just use
            the font height to set
            the character size.
          ************************/

          fprintf(hp_out, "SI %f, %f;\n", fontheight * fmag/2.0,
                                            fontheight * fmag );
          break;
        case 13: /* version number */
          sscanf(buf, "%s %d", command, &version);
          break;
        case 14: /* line */
          sscanf(buf, "%s %d %d %d %d %d", command,
                          &arrowflag, &x1, &y1, &x2, &y2 );
          x1 = x1 * mag/aspect + p1x + 0.5;
          y1 = (bm_height - y1) * mag + p1y + 0.5;
          x2 = x2 * mag/aspect + p1x + 0.5;
          y2 = (bm_height - y2) * mag + p1y + 0.5;
          fprintf(hp_out, "PU;PA %d, %d;PD;PA %d, %d;PU;\n",
                                 x1, y1,      x2, y2);
            /******************
              Put arrow heads
              on the appropriate
              line ends
              For the moment only
              hollow arrow heads
              are supported.
            *******************/
          if ((arrowflag & ARROW_START) | (arrowflag & ARROW_END))
          {
            theta = atan2( (double) (y2-y1), (double) (x2-x1)/aspect );
            ts = sin(theta);
            tc = cos(theta);
            t1 = (-arrowlength) * tc;
            t2 = (-arrowlength) * ts;
            t3 = arrowwidth/2.0*ts;
            t4 = arrowwidth/2.0*tc;
            p2x = t1 - t3;
            p2y = t2 + t4;
            p3x = t1 + t3;
            p3y = t2 - t4;
            SetLine(0);           /* solid line */
            if (arrowflag & ARROW_END)   /* arrow head at x1, y1 */
            {
              fprintf(hp_out, "PU;PA %d, %d;\n", x1, y1);
              fprintf(hp_out, "PD;PA %d, %d, %d, %d, %d, %d;PU;\n",
                               (int)(x1+p2x), (int)(y1+p2y),
                               (int)(x1+p3x), (int)(y1+p3y), x1, y1);
            }
            if (arrowflag & ARROW_START)   /* arrow head at x2, y2 */
            {
              fprintf(hp_out, "PU;PA %d, %d;\n", x2, y2);
              fprintf(hp_out, "PD;PA %d, %d, %d, %d, %d, %d;PU;\n",
                               (int)(x2-p3x), (int)(y2-p3y),
                               (int)(x2-p2x), (int)(y2-p2y), x2, y2);
            }
            SetLine(line_type);
          }
          break;
        case 15: /* pixel */
          sscanf(buf, "%s %d %d %d", command, &x, &y, &colour);
          break;
        case 16: /* cut   */
          sscanf(buf, "%s %d %d %d %d", command, &x1, &y1, &x2, &y2);
          break;
        case 17: /* paste */
          sscanf(buf, "%s %d %d %d", command, &x, &y, &mode);
          break;
        case 18: /* invert */
          sscanf(buf, "%s %d %d %d %d", command, &x1, &y1, &x2, &y2);
          break;
        case 19: /* copy (area move) */
          sscanf(buf, "%s %d %d %d %d %d %d %d", command,
                          &x1, &y1, &x2, &y2, &x, &y, &mode);
          break;
        case 20: /* specify the size of the arrow heads for lines */
          sscanf(buf, "%s %d %d", command, &arrowlength, &arrowwidth);
          arrowlength *= mag;
          arrowwidth  *= mag;
          break;
        case 21:  /* erase box (another command ignored) */
          sscanf(buf, "%s %d %d %d %d", command, &x1, &y1, &x2, &y2);
          break;
        default:
          break;
      }
    }
  }
  /******************
    The following
    plotter commands
    are used to close
    it down neatly
  *******************/

  fprintf(hp_out, "SP0;\n");

  fclose(audit_in);
  fclose(hp_out);
  fclose(guide_in);
}

/********************************************************************
   The following function is used to set the line type based on the
   sub menu topic that was selected back in Vdraw
*********************************************************************/

SetLine(y)
int y;
{
  switch(y)
  {
    case 0:  /* solid */
      fprintf(hp_out, "PU;LT;\n");
      break;
    case 1:
    case 2:
    case 3:      /* quick and dirty initial approximation */
    case 4:
    case 5:
    case 6:
      fprintf(hp_out, "PU;LT %d;\n", y);
      break;
    case 7:
    case 8:
    case 9:
    case 10:
    case 11:
    case 12:
      fprintf(hp_out, "PU;LT %d, 2.0;\n", y-6);
      break;
    case 13:
    case 14:
    case 15:
      fprintf(hp_out, "PU;LT %d, 1.0;\n", y-11);
      break;
    default:
      break;
  }
}
