#ifndef lint
static const char	RCSid[] = "$Id: psplot.c,v 1.4 2018/01/17 21:58:23 greg Exp $";
#endif
/*
 *  psplot.c - routines for generating PostScript output.
 *
 *	9/23/88
 */

#include <ctype.h>

#include "meta.h"
#include "plot.h"
#include "psplot.h"

#ifdef SMALLAREA
#define HMARGIN		(0.2*72)		/* horizontal margin */
#define VMARGIN		(0.2*72)		/* vertical margin */
#define PWIDTH		(6*72-2*HMARGIN)	/* width of device */
#define PHEIGHT		(6*72-2*VMARGIN)	/* height of device */
#else
#define HMARGIN		(.6*72)			/* horizontal margin */
#define VMARGIN		(.6*72)			/* vertical margin */
#define PWIDTH		(8.5*72-2*HMARGIN)	/* width of device */
#define PHEIGHT		(11*72-2*VMARGIN)	/* height of device */
#endif

#define PSQUARE		PWIDTH			/* maximal square */

#define MAXPATHL	700			/* maximum path length */

#define PREFIX		"seg_"		/* name prefix */
#define ESCCHAR		'#'		/* to introduce hex in name */

#define checkline()	if (inaline) endline()


static int	savelvl = 0;

static int	inaline = 0;

static char	hexdigit[] = "0123456789ABCDEF";

static char	buf[512];

static void endline(void);


static char *
convertname(s)				/* convert to segment name */
char	*s;
{
	register char	*cp, *cp0;

	for (cp = buf, cp0 = PREFIX; *cp0; )
		*cp++ = *cp0++;
	for (cp0 = s; *cp0; cp0++) {
		if (isalnum(*cp0))
			*cp++ = *cp0;
		else {
			*cp++ = ESCCHAR;
			*cp++ = hexdigit[*cp0 >> 4];
			*cp++ = hexdigit[*cp0 & 0xf];
		}
	}
	*cp = '\0';
	return(buf);
}


static char *
convertstring(		/* convert to acceptable string */
	register char	*s
)
{
	register char	*cp;
	
	for (cp = buf; *s; s++) {
		if (*s == '(' || *s == ')')
			*cp++ = '\\';
		if (isprint(*s))
			*cp++ = *s;
		else {
			*cp++ = '\\';
			*cp++ = (*s>>6) + '0';
			*cp++ = (*s>>3 & 07) + '0';
			*cp++ = (*s & 07) + '0';
		}
	}
	*cp = '\0';
	return(buf);
}


