static char rcsid[] = "$Header: /usr/jjc/dvitops/RCS/tpic.c,v 1.4 90/08/14 13:03:50 jjc Rel $";

#include "dvitops.h"

#ifdef TPIC_SUPPORT

/* Dotted lines look a bit anaemic; so we add this many milli-inches to
   the pen size of a dotted line. */

#define DOTTED_LINE_WIDTH_ADDITION 3

/* flags in tpic_object is or'ed from these */

#define ARC 1
#define SPLINE 2
#define SHADED 4
#define INVISIBLE 8
#define DOTTED 16
#define DASHED 32

struct point {
	integer x;
	integer y;
};

struct tpic_object {
	struct tpic_object *next;
	integer h, v;
	int region;
	unsigned flags;
	integer pen_size;
	float shade;
	union {
		struct {
			float dash_width;
			int npoints;
			struct point point_vector[2]; /* may be bigger than this */
		} p;
		struct {
			integer cx;
			integer cy;
			integer rx;
			integer ry;
			float s;
			float e;
		} a;
	} u;
};

#ifdef PROTO
static void draw_path(FILE *psfp, double sc, integer h, integer v,
					  struct point *pv, int np, int closed);
static void draw_spline(FILE *psfp, double sc, integer h, integer v,
					  struct point *pv, int np);
static void draw_ellipse(FILE *psfp, double sc, integer h, integer v,
						 integer cx, integer cy, integer rx, integer ry);
static void draw_arc(FILE *psfp, double sc, integer h, integer v,
					 integer cx, integer cy, integer r, double s, double e);
#else
static void draw_path();
static void draw_spline();
static void draw_ellipse();
static void draw_arc();
#endif

struct tpic_object *tpic_object_list = NULL;

#define TPIC_CODE(c1, c2) (((unsigned char)(c1) << 8) + (unsigned char)(c2))

/* return 1 if it was a tpic_special, 0 otherwise */

