/* writeps.c - routines to output PostScript representation of object
 *           - using TTDDDLIB by Glenn M. Lewis - 7/23/91
 */

static char rcs_id[] = "$Id: writeps.c,v 1.10 1991/11/01 16:18:54 glewis Exp glewis $";

#include <stdio.h>
#include "ttdddlib.h"

double XSIZE = 8.5;
double YSIZE = 11.0;
#define MARGIN	(0.25)	/* On each side of the page */
#define PPI	(72.0)
#define COS60	(0.5)
#define COS30	(0.8660)

static void write_PS_header();
static void draw_PS_view();
static void render_PS_object();

static FILE *out;
static WORLD *world;
static MBB mbb;
static double xsize, ysize;
static int view;
static double xoffset, yoffset, scale, ybottom;

int write_PostScript(object, file, selected_view)
WORLD *object;
FILE *file;
int selected_view;
{
	double llx, lly;
	register OBJECT *obj;
	if (!(out = file)) return(0);		/* File not open */
	if (!(world = object)) return(0);	/* No object */

	/* Determine the extent of the world object */
	mbb.minx = mbb.miny = mbb.minz =  1.0e10;
	mbb.maxx = mbb.maxy = mbb.maxz = -1.0e10;
	for (obj=world->object; obj; obj=obj->next) {
		calculate_MBB(obj);
		if (!obj->user) continue;
		if (((MBB*)obj->user)->minx<mbb.minx) mbb.minx = ((MBB*)obj->user)->minx;
		if (((MBB*)obj->user)->miny<mbb.miny) mbb.miny = ((MBB*)obj->user)->miny;
		if (((MBB*)obj->user)->minz<mbb.minz) mbb.minz = ((MBB*)obj->user)->minz;
		if (((MBB*)obj->user)->maxx>mbb.maxx) mbb.maxx = ((MBB*)obj->user)->maxx;
		if (((MBB*)obj->user)->maxy>mbb.maxy) mbb.maxy = ((MBB*)obj->user)->maxy;
		if (((MBB*)obj->user)->maxz>mbb.maxz) mbb.maxz = ((MBB*)obj->user)->maxz;
	}
#ifdef DEBUG
	fprintf(stderr, "MBB: (%g,%g,%g)-(%g,%g,%g)\n",
		mbb.minx, mbb.miny, mbb.minz,
		mbb.maxx, mbb.maxy, mbb.maxz);
#endif

	write_PS_header();
	if (selected_view==VIEW_ALL_FOUR) {
		for (view=0; view<4; view++) {
			llx = ((view&0x02) ? XSIZE/2.0 : MARGIN) * PPI;
			lly = ((view&0x01) ? MARGIN : YSIZE/2.0) * PPI;
			xsize = PPI*(XSIZE/2.0 - MARGIN);
			ysize = PPI*(YSIZE/2.0 - MARGIN);
			fprintf(out, "gsave\n0.1 setlinewidth\n%g %g translate\n", llx, lly);
			draw_PS_view();
			fprintf(out, "grestore\n");
		}
	} else {
		xsize = PPI*(XSIZE - 2.0*MARGIN);
		ysize = PPI*(YSIZE - 2.0*MARGIN);
		fprintf(out,"0.1 setlinewidth\n%g %g translate\n",PPI*MARGIN,PPI*MARGIN);
		view=selected_view;
		draw_PS_view();
	}
	fprintf(out, "showpage\n");
	return(1);
}

