/*****************************************************************************
* Filter to convert IRIT data files to a PostScript file.		     *
******************************************************************************
* (C) Gershon Elber, Technion, Israel Institute of Technology                *
******************************************************************************
* Written by:  Gershon Elber				Ver 1.0, Apr 1992    *
*****************************************************************************/

#include <stdio.h>
#include <math.h>
#include <string.h>
#include "irit_sm.h"
#include "iritprsr.h"
#include "allocate.h"
#include "attribut.h"
#include "iritgrap.h"
#include "geomat3d.h"
#include "getarg.h"
#include "bbox.h"
#include "genmat.h"
#include "ffcnvrt.h"
#include "ip_cnvrt.h"

#define DEFAULT_LINE_WIDTH		0.004
#define DEFAULT_SIZE			7.0
#define DEFAULT_DEPTH_CUE_CRV_SIZE	1.0
#define MAX_NUM_OF_PTS			100
#define MIN_PS_LINE_WIDTH		0.002

#define WIDEN_END_START			1
#define WIDEN_END_END			2
#define WIDEN_LN_SUBDIV_FACTOR		0.1

#define DIST_PT_PT_2D(Pt1, Pt2) sqrt(SQR(Pt1[0] - Pt2[0]) + \
				     SQR(Pt1[1] - Pt2[1]))

typedef enum {
    DRAW_POINT_CROSS,
    DRAW_POINT_FULL_CIRC,
    DRAW_POINT_HOLLOW_CIRC
} DrawPointType;

#ifdef NO_CONCAT_STR 
static char *VersionStr =
	"Irit2PS		Version 6.0,	Gershon Elber,\n\
	 (C) Copyright 1989/90-95 Gershon Elber, Non commercial use only.";
#else
static char *VersionStr = "Irit2PS		" VERSION
	",	Gershon Elber,	" __DATE__ ",   " __TIME__ "\n"
	COPYRIGHT ", Non commercial use only.";
#endif /* NO_CONCAT_STR */

static char *CtrlStr =
#ifdef DOUBLE
	"irit2ps l%- 4%- s%-Size!F I%-#UIso[:#VIso[:#WIso]]!s F%-PolyOpti|FineNess!d!F f%-PolyOpti|SampPerCrv!d!d M%- G%- P%- W%-LineWidth!F w%-WidenLen|WidenWidth!F!F b%-R|G|B!d!d!d B%-X1|Y1|X2|Y2!F!F!F!F c%- C%- T%- i%- o%-OutName!s d%-[Zmin|Zmax]%F%F D%-[Zmin|Zmax]%F%F p%-PtType|PtSize!s!F u%- z%- DFiles!*s";
#else
	"irit2ps l%- 4%- s%-Size!f I%-#UIso[:#VIso[:#WIso]]!s F%-PolyOpti|FineNess!d!f f%-PolyOpti|SampPerCrv!d!d M%- G%- P%- W%-LineWidth!f w%-WidenLen|WidenWidth!f!f b%-R|G|B!d!d!d B%-X1|Y1|X2|Y2!f!f!f!f c%- C%- T%- i%- o%-OutName!s d%-[Zmin|Zmax]%f%f D%-[Zmin|Zmax]%f%f p%-PtType|PtSize!s!f u%- z%- DFiles!*s";
#endif /* DOUBLE */

static char
    *GlblPSTitle = NULL,
    *OutFileName = "irit2ps.ps";

static DrawPointType
    GlblDrawPoint = DRAW_POINT_FULL_CIRC;

static int
    GlblColorPS = FALSE,
    GlblBBoxClip = FALSE,
    GlblPointIsCircle = TRUE,
    GlblWidenPolyEnds = FALSE,
    GlblDepthCue = FALSE,
    GlblDepthCueGray = FALSE,
    GlblBackGround = FALSE,
    GlblBackGroundColor[3] = { 0, 0, 0 };

static RealType
    GlblLineWidth = DEFAULT_LINE_WIDTH,
    GlblDepthCueZ[2] = {INFINITY, -INFINITY},
    GlblBBoxClipDomain[4],
    GlblPointScale = 0.01,
    GlblWidenEndLength = 0.1,
    GlblWidenEndWidthScale = 2.0,
    GlblPrintSize = DEFAULT_SIZE;

static MatrixType CrntViewMat;			/* This is the current view! */

static BBBboxStruct
    *GlblBbox = NULL;

static int TransColorTable[][4] = {
    { /* BLACK		*/ 0,    0,   0,   0 },
    { /* BLUE		*/ 1,    0,   0, 255 },
    { /* GREEN		*/ 2,    0, 255,   0 },
    { /* CYAN		*/ 3,    0, 255, 255 },
    { /* RED		*/ 4,  255,   0,   0 },
    { /* MAGENTA 	*/ 5,  255,   0, 255 },
    { /* BROWN		*/ 6,   50,   0,   0 },
    { /* LIGHTGRAY	*/ 7,  127, 127, 127 },
    { /* DARKGRAY	*/ 8,   63,  63,  63 },
    { /* LIGHTBLUE	*/ 9,    0,   0, 255 },
    { /* LIGHTGREEN	*/ 10,   0, 255,   0 },
    { /* LIGHTCYAN	*/ 11,   0, 255, 255 },
    { /* LIGHTRED	*/ 12, 255,   0,   0 },
    { /* LIGHTMAGENTA	*/ 13, 255,   0, 255 },
    { /* YELLOW		*/ 14, 255, 255,   0 },
    { /* WHITE		*/ 15, 255, 255, 255 },
    { /* BROWN		*/ 20,  50,   0,   0 },
    { /* DARKGRAY	*/ 56,  63,  63,  63 },
    { /* LIGHTBLUE	*/ 57,   0,   0, 255 },
    { /* LIGHTGREEN	*/ 58,   0, 255,   0 },
    { /* LIGHTCYAN	*/ 59,   0, 255, 255 },
    { /* LIGHTRED	*/ 60, 255,   0,   0 },
    { /* LIGHTMAGENTA	*/ 61, 255,   0, 255 },
    { /* YELLOW		*/ 62, 255, 255,   0 },
    { /* WHITE		*/ 63, 255, 255, 255 },
    {			   -1,   0,   0,   0 }
};

