/*
*  fig2MF -- convert fig to mfpic METAFONT code
*
*  copyright (c) 1993 Anthony Starks ([email protected])
*  You may do anything you want with this code except sell it
*  or claim that you wrote it.
*
*  Version 0.00 --     Incorporate Tobin's small suggestions
*  Version 0.01 --     Change scaling to inches,
*                      default [x-y]scale is 1/8 inch
*                      slight format changes in cinit()
*  Version 0.02 --     Fixed pen switching bug
*  Version 0.03 --     Support new arcthree mode
*  Version 0.04 --     Token support for text
*/

#include <stdio.h>

#define put_msg(x)      fputs(x, stderr); \
                       fprintf(stderr, \
                       " at line %d of file \"%s\" \n", line, curfile)
#define dofill()        1.2-((double)area_fill/(double)FILL_BLACK)
#define dopen(x)        ((x-1)*PEN_INCR)+DEF_PEN
#define VERSION         0.04
#define BUF_SIZE        1024
#define O_ELLIPSE       1
#define O_POLYLINE      2
#define O_SPLINE        3
#define O_TEXT          4
#define O_ARC           5
#define O_COMP          6
#define O_ECOMP         -6
#define FILL_BLACK      21
#define N_CURVE         11
#define N_POLYLINE      12
#define N_ELLIPSE       19
#define N_ARC           20
#define DEF_PEN         0.5
#define PEN_INCR        0.10
#define DEF_BFILE       "graphbase.mf"
char buf[BUF_SIZE];

typedef struct {
       char *keyword;
       double *value;
} Options;

int line;
double ppi;
double mag;
double code;
double mfpen;
double xscale;
double yscale;
double xl;
double yl;
double xu;
double yu;
double maxy;
char *progname;
char *curfile;
char basefile[100];

Options opts[]  = {
{"-mag",        &mag},
{"-code",       &code},
{"-pen",        &mfpen},
{"-xscale",     &xscale},
{"-yscale",     &yscale},
{"-xneg",       &xl},
{"-xpos",       &xu},
{"-yneg",       &yl},
{"-ypos",       &yu},
{"-top",        &maxy},
{NULL,          NULL}
};


main(argc, argv)
int             argc;
char          *argv[];
{
       FILE           *fp;
       int i;
       progname = argv[0];
       curfile = "Standard Input";
       strcpy(basefile, DEF_BFILE);
       line=1;
       mag=1000.0;
       code=32.0;
       mfpen=0.5;
       xscale=0.125;
       yscale=0.125;
       xl=0.0;
       xu=8.0;
       yl=0.0;
       yu=8.0;
       maxy=8.0;

       if (argc < 2)
               process("-");
       else
       {
               for (i=1; i < argc; i++)
               {
                       parse(argv[i]);
               }
       }

       ffin();
       exit(0);

}

parse(s)
char *s;
{
       int i, n;
       char *strchr();
       double atof();

       for (i=0; opts[i].keyword != NULL; i++)
       {
               if (strncmp(s, opts[i].keyword, strlen(opts[i].keyword)) == 0)
               {
                       *opts[i].value = atof(strchr(s, '=')+1);
                       return;
               }
       }
       process(s);
}

process(filename)
char *filename;
{
       FILE *fp;

       int coord_sys, object;

       if (filename[0] == '-')
       {
               curfile = "Standard Input";
               fp = stdin;
       }
       else
       {
               curfile=filename;
               if ((fp = fopen(filename, "r")) == NULL)
               {
                       fprintf(stderr,
                       "%s: cannot open the input file \"%s\"\n",
                       progname, filename);
                       return;
               }
       }

       line = 1;
       fgets(buf, sizeof(buf), fp);
       if (strncmp(buf, "#FIG 2.1", 8) != 0)
       {
               put_msg("Not a fig 2.1 file");
               return;
       }
       get_line(fp);
       if (sscanf(buf, "%lf%d\n", &ppi, &coord_sys) != 2)
       {
               put_msg("Incomplete data");
               return;
       }
       cinit();
       while (get_line(fp) > 0)
       {
               if (sscanf(buf, "%d", &object) != 1)
               {
                       put_msg("Incorrect format");
                       return;
               }
               switch(object)
               {
                       case O_COMP:
                       case O_ECOMP:
                               break;
                       case O_ELLIPSE:
                               ellipse(fp);
                               break;
                       case O_POLYLINE:
                               polyline(fp);
                               break;
                       case O_SPLINE:
                               curve(fp);
                               break;
                       case O_TEXT:
                               text(fp);
                               break;
                       case O_ARC:
                               arc(fp);
                               break;
                       default:
                               fprintf(stderr, "%d is an ", object);
                               put_msg("unknown object");
                               break;
               }
       }
       cfin();
       if (fp != stdin) fclose(fp);

}