void calculate_MBB(obj)
OBJECT *obj;
{
	OBJECT *child;
	register XYZ_st *p;
	register int i;
	MBB *mbb;
	double x, y, z;

	if (!obj->desc) return;	/* Not an object */
	if (obj->user) { free((char*)obj->user); obj->user=0; }

	if (!(mbb = (MBB*)malloc(sizeof(MBB)))) {
		fputs("Out of memory", out); exit(-1); }
	mbb->minx = mbb->miny = mbb->minz =  1.0e10;
	mbb->maxx = mbb->maxy = mbb->maxz = -1.0e10;

	/* Account for children... */
	for (child=obj->child; child; child=child->next) {
		calculate_MBB(child);
		if (!child->user) continue;
		if (((MBB*)child->user)->minx < mbb->minx)
			mbb->minx = ((MBB*)child->user)->minx;
		if (((MBB*)child->user)->miny < mbb->miny)
			mbb->miny = ((MBB*)child->user)->miny;
		if (((MBB*)child->user)->minz < mbb->minz)
			mbb->minz = ((MBB*)child->user)->minz;
		if (((MBB*)child->user)->maxx > mbb->maxx)
			mbb->maxx = ((MBB*)child->user)->maxx;
		if (((MBB*)child->user)->maxy > mbb->maxy)
			mbb->maxy = ((MBB*)child->user)->maxy;
		if (((MBB*)child->user)->maxz > mbb->maxz)
			mbb->maxz = ((MBB*)child->user)->maxz;
	}

	x = y = z = 0.0;
/*
	if (obj->desc->posi) {
		x = obj->desc->posi->val[0];
		y = obj->desc->posi->val[1];
		z = obj->desc->posi->val[2];
		fprintf(out, "position: %lx (%g,%g,%g)\n", (unsigned long)obj,x,y,z);
	}
*/
	for (p=obj->desc->pnts,i=obj->desc->pcount; i--; p++) {
		if (p->val[0]-x < mbb->minx) mbb->minx = p->val[0]-x;
		if (p->val[1]-y < mbb->miny) mbb->miny = p->val[1]-y;
		if (p->val[2]-z < mbb->minz) mbb->minz = p->val[2]-z;
		if (p->val[0]-x > mbb->maxx) mbb->maxx = p->val[0]-x;
		if (p->val[1]-y > mbb->maxy) mbb->maxy = p->val[1]-y;
		if (p->val[2]-z > mbb->maxz) mbb->maxz = p->val[2]-z;
	}
	obj->user = (void *)mbb;
#ifdef DEBUG
	fprintf(stderr, "MBB: %lx child:%lx next:%lx (%g,%g,%g)-(%g,%g,%g)\n",
		(unsigned long)obj,
		(unsigned long)obj->child,
		(unsigned long)obj->next,
		mbb->minx, mbb->miny, mbb->minz,
		mbb->maxx, mbb->maxy, mbb->maxz);
#endif
}

static void write_PS_header()
{
	fputs("%!PS-Adobe-2.0 EPSF-1.2\n", out);
	fprintf(out, "%%%%BoundingBox: %g %g %g %g\n",
		PPI*MARGIN, PPI*MARGIN, PPI*(XSIZE-2.0*MARGIN), PPI*(YSIZE-2.0*MARGIN));
	fputs("%%Creator: TTDDDLIB by Glenn M. Lewis\n", out);
	fputs("%%EndComments\n%%BeginSetup\n", out);
	fputs("/M {moveto} bind def\n", out);
	fputs("/D {lineto} bind def\n", out);
	fputs("/S {stroke} bind def\n", out);
	fputs("%%EndSetup\n", out);
}

static void draw_PS_view()
{
	double xratio, yratio, zratio;
	OBJECT *obj;
	fprintf(out, "0 0 M %g 0 D %g %g D 0 %g D 0 0 D S\n",
		xsize, xsize, ysize, ysize);

	if (mbb.maxx-mbb.minx>0.0) xratio=1.0/(mbb.maxx-mbb.minx); else xratio=1.0;
	if (mbb.maxy-mbb.miny>0.0) yratio=1.0/(mbb.maxy-mbb.miny); else yratio=1.0;
	if (mbb.maxz-mbb.minz>0.0) zratio=1.0/(mbb.maxz-mbb.minz); else zratio=1.0;
	scale = (ysize*yratio < xsize*yratio ? ysize*yratio : xsize*yratio);
	scale = (scale        < ysize*zratio ? scale        : ysize*zratio);
	scale = (scale        < xsize*xratio ? scale        : xsize*xratio);

	switch(view) {
		case VIEW_TOP:
			fputs("% Top view\n", out);
			xoffset = (xsize-scale/xratio)/2.0;
			yoffset = (ysize-scale/yratio)/2.0;
			break;
		case VIEW_FRONT:
			fputs("% Front view\n", out);
			xoffset = (xsize-scale/xratio)/2.0;
			yoffset = (ysize-scale/zratio)/2.0;
			break;
		case VIEW_ISO:
			fputs("% Isometric view\n", out);
			xratio = (mbb.maxx-mbb.minx)*COS30+(mbb.maxy-mbb.miny)*COS30;
			if (xratio>0.0) xratio = 1.0/xratio; else xratio=1.0;
			ybottom = -(mbb.maxx-mbb.minx)*COS60;
			yratio = (mbb.maxz-mbb.minz)+(mbb.maxy-mbb.miny)*COS60	/* Top */
					 - ybottom;
			if (yratio>0.0) yratio = 1.0/yratio; else yratio=1.0;
			scale = (xsize*xratio < ysize*yratio ? xsize*xratio : ysize*yratio);
			xoffset = (xsize-scale/xratio)/2.0;
			yoffset = (ysize-scale/yratio)/2.0;
			break;
		case VIEW_RIGHT:
			fputs("% Right view\n", out);
			xoffset = (xsize-scale/yratio)/2.0;
			yoffset = (ysize-scale/zratio)/2.0;
			break;
	}
	for (obj=world->object; obj; obj=obj->next)
		render_PS_object(obj);
}