void
init(			/* initialize */
	char	*id
)
{
	printf("%%!PS-Adobe-2.0 EPSF-2.0\n");
	printf("%%%%BoundingBox: %.0f %.0f %.0f %.0f\n",
			HMARGIN+(PWIDTH-PSQUARE*1.05)/2.,
			VMARGIN+(PHEIGHT-PSQUARE*1.05)/2.,
			HMARGIN+(PWIDTH+PSQUARE*1.05)/2.,
			VMARGIN+(PHEIGHT+PSQUARE*1.05)/2.);
	printf("%%%%DocumentFonts: Helvetica Courier\n");
	if (id != NULL)
		printf("%%%%Creator: %s\n", id);
	printf("gsave\n");
	printf("save\n");
	printf("256 dict begin\n");
	printf("%f %f translate\n", HMARGIN+(PWIDTH-PSQUARE)/2.,
			VMARGIN+(PHEIGHT-PSQUARE)/2.);
	printf("%f %f scale\n", (double)PSQUARE/XYSIZE,
			(double)PSQUARE/XYSIZE);
	printf("/mapping matrix currentmatrix def\n");
	printf("/segment {\t\t%% name lth pat col dir xmn ymn xmx ymx\n");
	printf("\t/startstate save def\n");
	printf("\t128 dict begin\t\t%% get parameters\n");
	printf("\t/ymax exch def /xmax exch def\n");
	printf("\t/ymin exch def /xmin exch def\n");
	printf("\t/orient exch def\n");
	printf("\t/colndx exch def /patndx exch def /lthick exch def\n");
	printf("\t%% segment name left on stack\n");
	printf("\txmax xmin add 2 div ymax ymin add 2 div translate\n");
	printf("\txmax xmin sub %d div ymax ymin sub %d div scale\n",
			XYSIZE, XYSIZE);
	printf("\torient 90 mul rotate\n");
	printf("\t%d %d translate\n", -XYSIZE/2, -XYSIZE/2);
	printf("\tcvx exec\t\t\t%% execute segment\n");
	printf("\tend\n");
	printf("\tstartstate restore\n");
	printf("} def\n");
	printf("/vstr {\t\t\t%% string\n");
	printf("\t0 setcolor\n");
	printf("\t/Helvetica findfont setfont\n");
	printf("\tdup stringwidth pop %d exch div %d scale\n", XYSIZE, XYSIZE);
	printf("\tcurrentfont charorigin translate\n");
	printf("\t0 0 moveto show\n");
	printf("} def\n");
	printf("/charorigin {\t\t\t%% font charorigin xorg yorg\n");
	printf("\tdup /FontBBox get aload pop pop pop\n");
	printf("\t3 2 roll /FontMatrix get transform\n");
	printf("\texch neg exch neg\n");
	printf("} def\n");
	printf("/newline {\t\t\t%% lpat lthick col x0 y0\n");
	printf("\tnewpath moveto\n");
	printf("\tsetcolor setlthick setlpat\n");
	printf("} def\n");
	printf("/rfilldict 4 dict def\n");
	printf("/rfill {\t\t\t%% pat col xmn ymn xmx ymx\n");
	printf("\trfilldict begin\n");
	printf("\t/ymax exch def /xmax exch def\n");
	printf("\t/ymin exch def /xmin exch def\n");
	printf("\tsetcolor setpattern\n");
	printf("\tnewpath\n");
	printf("\txmin ymin moveto\n");
	printf("\txmax ymin lineto\n");
	printf("\txmax ymax lineto\n");
	printf("\txmin ymax lineto\n");
	printf("\tclosepath patfill\n");
	printf("\tend\n");
	printf("} def\n");
	printf("/fillpoly {\t\t\t%% pat col mark x1 y1 .. xn yn\n");
	printf("\tnewpath moveto\n");
	printf("\tcounttomark 2 idiv { lineto } repeat cleartomark\n");
	printf("\tclosepath\n");
	printf("\tsetcolor setpattern\n");
	printf("\t{ gsave 0 setlinewidth stroke grestore } if\n");
	printf("\tpatfill\n");
	printf("} def\n");
	printf("/mstr {\t\t\t%% fnt col x y\n");
	printf("\t100 add moveto setcolor setmfont show\n");
	printf("} def\n");
	printf("/patfill {\n");
	printf("\tfill\t\t\t\t%% unimplemented\n");
	printf("} def\n");
	printf("/setmfont {\t\t\t%% fontndx\n");
	printf("\tmfontab exch get setfont\n");
	printf("} def\n");
	printf("/setcolor {\t\t\t%% colndx\n");
	printf("\tcolndx 0 ne { pop colndx } if\n");
	printf("\trgbcoltab exch get aload pop setrgbcolor\n");
	printf("} def\n");
	printf("/setlthick {\t\t\t%% lthick\n");
	printf("\tlthick 0 ne { pop lthick } if\n");
	printf("\tsetlinewidth\n");
	printf("} def\n");
	printf("/setlpat {\t\t\t%% lpatndx\n");
	printf("\tdashtab exch get 0 setdash\n");
	printf("} def\n");
	printf("/setpattern {\t\t\t%% patndx\n");
	printf("\tpop\t\t\t\t%% unimplemented\n");
	printf("} def\n");
	printf("/canonfont\t\t\t%% canonical matrix string font\n");
	printf("\t/Courier findfont\n");
	printf("\tdup charorigin matrix translate\n");
	printf("\tmakefont\n");
	printf("def\n");
	printf("/mfontab [\t\t\t%% hardware font table\n");
	printf("\t[\n");
	printf("\t\t[ 340 0 0 340 0 -340 ]\n");
	printf("\t\t[ 681 0 0 340 0 -340 ]\n");
	printf("\t\t[ 340 0 0 681 0 -681 ]\n");
	printf("\t\t[ 681 0 0 681 0 -681 ]\n");
	printf("\t\t[ 282 0 0 282 0 -282 ]\n");
	printf("\t\t[ 564 0 0 282 0 -282 ]\n");
	printf("\t\t[ 282 0 0 564 0 -564 ]\n");
	printf("\t\t[ 564 0 0 564 0 -564 ]\n");
	printf("\t\t[ 199 0 0 199 0 -199 ]\n");
	printf("\t\t[ 398 0 0 199 0 -199 ]\n");
	printf("\t\t[ 199 0 0 398 0 -398 ]\n");
	printf("\t\t[ 398 0 0 398 0 -398 ]\n");
	printf("\t\t[ 169 0 0 169 0 -169 ]\n");
	printf("\t\t[ 339 0 0 169 0 -169 ]\n");
	printf("\t\t[ 169 0 0 339 0 -339 ]\n");
	printf("\t\t[ 339 0 0 339 0 -339 ]\n");
	printf("\t]\n");
	printf("\t{ canonfont exch makefont }\n");
	printf("\tforall\n");
	printf("] def\n");
	printf("/dashtab [ [ ] [ 200 80 ] [ 80 80 ] [ 200 80 80 80 ] ] def\n");
	printf("/rgbcoltab [ [ 0 0 0 ] [ 1 0 0 ] [ 0 1 0 ] [ 0 0 1 ] ] def\n");
	printf("/colndx 0 def /patndx 0 def /lthick 0 def\n");
	printf("%%%%EndProlog\n");
}