int tpic_special(pass, arg, x, y)
int pass;
char *arg;
integer x, y;
{
	static integer current_pen_size = 8;
	static unsigned tpic_flags = 0;
	static float shade = 0.0;
	static int npoints = 0;
	static int maxpoints = 0;
	static struct point *point_vector = 0;
	double dash_width;
	int complete = 0;
	while (*arg == ' ')
		arg++;
	if (arg[0] == '\0' || arg[1] == '\0'
		|| (arg[2] != ' ' && arg[2] != '\0'))
		return 0;
	if (pass != PASS2) {
		switch (TPIC_CODE(arg[0], arg[1])) {
		case TPIC_CODE('p', 'n'):
		case TPIC_CODE('p', 'a'):
		case TPIC_CODE('s', 'p'):
		case TPIC_CODE('d', 'a'):
		case TPIC_CODE('d', 't'):
		case TPIC_CODE('i', 'p'):
		case TPIC_CODE('f', 'p'):
		case TPIC_CODE('i', 'a'):
		case TPIC_CODE('a', 'r'):
		case TPIC_CODE('s', 'h'):
		case TPIC_CODE('w', 'h'):
		case TPIC_CODE('b', 'k'):
			return 1;
		default:
			/* it's not a tpic special */
			return 0;
		}
	}
	switch (TPIC_CODE(arg[0], arg[1])) {
	case TPIC_CODE('p', 'n'):
	{
		long n;
		if (sscanf(arg + 2, "%ld", &n) != 1) {
			message(ERROR, "bad argument to pn special");
			return 1;
		}
		current_pen_size = n;
		break;
	}
	case TPIC_CODE('p', 'a'):
	{
		long x, y;
		if (sscanf(arg + 2, "%ld %ld", &x, &y) != 2) {
			message(ERROR, "bad arguments to pa special");
			return 1;
		}
		if (npoints >= maxpoints) {
			if (maxpoints == 0) {
				point_vector = (struct point *)
					malloc((maxpoints = 5)*sizeof(struct point));
				if (point_vector == NULL)
					out_of_memory();
			}
			else {
				point_vector = (struct point *)
					realloc((char *)point_vector,
							sizeof(struct point)*(maxpoints *= 2));
				if (point_vector == NULL)
					out_of_memory();
			}
		}
		point_vector[npoints].x = x;
		point_vector[npoints].y = y;
		npoints++;
		break;
	}
	case TPIC_CODE('s', 'p'):
	{
		float f;
		if (sscanf(arg + 2, "%f", &f) == 1 && f != 0.0) {
			if (f > 0.0) {
				tpic_flags |= DASHED;
				dash_width = f;
			}
			else {
				tpic_flags |= DOTTED;
				dash_width = -f;
			}
		}
		tpic_flags |= SPLINE;
		goto path;
	}
	case TPIC_CODE('d', 'a'):
	{
		float f;
		if (sscanf(arg + 2, "%f", &f) != 1) {
			message(ERROR, "bad argument to da special");
			return 1;
		}
		tpic_flags |= DASHED;
		dash_width = f;
		goto path;
	}
	case TPIC_CODE('d', 't'):
	{
		float f;
		if (sscanf(arg + 2, "%f", &f) != 1) {
			message(ERROR, "bad argument to dt special");
			return 1;
		}
		tpic_flags |= DOTTED;
		dash_width = f;
		goto path;
	}
	case TPIC_CODE('i', 'p'):
		tpic_flags |= INVISIBLE;
		/* fall through */
	case TPIC_CODE('f', 'p'):
	path:
	{
		struct tpic_object *p;
		size_t sz = sizeof(struct tpic_object);
		if (npoints > 2)
			sz += sizeof(struct point)*(npoints - 2);
		p = (struct tpic_object *)malloc(sz);
		if (p == NULL)
			out_of_memory();
		p->flags = tpic_flags;
		if (tpic_flags & (DOTTED|DASHED))
			p->u.p.dash_width = dash_width;
		tpic_flags = 0;
		memcpy((char *)p->u.p.point_vector, (char *)point_vector,
			   npoints*sizeof(struct point));
		p->u.p.npoints = npoints;
		npoints = 0;
		p->next = tpic_object_list;
		tpic_object_list = p;
		complete = 1;
		break;
	}
	case TPIC_CODE('i', 'a'):
		tpic_flags |= INVISIBLE;
		/* fall through */
	case TPIC_CODE('a', 'r'):
	{
		struct tpic_object *p;
		long x, y;
		long rx, ry;
		float s, e;
		if (sscanf(arg + 2, "%ld %ld %ld %ld %f %f",
				   &x, &y, &rx, &ry, &s, &e) != 6) {
			if (arg[0] == 'i')
				message(ERROR, "bad argument to ia special");
			else
				message(ERROR, "bad argument to ar special");
			return 1;
		}
		p = (struct tpic_object *)malloc(sizeof(struct tpic_object));
		if (p == NULL)
			out_of_memory();
		p->flags = ARC|tpic_flags;
		tpic_flags = 0;
		p->u.a.cx = x;
		p->u.a.cy = y;
		p->u.a.rx = rx;
		p->u.a.ry = ry;
		p->u.a.s = s;
		p->u.a.e = e;
		p->next = tpic_object_list;
		tpic_object_list = p;
		complete = 1;
		break;
	}
	case TPIC_CODE('s', 'h'):
	{
		float f;
		if (sscanf(arg + 2, "%f", &f) != 1)
			f = 0.5;
		tpic_flags |= SHADED;
		shade = f;
		break;
	}
	case TPIC_CODE('w', 'h'):
		tpic_flags |= SHADED;
    	shade = 0.0;
		break;
	case TPIC_CODE('b', 'k'):
		tpic_flags |= SHADED;
	    shade = 1.0;
		break;
	default:
	    /* it's not a tpic special */
		return 0;
	}
	if (complete) {
		/* we just completed an object, so fill in some details */
		tpic_object_list->h = x;
		tpic_object_list->v = y;
		tpic_object_list->region = current_region;
		tpic_object_list->pen_size = current_pen_size;
		if (tpic_object_list->flags & DOTTED)
			tpic_object_list->pen_size += DOTTED_LINE_WIDTH_ADDITION;
		if (tpic_object_list->flags & SHADED)
			tpic_object_list->shade = shade;
	}
	return 1;
}

#define round(d) ((long)((d) + 0.5))