static void render_PS_object(obj)
register OBJECT *obj;
{
	register UWORD *f;
	register OBJECT *op;
	register int i, j;
	int p[3];
	double x[3], y[3];

	if (!obj->desc) return;
	for (op=obj->child; op; op=op->next)	/* render children first */
		render_PS_object(op);
	
	/* Now, the meat... */
	if (obj->desc->shap && obj->desc->shap[1]!=0) return;	/* A lamp */
	if (!obj->desc->edge || !obj->desc->pnts || !obj->desc->face) return;
	for (f=obj->desc->face,i=obj->desc->fcount; i--; f+=3) {
		p[0] = obj->desc->edge[((*f)<<1)];
		p[1] = obj->desc->edge[((*f)<<1)+1];
		if (obj->desc->edge[((f[1])<<1)] == p[0] ||
			obj->desc->edge[((f[1])<<1)] == p[1])
			p[2] = obj->desc->edge[((f[1])<<1)+1];
		else
			p[2] = obj->desc->edge[((f[1])<<1)];
		switch(view) {
			case VIEW_TOP:
				for (j=3; j--; ) {
					x[j] = obj->desc->pnts[p[j]].val[0]-mbb.minx;
					y[j] = obj->desc->pnts[p[j]].val[1]-mbb.miny;
				}
				break;
			case VIEW_FRONT:
				for (j=3; j--; ) {
					x[j] = obj->desc->pnts[p[j]].val[0]-mbb.minx;
					y[j] = obj->desc->pnts[p[j]].val[2]-mbb.minz;
				}
				break;
			case VIEW_ISO:
				for (j=3; j--; ) {
					x[j] = (obj->desc->pnts[p[j]].val[0]-mbb.minx)*COS30
						  +(obj->desc->pnts[p[j]].val[1]-mbb.miny)*COS30;
					y[j] = (obj->desc->pnts[p[j]].val[2]-mbb.minz)
						  -(obj->desc->pnts[p[j]].val[0]-mbb.minx)*COS60
						  +(obj->desc->pnts[p[j]].val[1]-mbb.miny)*COS60
						  - ybottom;
				}
				break;
			case VIEW_RIGHT:
				for (j=3; j--; ) {
					x[j] = obj->desc->pnts[p[j]].val[1]-mbb.miny;
					y[j] = obj->desc->pnts[p[j]].val[2]-mbb.minz;
				}
				break;
		}
		if (x[0]==x[1] && x[1]==x[2] && y[0]==y[1] && y[1]==y[2]) continue;
		fprintf(out, "%0.2lf %0.2lf M ", xoffset+scale*x[0],yoffset+scale*y[0]);
		if (!(x[0]==x[1] && y[0]==y[1])) {
			fprintf(out, "%0.2lf %0.2lf D ",
				xoffset+scale*x[1], yoffset+scale*y[1]);
		}
		if (!(x[0]==x[2] && y[0]==y[2]) && !(x[1]==x[2] && y[1]==y[2])) {
			fprintf(out, "%0.2lf %0.2lf D ",
				xoffset+scale*x[2], yoffset+scale*y[2]);
		}
		fprintf(out, "%0.2lf %0.2lf D S\n",
			xoffset+scale*x[0],yoffset+scale*y[0]);
	}
}