polyline(fp)
FILE *fp;
{
       int     c,
               n,
               type,
               subtype,
               style,
               radius,
               thickness,
               color,
               depth,
               pen,
               area_fill,
               fa,
               ba,
               at,
               as,
               eflag;

       double  style_val,
               athick,
               awidth,
               aht;

       char    epsfile[100];

       c = 0;
       n = sscanf(buf, "%d%d%d%d%d%d%d%d%lf%d%d%d",
               &type,
               &subtype,
               &style,
               &thickness,
               &color,
               &depth,
               &pen,
               &area_fill,
               &style_val,
               &radius,
               &fa,
               &ba);

       if (n != N_POLYLINE)
       {
               put_msg("Malformed polyline");
               return c;
       }
       if (subtype == 5) fscanf(fp, "%d%s", &eflag, epsfile);
       if (fa) fscanf(fp, "%d%d%lf%lf%lf\n", &at, &as, &athick, &awidth, &aht);
       if (ba) fscanf(fp, "%d%d%lf%lf%lf\n", &at, &as, &athick, &awidth, &aht);
       if (thickness > 1)
               printf("  pickup pencircle scaled %.2lfpt;\n",
                       dopen(thickness));
       if (area_fill == FILL_BLACK)
               printf("  cycleshade(0, false,\n");
       else if (area_fill < FILL_BLACK && area_fill > 0)
               printf("  cycleshade(%lfpt, false,\n", dofill());
       else
               printf("  curve(false, false,\n");
       c = coords(type, fp);
       if (thickness > 1)
               printf("  pickup pencircle scaled %.2lfpt;\n",
                       mfpen);

       return c;
}


curve(fp)
FILE *fp;
{
       int     n,
               c,
               i,
               type,
               style,
               subtype,
               thickness,
               color,
               depth,
               pen,
               area_fill,
               fa,
               ba,
               at,
               as;

       double  style_val,
               athick,
               awidth,
               aht,
               z;

       c = 0;
       n = sscanf(buf, "%d%d%d%d%d%d%d%d%lf%d%d",
                  &type,
                  &subtype,
                  &style,
                  &thickness,
                  &color,
                  &depth,
                  &pen,
                  &area_fill,
                  &style_val,
                  &fa,
                  &ba);
       if (n != N_CURVE)
       {
               put_msg("Malformed spline");
               return c;
       }
       if (fa) fscanf(fp, "%d%d%lf%lf%lf\n", &at, &as, &athick, &awidth, &aht);
       if (ba) fscanf(fp, "%d%d%lf%lf%lf\n", &at, &as, &athick, &awidth, &aht);
       if (thickness > 1)
               printf("  pickup pencircle scaled %.2lfpt;\n",
                       dopen(thickness));
       if (area_fill == FILL_BLACK)
               printf("  cycleshade(0, true,\n");
       else if (area_fill < FILL_BLACK && area_fill > 0)
               printf("  cycleshade(%lfpt, true,\n", dofill());
       else
               printf("  curve(true, false,\n");
       c = coords(type, fp);
       if (subtype > 1)
               for (i=0; i < c*4; i++)
                       fscanf(fp, "%lf", &z);
       if (thickness > 1)
               printf("  pickup pencircle scaled %.2lfpt;\n", mfpen);
       return c;
}

coords(obj, fp)
int obj;
FILE *fp;
{
       int c, n, x, y;

       c = 0;
       if ((n = fscanf(fp, "%d%d", &x, &y)) != 2)
       {
               put_msg("Incomplete object");
               return c;
       }
       printf("       (%lf, %lf)", x/ppi, maxy-(y/ppi));
       for (c = 1;;)
       {
               if (fscanf(fp, "%d%d", &x, &y) != 2)
               {
                       put_msg("Incomplete object");
                       fputs(buf, stderr);
                       return c;
               }
               if (x == 9999) break;
               printf(",\n       (%lf, %lf)", x/ppi, maxy-(y/ppi));
               c++;
       }
       printf(");\n");
       return c;

}