void p_tpic_list(psfp, page)
FILE *psfp;
struct page_info *page;
{
	int r = NO_REGION;
	integer ox = 0, oy = 0;
	integer ps = -1;
	double sc = (254.0*page->den)/page->num;
	struct tpic_object *p = tpic_object_list;
	if (p == NULL)
		return;
	/* reverse tpic_object_list */
	tpic_object_list = NULL;
	while (p != NULL) {
		struct tpic_object *tem = p;
		p = p->next;
		tem->next = tpic_object_list;
		tpic_object_list = tem;
	}
	fputs("BO TP\n", psfp);
	while (tpic_object_list != NULL) {
		p = tpic_object_list;
		if (p->region != r) {
			if (r != NO_REGION)
				fputs("GR\n", psfp);
			if (p->region != NO_REGION) {
				fputs("GS\n", psfp);
				do_transform(p->region, &ox, &oy, psfp);
			}
			else
				ox = oy = 0;
			r = p->region;
		}
		p->h -= ox;
		p->v -= oy;
		if (p->pen_size != ps) {
			ps = p->pen_size;
			put_dim(round(ps*sc), psfp);
			fputs(" LW\n", psfp);
		}
		if (p->flags & ARC) {
			integer cx, cy, rx, ry;
			double s, e;
			cx = p->u.a.cx;
			cy = p->u.a.cy;
			rx = p->u.a.rx;
			ry = p->u.a.ry;
			s = p->u.a.s;
			e = p->u.a.e;
			if (e - s >= M_PI*2.0) {
				if (p->flags & SHADED) {
					if (p->shade != 1.0)
						fprintf(psfp, "%g SG\n", 1 - p->shade);
					draw_ellipse(psfp, sc, p->h, p->v, cx, cy, rx, ry);
					fputs("FI\n", psfp);
					if (p->shade != 1.0)
						fputs("0 SG\n", psfp);
				}
				if (!(p->flags & INVISIBLE)) {
					draw_ellipse(psfp, sc, p->h, p->v, cx, cy, rx, ry);
					fputs("ST\n", psfp);
				}
			}
			else {
				if (rx != ry)
					message(WARNING, "open arc with rx != ry");
				draw_arc(psfp, sc, p->h, p->v, cx, cy, rx, s, e);
				fputs("ST\n", psfp);
			}
		}
		else {
			int np = p->u.p.npoints;
			struct point *pv = p->u.p.point_vector;
			double dw = p->u.p.dash_width*1000.0;
			if (p->flags & SPLINE) {
				if (p->flags & DASHED) {
					put_dim(round(sc*dw), psfp);
					putc(' ', psfp);
					put_dim(round(sc*dw), psfp);
					fputs(" DH\n", psfp);
				}
				else if (p->flags & DOTTED) {
					fputs("0 ", psfp);
					put_dim(round(sc*dw), psfp);
					fputs(" DH\n", psfp);
				}
				draw_spline(psfp, sc, p->h, p->v, pv, np);
				fputs("ST\n", psfp);
				if (p->flags & (DOTTED|DASHED))
					fputs("SO\n", psfp);
			}
			else {
				int closed = (np >= 2
							  && pv[0].x == pv[np - 1].x
							  && pv[0].y == pv[np - 1].y);
				if (p->flags & SHADED) {
					if (p->shade != 1.0)
						fprintf(psfp, "%g SG\n", 1 - p->shade);
					draw_path(psfp, sc, p->h, p->v, pv, np - (closed != 0), 1);
					fputs("FI\n", psfp);
					if (p->shade != 1.0)
						fputs("0 SG\n", psfp);
				}
				if (!(p->flags & INVISIBLE)) {
					if (p->flags & DOTTED) {
						int i;
						for (i = 0; i < np - 1; i++) {
							double x = pv[i+1].x - pv[i].x;
							double y = pv[i+1].y - pv[i].y;
							double dist = sqrt(x*x + y*y);
							double adw;
							if (dist <= dw)
								adw = dist;
							else 
								adw = dist/round(dist/dw);
							fputs("0 ", psfp);
							put_dim(round(sc*adw), psfp);
							fputs(" DH\n", psfp);
							draw_path(psfp, sc, p->h, p->v, pv + i, 2, 0);
							fputs("ST\n", psfp);
						}
						fputs("SO\n", psfp);
					}
					else if (p->flags & DASHED) {
						int i;
						for (i = 0; i < np - 1; i++) {
							double x = pv[i+1].x - pv[i].x;
							double y = pv[i+1].y - pv[i].y;
							double dist = sqrt(x*x + y*y);
							double adw, gw;
							if (dist <= dw*2.0) {
								adw = dist;
								gw = 0.0;
							}
							else {
								int ndashes = (int)(ceil((dist - dw)/(dw*2.0)));
								gw = (dist - dw)/ndashes - dw;
								adw = dw;
							}
							put_dim(round(sc*adw), psfp);
							putc(' ', psfp);
							put_dim(round(sc*gw), psfp);
							fputs(" DH\n", psfp);
							draw_path(psfp, sc, p->h, p->v, pv + i, 2, 0);
							fputs("ST\n", psfp);
						}
						fputs("SO\n", psfp);
					}
					else {
						draw_path(psfp, sc, p->h, p->v, pv,
								  np - (closed != 0), closed);
						fputs("ST\n", psfp);
					}
				}
			}
		}
		tpic_object_list = tpic_object_list->next;
		free((char *)p);
	}
	if (r != NO_REGION)
		fputs("GR\n", psfp);
	fputs("EO\n", psfp);
}