void
done(void)				/* done with graphics */
{
	printf("end\nrestore\ngrestore\n");
}


void
endpage(void)			/* done with page */
{
	checkline();

	printf("showpage\nmapping setmatrix\n");
}


void
segopen(			/* open a segment */
	char	*s
)
{
	checkline();

	printf("/%s {\n", convertname(s));
}


void
segclose(void)			/* close a segment */
{
	checkline();

	printf("} def\n");
}


void
doseg(			/* instance of a segment */
	register PRIMITIVE	*p
)
{
	checkline();

	printf("/%s %d %d %d ", convertname(p->args),
			WIDTH(p->arg0>>2 & 03),
			p->arg0>>2 & 03, p->arg0 & 03);
	printf("%d %d %d %d %d segment\n", p->arg0>>4,
			p->xy[XMN], p->xy[YMN], p->xy[XMX], p->xy[YMX]);
}


extern void
printstr(			/* print a string */
	register PRIMITIVE	*p
)
{
	checkline();
	printf("(%s) %d %d %d %d mstr\n", convertstring(p->args),
			p->arg0>>2 & 017, p->arg0 & 03,
			p->xy[XMN], p->xy[YMX]);
}


void
plotvstr(			/* print a vector string */
	register PRIMITIVE	*p
)
{
	checkline();

	printf("(%s) /vstr %d 0 %d ", convertstring(p->args),
			WIDTH(p->arg0>>2 & 03), p->arg0 & 03);
	printf("%d %d %d %d %d segment\n", p->arg0>>4,
			p->xy[XMN], p->xy[YMN], p->xy[XMX], p->xy[YMX]);
}


void
plotlseg(			/* plot a line segment */
	register PRIMITIVE	*p
)
{
	static int	right = FALSE;
	static int	curx, cury;
	static int	curlpat, curlthick, curlcol;
	int	y1, y2;
	int	lpat, lthick, lcol;

	if (p->arg0 & 0100) {
		y1 = p->xy[YMX];
		y2 = p->xy[YMN];
	} else {
		y1 = p->xy[YMN];
		y2 = p->xy[YMX];
	}
	lpat = p->arg0>>4 & 03;
	lthick = WIDTH(p->arg0>>2 & 03);
	lcol = p->arg0 & 03;
	if (!inaline || lpat != curlpat ||
			lthick != curlthick || lcol != curlcol) {
		checkline();
		printf("%d %d %d %d %d newline\n",
				curlpat = lpat,
				curlthick = lthick,
				curlcol = lcol,
				curx = p->xy[XMN],
				cury = y1);
	}
	if (curx == p->xy[XMN] && cury == y1) {
		printf("%d %d lineto\n", curx = p->xy[XMX], cury = y2);
	} else if (curx == p->xy[XMX] && cury == y2) {
		printf("%d %d lineto\n", curx = p->xy[XMN], cury = y1);
	} else if ( (right = !right) ) {
		printf("%d %d moveto ", p->xy[XMN], y1);
		printf("%d %d lineto\n", curx = p->xy[XMX], cury = y2);
	} else {
		printf("%d %d moveto ", p->xy[XMX], y2);
		printf("%d %d lineto\n", curx = p->xy[XMN], cury = y1);
	}
	if (++inaline >= MAXPATHL)
		endline();
}