static RealType RGBtoGray(int R, int G, int B);
static void DumpDataForPostScript(char *FileName, IPObjectStruct *PObjects);
static void DumpAttrState(FILE *f,
			  RealType Width,
			  RealType Gray,
			  RealType *RGBColor,
			  char *Dash);
static void DumpOneObject(FILE *f, IPObjectStruct *PObject);
static void DumpOnePoly(FILE *f,
			IPPolygonStruct *PPolygon,
			int IsPolygon,
			int Fill,
			RealType Gray,
			RealType LineWidth,
			RealType *RGBColor);
static RealType PolylineLength2D(IPVertexStruct *V);
static RealType EdgeLength2D(IPVertexStruct *V);
static void SubdivAndWidenEdge(IPVertexStruct *V,
			       RealType Scale1,
			       RealType Scale2,
			       RealType LineWidth,
			       RealType EdgePortion);
static void DumpOneCubicCurve(FILE *f,
			      CagdCrvStruct *Crv,
			      int Fill,
			      RealType Gray,
			      RealType LineWidth,
			      RealType *RGBColor);
static void DumpOnePoint(FILE *f,
			 RealType *Pt,
			 int IsVector,
			 RealType *RGBColor,
			 RealType Gray);
static void Irit2PsExit(int ExitCode);