static void draw_path(psfp, sc, ox, oy, pv, np, closed)
FILE *psfp;
double sc;
integer ox, oy;
struct point *pv;
int np;
int closed;
{
	int i;
	put_dim(ox + round(pv[0].x*sc), psfp);
	putc(' ', psfp);
	put_dim(oy + round(pv[0].y*sc), psfp);
	fputs(" MT\n", psfp);
	for (i = 1; i < np; i++) {
		put_dim(ox + round(pv[i].x*sc), psfp);
		putc(' ', psfp);
		put_dim(oy + round(pv[i].y*sc), psfp);
		fputs(" LT\n", psfp);
	}
	if (closed)
		fputs("CP\n", psfp);
}

static void draw_spline(psfp, sc, ox, oy, pv, np)
FILE *psfp;
double sc;
integer ox, oy;
struct point *pv;
int np;
{
	int i;
	put_dim(ox + round(pv[0].x*sc), psfp);
	putc(' ', psfp);
	put_dim(oy + round(pv[0].y*sc), psfp);
	fputs(" MT\n", psfp);
	put_dim(ox + round((pv[0].x+pv[1].x)*sc/2.0), psfp);
	putc(' ', psfp);
	put_dim(oy + round((pv[0].y+pv[1].y)*sc/2.0), psfp);
	fputs(" LT\n", psfp);
	for (i = 1; i < np - 1; i++) {
		put_dim(ox + round((pv[i-1].x + 5*pv[i].x)*sc/6.0), psfp);
		putc(' ', psfp);
		put_dim(oy + round((pv[i-1].y + 5*pv[i].y)*sc/6.0), psfp);
		putc(' ', psfp);
		put_dim(ox + round((5*pv[i].x + pv[i+1].x)*sc/6.0), psfp);
		putc(' ', psfp);
		put_dim(oy + round((5*pv[i].y + pv[i+1].y)*sc/6.0), psfp);
		putc(' ', psfp);
		put_dim(ox + round((pv[i].x+pv[i+1].x)*sc/2.0), psfp);
		putc(' ', psfp);
		put_dim(oy + round((pv[i].y+pv[i+1].y)*sc/2.0), psfp);
		fputs(" CT\n", psfp);
	}
	put_dim(ox + round(pv[np - 1].x*sc), psfp);
	putc(' ', psfp);
	put_dim(oy + round(pv[np - 1].y*sc), psfp);
	fputs(" LT\n", psfp);
}

static void draw_ellipse(psfp, sc, ox, oy, cx, cy, rx, ry)
FILE *psfp;
double sc;
integer ox, oy;
integer cx, cy, rx, ry;
{
	put_dim(round(rx*sc), psfp);
	putc(' ', psfp);
	put_dim(round(ry*sc), psfp);
	putc(' ', psfp);
	put_dim(ox + round(sc*cx), psfp);
	putc(' ', psfp);
	put_dim(oy + round(sc*cy), psfp);
	fputs(" EL\n", psfp);
}

static void draw_arc(psfp, sc, ox, oy, cx, cy, r, s, e)
FILE *psfp;
double sc;
integer ox, oy;
integer cx, cy, r;
double s, e;
{
	put_dim(ox + round(sc*cx), psfp);
	putc(' ', psfp);
	put_dim(oy + round(sc*cy), psfp);
	putc(' ', psfp);
	put_dim(round(sc*r), psfp);
	fprintf(psfp, " %g %g AR\n", s*180.0/M_PI, e*180.0/M_PI);
}

#endif

/*
Local Variables:
c-indent-level: 4
c-continued-statement-offset: 4
c-brace-offset: -4
c-argdecl-indent: 0
c-label-offset: -4
tab-width: 4
End:
*/