void
endline(void)			/* close current line */
{
	printf("stroke\n");
	inaline = 0;
}


extern void
fillrect(			/* fill a rectangle */
	register PRIMITIVE	*p
)
{
	checkline();

	printf("%d %d %d %d %d %d rfill\n", p->arg0>>2 & 03, p->arg0 & 03,
			p->xy[XMN], p->xy[YMN], p->xy[XMX], p->xy[YMX]);
}


extern void
filltri(			/* fill a triangle */
	register PRIMITIVE	*p
)
{
	static short	corn[4][2] = {{XMN,YMX},{XMN,YMN},{XMX,YMN},{XMX,YMX}};
	int		orient;
	register int	i;
	
	checkline();

	printf("false %d %d mark\n", p->arg0>>2 & 03, p->arg0 & 03);
	orient = p->arg0>>4 & 03;
	for (i = 0; i < 4; i++)
		if (i != orient)
			printf("%d %d ", p->xy[corn[i][0]],
					p->xy[corn[i][1]]);
	printf("fillpoly\n");
}


void
xform(		/* transform a point according to p */
	register int  *xp,
	register int  *yp,
	register PRIMITIVE  *p
)
{
    int  x = 0, y = 0;

    switch (p->arg0 & 060) {
	case 0:			/* right */
	    x = *xp;
	    y = *yp;
	    break;
	case 020:		/* up */
	    x = (XYSIZE-1) - *yp;
	    y = *xp;
	    break;
	case 040:		/* left */
	    x = (XYSIZE-1) - *xp;
	    y = (XYSIZE-1) - *yp;
	    break;
	case 060:		/* down */
	    x = *yp;
	    y = (XYSIZE-1) - *xp;
	    break;
    }
    *xp = CONV(x, p->xy[XMX] - p->xy[XMN]) + p->xy[XMN];
    *yp = CONV(y, p->xy[YMX] - p->xy[YMN]) + p->xy[YMN];
}


extern void
fillpoly(p)			/* fill a polygon */
register PRIMITIVE	*p;
{
	register char	*s;
	int	curx, cury;

	checkline();

	printf("%s %d %d mark\n", p->arg0 & 0100 ? "false" : "true",
			p->arg0>>2 & 03, p->arg0 & 03);
	s = p->args;
	while ((s = nextscan(nextscan(s, "%d", (char*)&curx), "%d", (char*)&cury)) != NULL) {
		xform(&curx, &cury, p);
		printf("%d %d ", curx, cury);
	}
	printf("fillpoly\n");
}


void
set(		/* set an attribute or context */
	int	attrib,
	char	*val
)
{
	checkline();

	switch (attrib) {
	case SALL:
		printf("save\n");
		savelvl++;
		break;
	case SPAT0:
	case SPAT1:
	case SPAT2:
	case SPAT3:
		break;
	default:
		error(WARNING, "illegal set command");
		break;
	}
}


void
unset(			/* unset an attribute or context */
	int	attrib
)
{
	checkline();

	switch (attrib) {
	case SALL:
		if (savelvl > 0) {
			printf("restore\n");
			savelvl--;
		}
		break;
	case SPAT0:
	case SPAT1:
	case SPAT2:
	case SPAT3:
		break;
	default:
		error(WARNING, "illegal unset command");
		break;
	}
}


void
reset(			/* reset an attribute or context */
	int	attrib
)
{
	checkline();

	switch (attrib) {
	case SALL:
		while (savelvl > 0) {
			printf("restore\n");
			savelvl--;
		}
		printf("restore save\n");
		break;
	case SPAT0:
	case SPAT1:
	case SPAT2:
	case SPAT3:
		break;
	default:
		error(WARNING, "illegal reset command");
		break;
	}
}