ellipse(fp)
FILE *fp;
{
       int      n,
                type,
                subtype,
                style,
                thickness,
                color,
                depth,
                pen,
                area_fill,
                dir,
                cx,
                cy,
                rx,
                ry,
                sx,
                sy,
                ex,
                ey;

       double  style_val,
               angle;

       n = sscanf(buf, "%d%d%d%d%d%d%d%d%lf%d%lf%d%d%d%d%d%d%d%d",
               &type,
               &subtype,
               &style,
               &thickness,
               &color,
               &depth,
               &pen,
               &area_fill,
               &style_val,
               &dir,
               &angle,
               &cx,
               &cy,
               &rx,
               &ry,
               &sx,
               &sy,
               &ex,
               &ey);
       if (n != N_ELLIPSE)
       {
               put_msg("Malformed circle or ellipse");
               return;
       }

       if (thickness > 1)
               printf("  pickup pencircle scaled %.2lfpt;\n",
                       dopen(thickness));

       if (subtype == 3 || subtype == 4)
       {
               if (area_fill == FILL_BLACK)
                       printf("  circshade(0, ");
               else if (area_fill < FILL_BLACK && area_fill > 0)
                       printf("  circshade(%lfpt, ", dofill());
               else
                       printf("  circle(");
               printf("(%lf,%lf),%lf);\n",
                       cx/ppi, maxy-(cy/ppi), rx/ppi);
       }
       else if (subtype == 1 || subtype == 2)
       {
               if (area_fill == FILL_BLACK)
                       printf("  ellshade(0, ");
               else if (area_fill < FILL_BLACK && area_fill > 0)
                       printf("  ellshade(%lfpt, ", dofill());
               else
                       printf("  ellipse(");
               printf("(%lf,%lf),%lf,%lf,0);\n",
                       cx/ppi, maxy-(cy/ppi), rx/ppi, ry/ppi);
       }
       if (thickness > 1)
               printf("  pickup pencircle scaled %.2lfpt;\n", mfpen);

}

arc(fp)
FILE *fp;
{

       int     n,
               type,
               subtype,
               style,
               thickness,
               color,
               depth,
               pen,
               fill,
               dir,
               fa,
               ba,
               x1,
               y1,
               x2,
               y2,
               x3,
               y3;

       double  style_val,
               cx,
               cy;


       n = sscanf(buf, "%d%d%d%d%d%d%d%d%lf%d%d%d%lf%lf%d%d%d%d%d%d",
               &type,
               &subtype,
               &style,
               &thickness,
               &color,
               &depth,
               &pen,
               &fill,
               &style_val,
               &dir,
               &fa,
               &ba,
               &cx,
               &cy,
               &x1,
               &y1,
               &x2,
               &y2,
               &x3,
               &y3);

       if (fa) get_line(fp);
       if (ba) get_line(fp);

       if (n != N_ARC)
       {
               put_msg("Malformed arc");
               return n;
       }
       if (thickness > 1)
               printf("  pickup pencircle scaled %.2lfpt;\n",
                       dopen(thickness));

       printf("  arcthree((%lf,%lf), (%lf,%lf), (%lf,%lf));\n",
               x1/ppi,
               maxy-(y1/ppi),
               x2/ppi,
               maxy-(y2/ppi),
               x3/ppi,
               maxy-(y3/ppi));

       if (thickness > 1)
               printf("  pickup pencircle scaled %.2lfpt;\n",
                       mfpen);

       return n;

}

text(fp)
FILE *fp;
{

       int     n,
               x,
               y;

       int     type,
               font,
               size,
               pen,
               color,
               depth,
               filler,
               angle,
               flags,
               ht,
               len;
       char    text[80],
               junk[2],
               *p,
               *strrchr();

       n = sscanf(buf, "%*d%d%d%d%d%d%d%lf%d%d%d%d%d%[^\1]%[\1]",
                       &type,
                       &font,
                       &size,
                       &pen,
                       &color,
                       &depth,
                       &angle,
                       &flags,
                       &ht,
                       &len,
                       &x,
                       &y,
                       text,
                       junk);
       if ((n != 13) && (n != 14))
       {
               put_msg("Bad Text");
               return 0;
       }
       p = &text[1];
       printf("%% label((%lf,%lf),%s)\n", x/ppi, maxy-(y/ppi), p);
       return(strlen(text));
}

cinit()
{
       static int once = 0;
       static int curchar;
       if (++once == 1)
       {
               curchar = (int)code;
               printf("%%\n%% %s version %.2lf --- Preamble\n%%\n",
                       progname, VERSION);
               printf("mag:=%g/1000; input %s; code:=%g;\n",
                       mag, basefile, code);
               printf("mfpicenv;\ninterim hdwdr:=1; interim hdten:=1;\n");
               printf("interim penwd:=%.2lfpt;\npickup pencircle scaled penwd;\n", mfpen);
       }
       printf("%%\n%% %s (char %d)\n%%\n", curfile, ++curchar);
       printf("xscale:=%.3lf; yscale:=%.3lf; bounds(%.3lf,%.3lf,%.3lf,%.3lf);\n",
               xscale, yscale, xl, xu, yl, yu);
       printf("beginchar(incr code,xscale*(xpos-xneg)*in#,yscale*(ypos-yneg)*in#,0);\n");
       printf("  setztr;\n");
       printf("  pickup pencircle scaled %.2lfpt;\n", mfpen);
}

cfin()
{
       printf("endchar;\n");
}

ffin()
{
       if (line > 1) printf("endmfpicenv;\nend.\n");
}

get_line(fp)
       FILE           *fp;
{
       while (1)
       {
               if (NULL == fgets(buf, BUF_SIZE, fp))
               {
                       return (-1);
               }
               line++;
               if (*buf != '\n' && *buf != '#')
                       return (1);
               /* Skip empty and comment lines */
       }
}