/*****************************************************************************
* DESCRIPTION:                                                               M
* Main module of irit2ps - Read command line and do what is needed...	     M
*                                                                            *
* PARAMETERS:                                                                M
*   argc, argv:  Command line.                                               M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   main                                                                     M
*****************************************************************************/
void main(int argc, char **argv)
{
    int Error,
	DrawFFAsPolylines = TRUE,
	DrawFFAsPolygons = FALSE,
	PrintSizeFlag = FALSE,
	NumOfIsolinesFlag = FALSE,
	SrfFineNessFlag = FALSE,
	CrvOptiPolylineFlag = FALSE,
        LineWidthFlag = FALSE,
	VerFlag = FALSE,
	PointFlag = FALSE,
	ForceUnitMat = FALSE,
	OutFileFlag = FALSE,
	NumFiles = 0;
    char *DrawPoint,
        *StrNumOfIsolines = NULL,
	**FileNames = NULL;
    IPObjectStruct *PObjects;

    if ((Error = GAGetArgs(argc, argv, CtrlStr,
			   &FFCState.LinearOnePolyFlag,
			   &FFCState.FourPerFlat, &PrintSizeFlag,
			   &GlblPrintSize, &NumOfIsolinesFlag,
			   &StrNumOfIsolines, &SrfFineNessFlag,
			   &FFCState.OptimalPolygons,
			   &FFCState.FineNess, &CrvOptiPolylineFlag,
			   &FFCState.OptimalPolylines,
			   &FFCState.SamplesPerCurve,
			   &FFCState.DrawFFMesh, &DrawFFAsPolylines,
			   &DrawFFAsPolygons, &LineWidthFlag,
			   &GlblLineWidth, &GlblWidenPolyEnds,
			   &GlblWidenEndLength,
			   &GlblWidenEndWidthScale, &GlblBackGround,
			   &GlblBackGroundColor[0],
			   &GlblBackGroundColor[1],
			   &GlblBackGroundColor[2], &GlblBBoxClip,
			   &GlblBBoxClipDomain[0], &GlblBBoxClipDomain[1],
			   &GlblBBoxClipDomain[2], &GlblBBoxClipDomain[3],
			   &GlblColorPS, &FFCState.CubicCrvsAprox,
			   &FFCState.Talkative,
			   &FFCState.ShowInternal, &OutFileFlag,
			   &OutFileName, &GlblDepthCue,
			   &GlblDepthCueZ[0], &GlblDepthCueZ[1],
			   &GlblDepthCueGray, &GlblDepthCueZ[0],
			   &GlblDepthCueZ[1], &PointFlag, &DrawPoint,
			   &GlblPointScale, &ForceUnitMat, &VerFlag,
			   &NumFiles, &FileNames)) != 0) {
	GAPrintErrMsg(Error);
	GAPrintHowTo(CtrlStr);
	Irit2PsExit(1);
    }

    if (VerFlag) {
	fprintf(stderr, "\n%s\n\n", VersionStr);
	GAPrintHowTo(CtrlStr);
	Irit2PsExit(0);
    }

    if( DrawFFAsPolylines && DrawFFAsPolygons) {
	fprintf(stderr, "Cannot used both -G and -P.\n");
	Irit2PsExit(1);
    }
    if (DrawFFAsPolylines) {
	FFCState.DrawFFGeom = TRUE;
	FFCState.DumpObjsAsPolylines = TRUE;
    }
    else if (DrawFFAsPolygons) {
	FFCState.DrawFFGeom = TRUE;
	FFCState.DumpObjsAsPolylines = FALSE;
    }
    else {
	FFCState.DrawFFGeom = FALSE;
    }

    if (!NumFiles) {
	fprintf(stderr, "No data file names were given, exit.\n");
	GAPrintHowTo(CtrlStr);
	Irit2PsExit(1);
    }
    GlblPSTitle = FileNames[0];

    if (GlblDepthCueGray) {
	if (GlblDepthCue) {
	    fprintf(stderr, "Only one of '-d' or '-D' at once please, exit.\n");
	    GAPrintHowTo(CtrlStr);
	    Irit2PsExit(1);
	}
	GlblDepthCue = TRUE;
    }
    if (GlblWidenPolyEnds && !GlblDepthCue) {
	fprintf(stderr, "If '-w' is set, either '-d' or -'D' must be set as well.\n");
	Irit2PsExit(1);
    }

    if (PointFlag) {
	switch (DrawPoint[0]) {
	    case 'h':
	    case 'H':
	        GlblDrawPoint = DRAW_POINT_HOLLOW_CIRC;
		break;
	    case 'f':
	    case 'F':
	        GlblDrawPoint = DRAW_POINT_FULL_CIRC;
		break;
	    case 'c':
	    case 'C':
	        GlblDrawPoint = DRAW_POINT_CROSS;
		break;
	    default:
		fprintf(stderr, "Point type drawing supported: H)ollowed circle, F)ull circle, C)ross.\n");
		GAPrintHowTo(CtrlStr);
		Irit2PsExit(1);
	}
    }

    if (GlblPointScale < 0.0) {
	GlblPointIsCircle = FALSE;
	GlblPointScale = -GlblPointScale;
    }

    if (NumOfIsolinesFlag && StrNumOfIsolines != NULL) {
	if (sscanf(StrNumOfIsolines, "%d:%d:%d",
		   &FFCState.NumOfIsolines[0],
		   &FFCState.NumOfIsolines[1],
		   &FFCState.NumOfIsolines[2]) != 3) {
	    if (sscanf(StrNumOfIsolines, "%d:%d",
		       &FFCState.NumOfIsolines[0],
		       &FFCState.NumOfIsolines[1]) != 2) {
		if (sscanf(StrNumOfIsolines, "%d",
			   &FFCState.NumOfIsolines[1]) != 1) {
		    fprintf(stderr,
			    "Number(s) of isolines (-I) cannot be parsed.\n");
		    GAPrintHowTo(CtrlStr);
		    Irit2PsExit(1);
		}
		else {
		    FFCState.NumOfIsolines[2] =
			FFCState.NumOfIsolines[1] =
			    FFCState.NumOfIsolines[0];
		}
	    }
	    else {
		FFCState.NumOfIsolines[2] = FFCState.NumOfIsolines[0];
	    }

	}
    }

    FFCState.BBoxGrid = TRUE;

    /* Get the data files: */
    IritPrsrSetFlattenObjects(TRUE);
    if ((PObjects = IritPrsrGetDataFiles(FileNames, NumFiles, TRUE, FALSE)) ==
									NULL)
	Irit2PsExit(1);

    if (!ForceUnitMat) {
	IPObjectStruct
	    *PTmpObjs = PObjects;

	if (IritPrsrWasPrspMat)
	    MatMultTwo4by4(CrntViewMat, IritPrsrViewMat, IritPrsrPrspMat);
	else
	    GEN_COPY(CrntViewMat, IritPrsrViewMat, sizeof(MatrixType));

	PObjects = GMTransformObjectList(PTmpObjs, CrntViewMat);
	IPFreeObjectList(PTmpObjs);
    }

    GlblBbox = BBComputeBboxObjectList(PObjects);

    if (GlblDepthCue) {
	if (GlblDepthCueZ[0] == INFINITY ||
	    GlblDepthCueZ[1] == -INFINITY ||
	    APX_EQ(GlblDepthCueZ[0], GlblDepthCueZ[1])) {

	    GlblDepthCueZ[0] = GlblBbox -> Min[2];
	    GlblDepthCueZ[1] = GlblBbox -> Max[2];
	}
	fprintf(stderr, "Z Depth Cue Bounding Box is [%f...%f]\n",
		GlblDepthCueZ[0], GlblDepthCueZ[1]);
    }

    DumpDataForPostScript(OutFileFlag ? OutFileName : NULL, PObjects);

    Irit2PsExit(0);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Converts RGB to a gray level value.					     *
*                                                                            *
* PARAMETERS:                                                                *
*   R, G, B:  Color coefficients to convert to a gray level.                 *
*                                                                            *
* RETURN VALUE:                                                              *
*   RealType:   Gray level, normalized to be between zero and one.           *
*****************************************************************************/
static RealType RGBtoGray(int R, int G, int B)
{
    return (R * 0.3 + G * 0.59 + B * 0.11) / 255.0;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Dumps the data for Postscript into FileName (stdout in NULL).		     *
*                                                                            *
* PARAMETERS:                                                                *
*   FileName:   Where output should go to.                                   *
*   PObjects:   To dump into file.                                           *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void DumpDataForPostScript(char *FileName, IPObjectStruct *PObjects)
{
    static char *PSHeader[] = {
	"%!PS-Adobe-2.0 EPSF-1.2",
	"BBOX",
	"TITLE",
	"%%Creator: irit2ps",
	"DATE",
	"%%EndComments",
	"",
	"% This file was automatically created from IRIT solid modeller data",
	"% using Irit2ps - IRIT to PostScript filter.",
	"",
	"gsave",
	"",
	"72 72 scale",	/* First scale to inches, se we can speak in inches. */
	"TRANSFORM",
	"",
	"/pl {",
	"    newpath",
	"    moveto",
        "    {counttomark 0 ne {lineto} {exit} ifelse} loop",
	"    pop % the mark",
	"    stroke",
	"} def",
	"",
	"/pf {",
	"    newpath",
	"    moveto",
        "    {counttomark 0 ne {lineto} {exit} ifelse} loop",
	"    pop % the mark",
	"    fill",
	"} def",
	"",
	"/cl {",
	"    newpath",
	"    moveto",
        "    curveto",
	"    stroke",
	"} def",
	"",
	"/cf {",
	"    newpath",
	"    moveto",
        "    curveto",
	"    fill",
	"} def",
	"",
	"/ln { % Line",
	"    newpath",
	"    moveto",
	"    lineto",
	"    stroke",
	"} def",
	"",
	"/lw { % Line with Width control",
	"    newpath",
	"    setlinewidth",
	"    moveto",
	"    lineto",
	"    stroke",
	"} def",
	"",
	"/ldg { % Line with Depth control - Gray",
	"    newpath",
	"    setgray",
	"    moveto",
	"    lineto",
	"    stroke",
	"} def",
	"",
	"/ldc { % Line with Depth control - Color",
	"    newpath",
	"    setrgbcolor",
	"    moveto",
	"    lineto",
	"    stroke",
	"} def",
	"",
	"/cw { % Curve with Width control",
	"    newpath",
	"    setlinewidth",
	"    moveto",
	"    curveto",
	"    stroke",
	"} def",
	"",
	"/cdg { % Curve with Width control - Gray",
	"    newpath",
	"    setgray",
	"    moveto",
	"    curveto",
	"    stroke",
	"} def",
	"",
	"/cdc { % Curve with Width control - Color",
	"    newpath",
	"    setrgbcolor",
	"    moveto",
	"    curveto",
	"    stroke",
	"} def",
	"",
	"1 setlinecap",
	"1 setlinejoin",
	"WIDTH",
	"",
	NULL
    };
    static char *PSTrailer[] = {
	"",
	"showpage",
	"grestore",
	NULL
    };
    FILE *f;
    RealType
	PrintWidth = GlblPrintSize,
	PrintHeight = GlblPrintSize;
    int i,
	IPrintCntrX = (int) (72.0 * 4.0),
	IPrintCntrY = (int) (72.0 * (1.0 + PrintHeight / 2.0)),
	IPrintWidth2 = (int) (72.0 * PrintWidth / 2),
	IPrintHeight2 = (int) (72.0 * PrintHeight / 2),
	IPrintLeft = IPrintCntrX + ((int) (IPrintWidth2 * GlblBbox -> Min[0])),
	IPrintRight = IPrintCntrX + ((int) (IPrintWidth2 * GlblBbox -> Max[0])),
	IPrintBot = IPrintCntrY + ((int) (IPrintHeight2 * GlblBbox -> Min[1])),
	IPrintTop = IPrintCntrY + ((int) (IPrintHeight2 * GlblBbox -> Max[1]));

    if (FileName != NULL) {
	if ((f = fopen(FileName, "w")) == NULL) {
	    fprintf(stderr, "Failed to open \"%s\".\n", FileName);
	    Irit2PsExit(2);
	}
    }
    else
	f = stdout;

    for (i = 0; PSHeader[i] != NULL; i++) {
	if (strcmp(PSHeader[i], "BBOX") == 0)
	    fprintf(f, "%%%%BoundingBox: %d %d %d %d\n",
		    IPrintLeft, IPrintBot, IPrintRight, IPrintTop);
	else if (strcmp(PSHeader[i], "DATE") == 0) {
	    fprintf(f, "%%%%CreationDate: %s\n", IritRealTimeDate());
	}
	else if (strcmp(PSHeader[i], "TITLE") == 0)
	    fprintf(f, "%%%%Title: %s\n", GlblPSTitle);
	else if (strcmp(PSHeader[i], "TRANSFORM") == 0) {
	    fprintf(f, "4.0 %f translate\t\t%% center image\n",
		    1.0 + PrintHeight / 2.0);
	    fprintf(f, "%f %f scale\t\t%% maps -1..1 to this domain\n",
		    PrintWidth / 2.0, PrintHeight / 2.0);
	}
	else if (strcmp(PSHeader[i], "WIDTH") == 0)
	    fprintf(f, "%f setlinewidth\n",
		    GlblLineWidth > 0.0 ? GlblLineWidth
					: -GlblLineWidth * DEFAULT_LINE_WIDTH);
	else
	    fprintf(f, "%s\n", PSHeader[i]);
    }

    if (GlblBackGround) {
	if (GlblColorPS)
	    fprintf(f, "newpath\n%f %f %f setrgbcolor\n",
		    GlblBackGroundColor[0] / 255.0,
		    GlblBackGroundColor[1] / 255.0,
		    GlblBackGroundColor[2] / 255.0);
	else
	    fprintf(f, "newpath\n%f setgray\n",
		    RGBtoGray(GlblBackGroundColor[0],
			      GlblBackGroundColor[1],
			      GlblBackGroundColor[2]));
	fprintf(f, "-2 -2 moveto\n 2 -2 lineto\n 2  2 lineto\n-2  2 lineto\n-2 -2 lineto\nfill\n");
    }

    if (GlblBBoxClip) {
	fprintf(f, "newpath\n%f %f moveto\n%f %f lineto\n%f %f lineto\n%f %f lineto\n%f %f lineto\nclip\n",
		GlblBBoxClipDomain[0], GlblBBoxClipDomain[1],
		GlblBBoxClipDomain[2], GlblBBoxClipDomain[1],
		GlblBBoxClipDomain[2], GlblBBoxClipDomain[3],
		GlblBBoxClipDomain[0], GlblBBoxClipDomain[3],
		GlblBBoxClipDomain[0], GlblBBoxClipDomain[1]);
    }

    for ( ; PObjects != NULL; PObjects = PObjects -> Pnext) {
	if (IP_IS_FFGEOM_OBJ(PObjects))
	    ProcessFreeForm(PObjects, &FFCState);

	DumpOneObject(f, PObjects);
    }

    for (i = 0; PSTrailer[i] != NULL; i++)
	fprintf(f, "%s\n", PSTrailer[i]);

    fclose(f);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Dumps attribute's state.	                                             *
*                                                                            *
* PARAMETERS:                                                                *
*   f:          File to dumps to.                                            *
*   Width:      Width PS attribute.                                          *
*   Gray:       Gray level PS attribute.                                     *
*   RGBColor:   RGB color PS attribute.                                      *
*   Dash:       Dash/dotted lines PS attribute.                              *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void DumpAttrState(FILE *f,
			  RealType Width,
			  RealType Gray,
			  RealType *RGBColor,
			  char *Dash)
{
    fprintf(f, "%f setlinewidth\n", Width);

    if (Dash != NULL)
	fprintf(f, "%s setdash\n", Dash);
    else
	fprintf(f, "[] 0 setdash\n");

    if (GlblColorPS)
	fprintf(f, "%f %f %f setrgbcolor\n",
		RGBColor[0] / 255.0, RGBColor[1] / 255.0, RGBColor[2] / 255.0);
    else
	fprintf(f, "%f setgray\n", Gray);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Dumps one object PObject to file f.                                        *
*                                                                            *
* PARAMETERS:                                                                *
*   f:            File to dump object to.                                    *
*   PObject:      Object to dump to file f.                                  *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void DumpOneObject(FILE *f, IPObjectStruct *PObject)
{
    static RealType
	RGBWhite[3] = {255.0, 255.0, 255.0};
    int i, j, Red, Green, Blue, Color,
	HasColor = FALSE;
    char *Dash;
    RealType *R, RGBColor[3], Fill, Gray, Width, TubeRatio;
    PointType Pt;
    IPPolygonStruct *PList;
    CagdCrvStruct *CList;

    if ((Width = AttrGetObjectRealAttrib(PObject, "width")) >
							   IP_ATTR_BAD_REAL) {
        if (GlblLineWidth > 0.0)
	    Width = GlblLineWidth;
	else
	    Width = -GlblLineWidth * DEFAULT_LINE_WIDTH;
    }
    else {
	if (GlblLineWidth < 0.0)
	    Width *= -GlblLineWidth;
    }

    Dash = AttrGetObjectStrAttrib(PObject, "dash");

    if ((Fill = AttrGetObjectRealAttrib(PObject, "fill")) > IP_ATTR_BAD_REAL)
	Fill = -1.0;

    if ((Gray = AttrGetObjectRealAttrib(PObject, "gray")) > IP_ATTR_BAD_REAL)
	Gray = 0.0;

    if ((TubeRatio = AttrGetObjectRealAttrib(PObject, "tubular")) >
							IP_ATTR_BAD_REAL)
	TubeRatio = 0.0;

    /* Do not attempt to make tubes on such skiny lines. */
    if (Width < MIN_PS_LINE_WIDTH)
	TubeRatio = 0.0;

    if (GlblColorPS) {
	if (AttrGetObjectRGBColor(PObject, &Red, &Green, &Blue)) {
	    HasColor = TRUE;
	    RGBColor[0] = Red;
	    RGBColor[1] = Green;
	    RGBColor[2] = Blue;
	}
	else if ((Color = AttrGetObjectColor(PObject)) != IP_ATTR_NO_COLOR) {
	    for (i = 0; TransColorTable[i][0] >= 0; i++) {
		if (TransColorTable[i][0] == Color) {
		    HasColor = TRUE;
		    for (j = 0; j < 3; j++)
			RGBColor[j] = TransColorTable[i][j+1];
		    break;
		}
	    }
	}

	if (!HasColor)
	    for (i = 0; i < 3; i++)
		RGBColor[i] = 0.0;
    }

    fprintf(f, "\n%%\n%% Object: %s\n%%\n",
	    PObject -> Name ? PObject ->Name : "Noname");
    DumpAttrState(f, Width, Gray, RGBColor, Dash);

    switch (PObject -> ObjType) {
	case IP_OBJ_POLY:
	    PList = PObject -> U.Pl;

	    while (PList) {
		if (IP_IS_POINTLIST_OBJ(PObject)) {
		    IPVertexStruct *V;

		    for (V = PList -> PVertex; V != NULL; V = V -> Pnext) {
			DumpOnePoint(f, V -> Coord, FALSE, RGBColor, Gray);
			if (V -> Pnext != NULL)
			    DumpAttrState(f, Width, Gray, RGBColor, Dash);
		    }
		}
		else {
		    if (TubeRatio > 0.0)
			DumpAttrState(f, Width, Gray, RGBColor, Dash);

		    DumpOnePoly(f, PList, IP_IS_POLYGON_OBJ(PObject),
				Fill >= 0.0, Gray, Width, RGBColor);

		    if (TubeRatio > 0.0) {
			DumpAttrState(f, Width * TubeRatio, 1.0,
				      RGBWhite, Dash);

			DumpOnePoly(f, PList, IP_IS_POLYGON_OBJ(PObject),
				    Fill >= 0.0, Gray,
				    Width * TubeRatio, RGBColor);
		    }
		}

		PList = PList -> Pnext;
	    }
	    break;
	case IP_OBJ_CURVE:
	    if (FFCState.CubicCrvsAprox) {
		if (FFCState.DrawFFGeom) {
		    CList = PObject -> U.Crvs;

		    while (CList) {
			if (TubeRatio > 0.0)
			    DumpAttrState(f, Width, Gray, RGBColor, Dash);

			DumpOneCubicCurve(f, CList, Fill >= 0.0, Gray, Width,
					  RGBColor);

			if (TubeRatio > 0.0) {
			    DumpAttrState(f, Width * TubeRatio, 1.0,
					  RGBWhite, Dash);

			    DumpOneCubicCurve(f, CList, Fill >= 0.0, Gray,
					      Width * TubeRatio, RGBColor);
			}
			CList = CList -> Pnext;
		    }
	
		}
		break;
	    }
	    break;
	case IP_OBJ_VECTOR:
	    DumpOnePoint(f, PObject -> U.Vec, TRUE, RGBColor, Gray);
	    break;
	case IP_OBJ_CTLPT:
	    R = PObject -> U.CtlPt.Coords;
	    CagdCoercePointTo(Pt, CAGD_PT_E3_TYPE,
			      &R, -1, PObject -> U.CtlPt.PtType);
	    DumpOnePoint(f, Pt, FALSE, RGBColor, Gray);
	    break;
	case IP_OBJ_POINT:
	    DumpOnePoint(f, PObject -> U.Vec, FALSE, RGBColor, Gray);
	default:
	    break;
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Dumps one polygon/line, using global transform CrntViewMat.		     *
*                                                                            *
* PARAMETERS:                                                                *
*   f:            File to dump object to.                                    *
*   PPolygon:     Poly to dump to file f.                                    *
*   IsPolygon:    TRUE for polygon, FALSE otherwise.                         *
*   Fill:         Fill PS attribute.                                         *
*   Gray:         Gray level PS attribute.                                   *
*   LineWidth:    Line width PS attribute.                                   *
*   RGBColor:     RGB color PS attribute.                                    *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void DumpOnePoly(FILE *f,
			IPPolygonStruct *PPolygon,
			int IsPolygon,
			int Fill,
			RealType Gray,
			RealType LineWidth,
			RealType *RGBColor)
{
    int Length, WidenEnd;
    IPVertexStruct
	*VList = PPolygon -> PVertex;

    if (!IsPolygon && GlblWidenPolyEnds) {
	if ((WidenEnd = AttrGetIntAttrib(PPolygon -> Attrs, "widenend")) !=
	       						     IP_ATTR_BAD_INT) {
	    RealType
		PolyLength = PolylineLength2D(PPolygon -> PVertex),
		WidenEndLen = GlblWidenEndLength,
		EndScale = GlblWidenEndWidthScale;

	    if (WidenEndLen > PolyLength / 2.0) {
		EndScale *= (PolyLength / 2.0) / WidenEndLen;
		if (EndScale < 1.0)
		    EndScale = 1.0;
		WidenEndLen = PolyLength / 2.0;
	    }

	    if (WidenEnd & WIDEN_END_START) {
		RealType
		    Scale2 = EndScale,
		    WidenEndLenLeft = WidenEndLen;
		IPVertexStruct
		    *V = VList;

		while (WidenEndLenLeft > 0.0) {
		    RealType
			EdgeLength = EdgeLength2D(V),
			ProcessedLength = MIN(EdgeLength, WidenEndLenLeft),
			Scale1 = Scale2;

		    Scale2 = Scale2 * (WidenEndLenLeft - ProcessedLength) /
							    WidenEndLenLeft;
		    if (Scale2 < 1.0)
			Scale2 = 1.0;

		    SubdivAndWidenEdge(V, Scale1, Scale2, LineWidth,
				       WidenEndLenLeft > EdgeLength ?
				            1.0 :
				            WidenEndLenLeft / EdgeLength);
		    WidenEndLenLeft -= ProcessedLength;

		    V = V -> Pnext;
		}
	    }
	    if (WidenEnd & WIDEN_END_END) {
		RealType
		    Scale2 = EndScale,
		    WidenEndLenLeft = WidenEndLen;
		IPVertexStruct
		    *V = IritPrsrGetPrevVrtx(VList,
					     IritPrsrGetLastVrtx(VList));

		while (WidenEndLenLeft > 0.0) {
		    RealType
			EdgeLength = EdgeLength2D(V),
			ProcessedLength = MIN(EdgeLength, WidenEndLenLeft),
			Scale1 = Scale2;

		    Scale2 = Scale2 * (WidenEndLenLeft - ProcessedLength) /
							    WidenEndLenLeft;
		    if (Scale2 < 1.0)
			Scale2 = 1.0;

		    SubdivAndWidenEdge(V, Scale2, Scale1, LineWidth,
				       WidenEndLenLeft > EdgeLength ?
				            1.0 :
				            WidenEndLenLeft / EdgeLength);
		    WidenEndLenLeft -= ProcessedLength;

		    V = IritPrsrGetPrevVrtx(VList, V);
		}
	    }
	}
	else
	    WidenEnd = 0;
    }

    if (GlblDepthCue) {
	if (VList != NULL && VList -> Pnext != NULL) {
	    RealType Z, Ztemp, Scale;

	    do {
		if (FFCState.ShowInternal || !IP_IS_INTERNAL_VRTX(VList)) {
		    if ((Scale = AttrGetRealAttrib(VList -> Attrs,
					  "widthscale")) > IP_ATTR_BAD_REAL)
			Scale = 1.0;
		    /* Disable rounding of ends. */
		    if (Scale != 1.0)
			fprintf(f, "0 setlinecap\n");

		    Z = VList -> Coord[2];

		    fprintf(f, "%6.4f %6.4f",
			    VList -> Coord[0], VList -> Coord[1]);

		    Ztemp = (VList -> Pnext -> Coord[2] - GlblDepthCueZ[0]) /
			    (GlblDepthCueZ[1] - GlblDepthCueZ[0]);
		    Ztemp = Scale * MAX(Ztemp, EPSILON);
		    if (GlblDepthCueGray) {
			if (GlblColorPS)
			    fprintf(f, " %6.4f %6.4f %6.4f %6.4f %6.4f ldc\n",
				    VList -> Pnext -> Coord[0],
				    VList -> Pnext -> Coord[1],
				    RGBColor[0] * Ztemp / 255.0 + 1.0 - Ztemp,
				    RGBColor[1] * Ztemp / 255.0 + 1.0 - Ztemp,
				    RGBColor[2] * Ztemp / 255.0 + 1.0 - Ztemp);
			else
			    fprintf(f, " %6.4f %6.4f %6.4f ldg\n",
				    VList -> Pnext -> Coord[0],
				    VList -> Pnext -> Coord[1],
				    (1.0 - Gray) * (1.0 - Ztemp) );
		    }
		    else
			fprintf(f, " %6.4f %6.4f %6.4f lw\n",
				VList -> Pnext -> Coord[0],
				VList -> Pnext -> Coord[1],
				LineWidth * Ztemp);

		    /* Enable rounding of ends. */
		    if (Scale != 1.0)
			fprintf(f, "1 setlinecap\n");
		}

		VList = VList -> Pnext;
	    }
	    while (VList -> Pnext != NULL &&
		   VList != PPolygon -> PVertex);

	    if (IsPolygon) {
		if ((Scale = AttrGetRealAttrib(PPolygon -> PVertex -> Attrs,
					  "widthscale")) > IP_ATTR_BAD_REAL)
			Scale = 1.0;

		fprintf(f, "%6.4f %6.4f",
			VList -> Coord[0], VList -> Coord[1]);

		Ztemp = (PPolygon -> PVertex -> Coord[2] - GlblDepthCueZ[0]) /
			(GlblDepthCueZ[1] - GlblDepthCueZ[0]);
		Ztemp = Scale * MAX(Ztemp, EPSILON);
		if (GlblDepthCueGray) {
		    if (GlblColorPS)
			fprintf(f, " %6.4f %6.4f %6.4f %6.4f %6.4f ldc\n",
				PPolygon -> PVertex -> Coord[0],
				PPolygon -> PVertex -> Coord[1],
				RGBColor[0] * Ztemp / 255.0 + 1.0 - Ztemp,
				RGBColor[1] * Ztemp / 255.0 + 1.0 - Ztemp,
				RGBColor[2] * Ztemp / 255.0 + 1.0 - Ztemp);
		    else
			fprintf(f, " %6.4f %6.4f %6.4f ldg\n",
				PPolygon -> PVertex -> Coord[0],
				PPolygon -> PVertex -> Coord[1],
				(1.0 - Gray) * (1.0 - Ztemp));
		}
		else
		    fprintf(f, " %6.4f %6.4f %6.4f lw\n",
			    PPolygon -> PVertex -> Coord[0],
			    PPolygon -> PVertex -> Coord[1],
			    LineWidth * Ztemp);
	    }
	}
    }
    else {
	Length = 0;
	do {
	    if ((!FFCState.ShowInternal && IP_IS_INTERNAL_VRTX(VList)) ||
		Length > MAX_NUM_OF_PTS) {
		if (Length > 0) {
		    fprintf(f, "%6.4f %6.4f\n",
			    VList -> Coord[0], VList -> Coord[1]);
		    fprintf(f, Fill ? "pf\n" : "pl\n");
		    Length = 0;
		}
		if (!FFCState.ShowInternal && IP_IS_INTERNAL_VRTX(VList))
		    VList = VList -> Pnext;
	    }
	    else {
		if (Length == 0)
		    fprintf(f, "mark\n");
		fprintf(f, "%6.4f %6.4f\n",
			VList -> Coord[0], VList -> Coord[1]);
		Length++;
		VList = VList -> Pnext;
	    }
	}
	while (VList != NULL && VList != PPolygon -> PVertex);

	if (IsPolygon && Length > 0) {
	    fprintf(f, "%6.4f %6.4f\n",
		    PPolygon -> PVertex -> Coord[0],
		    PPolygon -> PVertex -> Coord[1]);
	}
	if (Length > 0)
	    fprintf(f, Fill ? "pf\n" : "pl\n");
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Computes the length of a polyline in the XY plane.			     *
*                                                                            *
* PARAMETERS:                                                                *
*   V:        First vertex in polyline.					     *
*                                                                            *
* RETURN VALUE:                                                              *
*   RealType:   Length of polyline.                                          *
*****************************************************************************/
static RealType PolylineLength2D(IPVertexStruct *V)
{
    RealType Len = 0.0;

    for ( ; V -> Pnext != NULL; V = V -> Pnext)
	Len += EdgeLength2D(V);

    return Len;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Computes the length of an edge in the XY plane.			     *
*                                                                            *
* PARAMETERS:                                                                *
*   V:         First vertex of edge.                                         *
*                                                                            *
* RETURN VALUE:                                                              *
*   RealType:    Distance from V to V -> Pnext.                              *
*****************************************************************************/
static RealType EdgeLength2D(IPVertexStruct *V)
{
    return sqrt(SQR(V -> Pnext -> Coord[0] - V -> Coord[0]) +
		SQR(V -> Pnext -> Coord[1] - V -> Coord[1]));
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Subdivides the EdgePortion from V to V -> Pnext into small segments and    *
* sets their width attribute to be from Scale1 to Scale2 of LineWidth.	     *
*                                                                            *
* PARAMETERS:                                                                *
*   V:               First vertex of edge.                                   *
*   Scale1, Scale2:  Scaling of width from V to V -> Pnext.                  *
*   LineWidth:       Line width expected.                                    *
*   EdgePortion:     1.0 for all the edge, <1.0 for less than full edge.     *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void SubdivAndWidenEdge(IPVertexStruct *V,
			       RealType Scale1,
			       RealType Scale2,
			       RealType LineWidth,
			       RealType EdgePortion)
{
    IPVertexStruct *Vtmp;
    RealType Len,
	SubdivFactor = GlblWidenEndLength * WIDEN_LN_SUBDIV_FACTOR;

    AttrSetRealAttrib(&V -> Attrs, "widthscale", Scale1);

    if (EdgePortion < 1.0) {
	/* Clip edge to make a full edge that needs to be subdivided. */
	Vtmp = IPAllocVertex(0, 0, NULL, V -> Pnext);

	if (Scale1 > Scale2) {
	    /* It is the beginning of the edge. */
	    PT_BLEND(Vtmp -> Coord, V -> Pnext -> Coord, V -> Coord,
		     EdgePortion);
	    V -> Pnext = Vtmp;
	}
	else {
	    /* It is the end of the edge. */
	    PT_BLEND(Vtmp -> Coord, V -> Coord, V -> Pnext -> Coord,
		     EdgePortion);
	    V -> Pnext = Vtmp;
	    V = Vtmp;
	}
    }

    while ((Len = EdgeLength2D(V)) > SubdivFactor) {
	EdgePortion = SubdivFactor / Len;
	Vtmp = IPAllocVertex(0, 0, NULL, V -> Pnext);
	PT_BLEND(Vtmp -> Coord, V -> Pnext -> Coord, V -> Coord, EdgePortion);
	V -> Pnext = Vtmp;
 	V = Vtmp;
	Scale1 = Scale1 * (1.0 - EdgePortion) + Scale2 * EdgePortion;
	AttrSetRealAttrib(&Vtmp -> Attrs, "widthscale", Scale1);
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Dumps one curve, using global transform CrntViewMat.                       *
*                                                                            *
* PARAMETERS:                                                                *
*   f:            File to dump object to.                                    *
*   Crv:	  Curve to dump to file f.                                   *
*   Fill:         Fill PS attribute.                                         *
*   Gray:         Gray level PS attribute.                                   *
*   LineWidth:    Line width PS attribute.                                   *
*   RGBColor:     RGB color PS attribute.                                    *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void DumpOneCubicCurve(FILE *f,
			      CagdCrvStruct *Crv,
			      int Fill,
			      RealType Gray,
			      RealType LineWidth,
			      RealType *RGBColor)
{
    CagdBType
	NewCrv = FALSE;
    int i, j;
    PointType Pt;
    CagdRType **Points;

    if (Crv ->  PType != CAGD_PT_E3_TYPE) {
	Crv = CagdCoerceCrvTo(Crv, CAGD_PT_E3_TYPE);
	NewCrv = TRUE;
    }
    Points = Crv -> Points;

    if (GlblDepthCue) {
	RealType Z, Ztemp;

	for (j = 1; j < 4; j++)
	    fprintf(f, "%6.4f %6.4f ", Points[1][j], Points[2][j]);
	for (i = 0; i < 3; i++)
	    Pt[i] = Points[i+1][0];
	fprintf(f, "%6.4f %6.4f ", Points[1][0], Points[2][0]);
	Z = Points[2][0];

	Ztemp = (Z - GlblDepthCueZ[0]) /
		(GlblDepthCueZ[1] - GlblDepthCueZ[0]);
	Ztemp = MAX((1.0 - Ztemp), EPSILON);

	if (GlblDepthCueGray) {
	    if (GlblColorPS)
		fprintf(f, "%6.4f %6.4f %6.4f cdc\n",
			RGBColor[0] * Ztemp / 255.0 + 1.0 - Ztemp,
			RGBColor[1] * Ztemp / 255.0 + 1.0 - Ztemp,
			RGBColor[2] * Ztemp / 255.0 + 1.0 - Ztemp);
	    else
		fprintf(f, "%6.4f cdg\n", (1.0 - Gray) * (1.0 - Ztemp));
	}
	else
	    fprintf(f, "%6.4f cw\n", LineWidth * Ztemp);
    }
    else {
	for (j = 1; j < 4; j++)
	    fprintf(f, "%6.4f %6.4f ", Points[1][j], Points[2][j]);
	fprintf(f, "%6.4f %6.4f ", Points[1][0], Points[2][0]);
	fprintf(f, Fill ? "cf\n" : "cl\n");
    }

    if (NewCrv)
	CagdCrvFree(Crv);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Dumps one point, using global transform CrntViewMat.                       *
*                                                                            *
* PARAMETERS:                                                                *
*   f:            File to dump object to.                                    *
*   Pt:	  	  Point to dump to file f.                                   *
*   IsVector:	  If TRUE, dumps a line to origin.                           *
*   RGBColor:     RGB color PS attribute.                                    *
*   Gray:         Gray level PS attribute.                                   *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void DumpOnePoint(FILE *f,
			 RealType *Pt,
			 int IsVector,
			 RealType *RGBColor,
			 RealType Gray)
{
    RealType
	Z = Pt[2],
	Ztemp = (Z - GlblDepthCueZ[0]) / (GlblDepthCueZ[1] - GlblDepthCueZ[0]),
	Size = GlblDepthCue ? GlblPointScale * MAX(Ztemp, EPSILON)
			    : GlblPointScale;

    if (GlblDepthCueGray) {
	if (GlblColorPS)
	    fprintf(f, "%f %f %f setrgbcolor\n",
		    RGBColor[0] * Ztemp / 255.0 + 1.0 - Ztemp,
		    RGBColor[1] * Ztemp / 255.0 + 1.0 - Ztemp,
		    RGBColor[2] * Ztemp / 255.0 + 1.0 - Ztemp);
	else
	    fprintf(f, "%f setgray\n", (1.0 - Gray) * (1.0 - Ztemp));
    }

    if (IsVector) {
	fprintf(f, "0 0 %6.4f %6.4f 0 lw\n", Pt[0], Pt[1]);
    }

    switch (GlblDrawPoint) {
	case DRAW_POINT_FULL_CIRC:
	    /* Dump a point as a full circle. */
	    fprintf(f, "newpath %6.4f %6.4f %6.4f 0 360 arc fill\n",
		    Pt[0], Pt[1], GlblDepthCueGray ? GlblPointScale : Size); 
	    break;
	case DRAW_POINT_HOLLOW_CIRC:
	    /* Dump a point as a hollowed circle. */
	    fprintf(f, "newpath %6.4f %6.4f %6.4f 0 360 arc fill\n",
		    Pt[0], Pt[1], GlblDepthCueGray ? GlblPointScale : Size); 
	    if (GlblColorPS)
		fprintf(f, "1.0 1.0 1.0 setrgbcolor\n");
	    else
		fprintf(f, "1.0 setgray\n");
	    fprintf(f, "newpath %6.4f %6.4f %6.4f 0 360 arc fill\n",
		    Pt[0], Pt[1], GlblDepthCueGray ? GlblPointScale * 0.7
						   : Size * 0.7); 
	    break;
	case DRAW_POINT_CROSS:
	    /* Dump a point as a cross. */
	    fprintf(f, "%6.4f %6.4f %6.4f %6.4f  %6.4f lw\n",
		    Pt[0] - Size, Pt[1],
		    Pt[0] + Size, Pt[1],
		    GlblDepthCueGray ? GlblPointScale * 0.2 : Size * 0.2);
	    fprintf(f, "%6.4f %6.4f %6.4f %6.4f  %6.4f lw\n",
		    Pt[0], Pt[1] - Size,
		    Pt[0], Pt[1] + Size,
		    GlblDepthCueGray ? GlblPointScale * 0.2 : Size * 0.2);
	    break;
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Irit2Ps exit routine.							     *
*                                                                            *
* PARAMETERS:                                                                *
*   ExitCode:                                                                *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void Irit2PsExit(int ExitCode)
{
    exit(ExitCode);
}

#ifdef DEBUG

/*****************************************************************************
* DESCRIPTION:                                                               *
*    Dummy function to link at debugging time.                               *
*                                                                            *
* PARAMETERS:                                                                *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*                                                                            *
* KEYWORDS:                                                                  *
*****************************************************************************/
void DummyLinkCagdDebug(void)
{
    IritPrsrDbg();
}

#endif /* DEBUG */
