/* 
 * File: process.c
 * Project: DXF to RIB
 *  (c) SPDsoft Tuesday, June 21, 1994, GTIC
 */

/*
 * Looks better at 4 spaces/tab
 * -c -v 0 :files:3.dxf
 * -v 2 -t 1 :files:6.dxf
 */
 
/* 
	 -> x	M,N
	|
	V
	
	y
	         0    1    2       i       M-2   M-1
	    0    +----+----+- ... -+- .... -+----+
	         |    |    |       |        |    |
	    1    +----+----+- ... -+- .... -+----+
	         |    |    |       |        |    |
	    2    +----+----+- ... -+- .... -+----+
	         |    |    |       |        |    |
	         
	    j    +----+----+- ... -+- .... -+----+
	        
	   N-2   +----+----+- ... -+- .... -+----+
	         |    |    |       |        |    |
	   N-1   +----+----+- ... -+- .... -+----+
*/
 
#include <stdio.h>
#include <math.h>
#include <time.h>
#include <stdlib.h> /* abs, in dxf.h */
#include <string.h>

#ifndef SEEK_SET
#	define SEEK_SET    0
#endif

#include "taylor.h"

#include "prefs.h"
#include "errors.h"
#include "colors.h"
#include "dxf.h"
#include "points.h"


#ifndef	FFMT
/*#define	FFMT	"% 1.06f"*/
#define	FFMT	"% g"
#endif

#define	OBJ_OK	0
#define	OBJ_DEG	1
#define OBJ_NI	2
#define OBJ_NIR	4
#define OBJ_NOB	3
#define	OBJ_ERR	5

#define	GetLine(a)	if(0!=(done=getline(a))) goto end; else g_line++
#define	Return(a)	{ done=a; goto ret; }
#define BUFSIZE	256

#define ANG(a)		(a)
#ifndef fatan2
#define fatan2(a,b)	(float)(atan2((double)(a),(double)(b)))
#endif
#ifndef fpow
#define fpow(a,b)	(float)(pow((double)(a),(double)(b)))
#endif

#define	PL_UpM(a)		((a)+1 == PolylineMCount ? 0 : (a)+1 )
#define	PL_DownM(a)		((a)-1 < 0 ? PolylineMCount-1 : (a)-1 )
#define	PL_UpN(a)		((a)+1 == PolylineNCount ? 0 : (a)+1 )
#define	PL_DownN(a)		((a)-1 < 0 ? PolylineNCount-1 : (a)-1 )

static int 	getline(FILE* infile);
static void	cutCR(char *s);
static int	writeobj(FILE* outfile);
static int	checkdegen(int a,int b,int c);

static int	out_p4(FILE* outfile);
static int	out_p3(int i, int j, int k,FILE* outfile);
static void out_AtBegin( FILE* outfile);
			
		

extern char*	g_ProgName;
extern char		g_iname[];
extern char		g_oname[];

int				done;
int				g_open_object;
int				g_at_open;
unsigned long	g_line;

float			*g_vertex_ptr;

int		groupcode;
char	linbuf[BUFSIZE];
long	primitives = 0L,
		degenerates = 0L;
char	curobj[80];
int		curcolor;
float	curthick;
float	xcoords[10];
float	ycoords[10];
float	zcoords[10];
float	floats[10];
float	angles[10];
int		ints[10];
float	max_x, max_y, max_z,
		min_x, min_y, min_z;
		
char	**blockname;
char	curblockname[BUFSIZE];
int		lastblockname;

Point3D *SurfN;


int process(FILE* i_f, FILE* o_f)
{
	time_t	timer;
	char	layer[BUFSIZE];

	/**************************************** RIB header & struct */
	/* The World is My Oyster */

	time(&timer);
	
	fprintf( o_f,
		"##RenderMan RIB-Structure 1.0 Entity\n"
		"##Creator SPDsoft: %s\n"
		"##CreationDate %s"
		"#From DXF file: %s\n"
		"version 3.03\n"
		"AttributeBegin\n",
		g_ProgName,
		ctime(&timer),
		g_iname
		);
		
	done =			0;
	g_open_object=	false;
	g_at_open=		false;
	g_line =		1;
	blockname =		NULL;
	lastblockname =	0;
	
	if(Prefs.shaderf) /* rib declares */
	{
		fseek(Prefs.shaderf,0,SEEK_SET);

		for(	fgets(linbuf, BUFSIZE, Prefs.shaderf);
				!strstr(linbuf, "# LAYER")&&!feof(Prefs.shaderf);
				fgets(linbuf, BUFSIZE, Prefs.shaderf)	)
				
				fputs(linbuf,o_f);
	}
	
	if(!(VertexCoords =
				(float*)calloc((size_t)(Prefs.max_vertex*3),sizeof(float))))
		DoError(SYS_ERR,ABORT_ERR, "Not enough memory for: %d vertex, use -M",
								Prefs.max_vertex );
										
	if(Prefs.phong)
	if(!(SurfN =
				(Point3D *)calloc((size_t)(Prefs.max_vertex*2),sizeof(Point3D))))
		DoError(SYS_ERR,ABORT_ERR, "Not enough memory for: %d normals, use -M",
								Prefs.max_vertex*2 );
								
	curobj[0] = '\0';	/* not working on any object currently */
	curcolor = 7;		/* and it also doesn't have a color yet... */
	max_x = max_y = max_z = MINF;	/* init bounding limits */
	min_x = min_y = min_z = MAXF;
	
	
find:
	while (!feof(i_f))	/* run file up to the "ENTITIES" section */
	{
		GetLine(i_f);	/* get a group code and a line */

		if (groupcode == 0)	/* file section mark */
		{
			if (strstr(linbuf, "EOF"))
				goto end;
			if (strstr(linbuf, "SECTION"))
			{
				GetLine(i_f);
				
				if (groupcode != 2)
					continue;
				if (strstr(linbuf, "ENTITIES"))
					break;
				if (strstr(linbuf, "BLOCKS"))
				{
					if(!(blockname =
					(char**)calloc((size_t)(Prefs.max_blocks),sizeof(char*))))
						DoError(	SYS_ERR,ABORT_ERR,
									"Not enough memory for: %d blocks, use -B",
									Prefs.max_blocks );

					break;
				}
			}
		}
		else if (groupcode == 999)
		{
				fprintf( o_f,"# %s",linbuf);
		}
	}

	while (!feof(i_f))		/* scan ENTITIES or BLOCKS section */
	{
		GetLine(i_f);		/* get a group code and a line */
		
		if (groupcode < 10)	/* cardinal group codes */
		{
			switch(groupcode)
			{
				case 0:
					/* start of entity, table, file sep */
					
					if (strstr(linbuf, "EOF"))
					{
						if (curobj[0]!='\0')
							writeobj(o_f);		/* dump object */
						goto end;
					}
					
					if (strstr(linbuf, "ENDSEC"))
					{
						if (curobj[0]!='\0')
							writeobj(o_f);		/* dump object */
							curobj[0] = '\0';	/* reset object */
						goto find;
					}
					
					if (curobj[0]!='\0')
						writeobj(o_f);			/* dump old object */
					curobj[0] = '\0';			/* reset object */
					
					curcolor = 7;
					strcpy(curobj, linbuf);		/* get new */
					cutCR(curobj);
					break;
					
				case 1:
					/* primary text value for entity (?)*/
					break;
					
				case 2:
					/* block name, attribute tag, etc */
					strcpy(curblockname, linbuf);	/* get new */
					cutCR(curblockname);
					break;
					
				case 3:
					/* other names */
				case 4:
					break;
				
				case 5:
					/* entity handle (hex string) */
					break;
				case 6:
					/* line type name */
					break;
				case 7:
					/* text style name */
					break;
				
				case 8:
				
					/* layer name */
					
					cutCR(linbuf);

					if((Prefs.structured)&&!(g_open_object))
					{
						fprintf( o_f,"# Layer: \"%s\"\n",linbuf);
					}
					
					if((Prefs.shaderf)&&!(g_open_object)) /* rib shader */
					{
						if(0!=strcmp(layer, linbuf))
						{
						strcpy(layer, linbuf);
						fseek(Prefs.shaderf,0,SEEK_SET);
				
						for(	fgets(linbuf, BUFSIZE, Prefs.shaderf);
								!feof(Prefs.shaderf);
								fgets(linbuf, BUFSIZE, Prefs.shaderf)	)
						{
							if ( strstr(linbuf, layer)
								&& strstr(linbuf, "# LAYER")) break;
						}
	
						if(feof(Prefs.shaderf))
						{
							DoError(INPUT_ERR,WARN_ERR,
							"Layer not found: %s, line %ld",
							layer,g_line);
						}
						
						else
						for(	fgets(linbuf, BUFSIZE, Prefs.shaderf);
							!strstr(linbuf, "# LAYER")&&!feof(Prefs.shaderf);
								fgets(linbuf, BUFSIZE, Prefs.shaderf)	)
								
								fputs(linbuf,o_f);
						}
					}

					break;
					
				case 9:
					/* variable name ID (only in header)*/
					break;
			}
		}
		else if (groupcode >= 10 && groupcode < 19) /* Some X coord */
		{
			sscanf(linbuf, "%f", &(xcoords[groupcode-10]));
			if (xcoords[groupcode-10] > max_x)
				max_x = xcoords[groupcode-10];
			if (xcoords[groupcode-10] < min_x)
				min_x = xcoords[groupcode-10];
		}
		else if (groupcode >= 20 && groupcode < 29) /* Some Y coord */
		{
			sscanf(linbuf, "%f", &(ycoords[groupcode-20]));
			if (ycoords[groupcode-20] > max_y)
				max_y = ycoords[groupcode-20];
			if (ycoords[groupcode-20] < min_y)
				min_y = ycoords[groupcode-20];
		}
		else if (groupcode >= 30 && groupcode < 38) /* Some Z coord */
		{
			sscanf(linbuf, "%f", &(zcoords[groupcode-30]));
			if (zcoords[groupcode-30] > max_z)
				max_z = zcoords[groupcode-30];
			if (zcoords[groupcode-30] < min_z)
				min_z = zcoords[groupcode-30];
		}
		else if (groupcode == 38) /* entity elevation if nonzero */
		{
		}
		else if (groupcode == 39) /* entity thickness if nonzero */
		{
		}
		else if (groupcode >= 40 && groupcode < 49) /* misc floats */
		{
			sscanf(linbuf, "%f", &(floats[groupcode-40]));
		}
		else if (groupcode == 49) /* repeated value groups */
		{
		}
		else if (groupcode >= 50 && groupcode < 59) /* misc angles */
		{
			sscanf(linbuf, "%f", &(angles[groupcode-50]));
		}
		else if (groupcode == 62) /* Color number */
		{
			sscanf(linbuf, "%6d", &curcolor);
		}
		else if (groupcode == 66) /* "entities follow" flag */
		{
			/* vertices follow flag, must be 1 */
		}
		else if (groupcode >= 70 && groupcode < 79) /* misc ints */
		{
			sscanf(linbuf, "%d", &(ints[groupcode-70]));
		}
		else if (groupcode == 210 || groupcode == 220 || groupcode == 230)
		{	/* X, Y, Z components of extrusion direction */
		}
	}

end:
		fprintf( o_f,"AttributeEnd\n");
	
	/* -> se acabo __________________________________*/
		
	
	fprintf( o_f,
		"# %ld Primitives\n"
		"# Total degenerate triangles removed from scene: %ld\n"
		"# Bound: %f %f %f %f %f %f\n",
		primitives,
		degenerates,
		min_x, max_x,min_y, max_y, min_z, max_z
	);

	free(VertexCoords);
	
	if(NULL!=blockname)
	{
		for(lastblockname;lastblockname>0;lastblockname--)
			free(*(blockname+lastblockname-1));
		free(blockname);
	}
	return(0);
}

/******************************************************************************/

static int getline(FILE* infile) /* read a group code and the next line */
{
	PeriodicTask(NULL);
	fgets(linbuf, BUFSIZE, infile);		/* get a line from .DXF */
	if (feof(infile))
		return(1);
	sscanf(linbuf, "%3d", &groupcode);	/* scan out group code */
	fgets(linbuf, BUFSIZE, infile);		/* get a line from .DXF */
	if (feof(infile))
		return(2);
	return(0);
}
static void cutCR(char *s)
{
	if (*(s+strlen(s)-1) == '\n' ) *(s+strlen(s)-1) = '\0';
}
static int writeobj(FILE* outfile)
{
	int		done=OBJ_NOB;
	float	p_aux[3], p_f;
	int		i,j;
	char	*p;
	

	
	if (0==strcmp(curobj, "LINE"))		/* 2D line -> cylinder */
	{
		if (	xcoords[0] == xcoords[1] &&
				ycoords[0] == ycoords[1] &&
				zcoords[0] == zcoords[1]	)
		{
			degenerates++;
			Return(OBJ_DEG);
		}

		if(Prefs.radius!=0.0)
		{
			out_AtBegin(outfile);
			
			p_aux[0]=xcoords[1]-xcoords[0];
			p_aux[1]=ycoords[1]-ycoords[0];
			p_aux[1]=zcoords[1]-zcoords[0];
		
			fprintf(outfile, "\t\tTranslate "FFMT" "FFMT" "FFMT"\n",
									xcoords[0],ycoords[0],zcoords[0]);
		
			fprintf(outfile, "\t\tRotate "FFMT" 0.0 0.0 1.0\n",
									-DEGS(fatan2(p_aux[0],p_aux[1])));
		
			fprintf(outfile, "\t\tRotate "FFMT" 1.0 0.0 0.0\n",
									-DEGS(fatan2(fpow(
									p_aux[0]*p_aux[0]+p_aux[1]*p_aux[1],
									0.5),p_aux[2])));

			p_f = 	fpow(
							p_aux[0]*p_aux[0] +
							p_aux[1]*p_aux[1] +
							p_aux[2]*p_aux[2] ,
							0.5
						);
						
			fprintf(outfile, "\t\tCylinder "FFMT" 0.0 "FFMT" 360.0\n",
									Prefs.radius, p_f);

			fprintf(outfile, "\t\tDisk "FFMT" "FFMT" 360.0\n",
									0.0, Prefs.radius);

			fprintf(outfile, "\t\tDisk "FFMT" "FFMT" 360.0\n",
									p_f, Prefs.radius);

			Return(OBJ_OK);
		}
		else
			Return(OBJ_NIR);
	}
	else if (0==strcmp(curobj, "POINT"))	/* an itty, bitty sphere! */
	{
		if(Prefs.radius!=0.0)
		{
			out_AtBegin(outfile);
			
			fprintf(outfile, "\t\tTranslate "FFMT" "FFMT" "FFMT"\n",
									xcoords[0],ycoords[0],zcoords[0]);
			fprintf(outfile, "\t\tSphere "FFMT" "FFMT" "FFMT" 360.0\n",
									Prefs.radius,-Prefs.radius,Prefs.radius);
			Return(OBJ_OK);
		}
		else
			Return(OBJ_NIR);
	}
	else if (0==strcmp(curobj, "CIRCLE"))	/* a torus? */
	{
		if(Prefs.radius!=0.0)
		{
			out_AtBegin(outfile);
			
			fprintf(outfile, "\t\tTranslate "FFMT" "FFMT" "FFMT"\n",
									xcoords[0],ycoords[0],zcoords[0]);
			fprintf(outfile, "\t\tTorus "FFMT" "FFMT" "FFMT" "FFMT" 360.0\n",
									floats[0]+Prefs.radius/2.0,
									floats[0]-Prefs.radius/2.0,
									0.0,360.0);
			Return(OBJ_OK);
		}
		else
			Return(OBJ_NIR);
	}
	else if (0==strcmp(curobj, "ARC"))		/* a torus? -WARNING: open ends- */
	{
		if(Prefs.radius!=0.0)
		{
			out_AtBegin(outfile);
			
			fprintf(outfile, "\t\tTranslate "FFMT" "FFMT" "FFMT"\n",
									xcoords[0],ycoords[0],zcoords[0]);
			fprintf(outfile, "\t\tRotate "FFMT" 0.0 0.0 1.0\n",
									ANG(floats[1]));
									
			fprintf(outfile, "\t\tTorus "FFMT" "FFMT" "FFMT" "FFMT" "FFMT"\n",
									floats[0]+Prefs.radius/2.0,
									floats[0]+Prefs.radius/2.0,
									0.0,360.0,ANG(floats[2]));
			Return(OBJ_OK);
		}
		else
			Return(OBJ_NIR);
	}
	else if (0==strcmp(curobj, "TRACE"))	/* 2 back-to-back triangles */
	{
		out_AtBegin(outfile);
		Return(out_p4(outfile));
	}
	else if (0==strcmp(curobj, "SOLID"))	/* 1 or 2 triangles */
	{
		out_AtBegin(outfile);
		Return(out_p4(outfile));
	}
	else if (0==strcmp(curobj, "TEXT"))	/* not implemented for now */
	{
		Return(OBJ_NI);
	}
	else if (0==strcmp(curobj, "SHAPE"))	/* these look very hard */
	{
		Return(OBJ_NI);
	}
	else if (0==strcmp(curobj, "BLOCK"))	/* ObjectBegin/End */
	{
		if (BL_Anonymous) Return(OBJ_OK);
		
		if(NULL!=blockname)
		{
			if(lastblockname==Prefs.max_blocks)
				DoError(	SYS_ERR,ABORT_ERR,
							"Too many blocks, use -B" );

			if(!(*(blockname+lastblockname)
									 = (char*)malloc((size_t)BUFSIZE)))
				DoError(	SYS_ERR,ABORT_ERR,
							"Not enough memory for another block" );

			lastblockname++;
			
			strcpy(*blockname, curblockname);
			
			fprintf(outfile,"ObjectBegin %d\n",lastblockname);
			if(Prefs.structured)
				fprintf(outfile,"# %s\n",curblockname);
	
			g_open_object=true;
			
			Return(OBJ_OK);
		}
		else
			Return(OBJ_ERR);
	}
	else if (0==strcmp(curobj, "ENDBLK"))	/* ObjectBegin/End */
	{
		
		g_open_object=false;
		fprintf(outfile,"ObjectEnd\n");
		
		Return(OBJ_OK);
	}
	else if (0==strcmp(curobj, "INSERT"))	/* ObjectInstance */
	{
		/* OJO, falta punto de insercion, rotacion y escala */
		
		if(NULL!=blockname)
		{
			for(	p=*blockname,i=0;
					0!=strcmp(p, curblockname) && i<lastblockname;
					p++,i++); 					/* null loop */
			
			if(i==lastblockname) Return(OBJ_ERR);
			
			out_AtBegin(outfile);
			if(Prefs.structured)
				fprintf(outfile,"# %s\n",curblockname);
			fprintf(outfile,"ObjectInstance %d\n",i+1);
	
			Return(OBJ_OK);
		}
		else
			Return(OBJ_ERR);
	}
	else if (0==strcmp(curobj, "ATTDEF"))	/* not implemented for now */
	{
		Return(OBJ_NI);
	}
	else if (0==strcmp(curobj, "ATTRIB"))	/* not implemented for now */
	{
		Return(OBJ_NI);
	}
	else if (0==strcmp(curobj, "POLYLINE"))
	{
		out_AtBegin(outfile);
		
		memcpy(PolylineInts, ints, (size_t)6*sizeof(int));
		g_vertex_ptr=VertexCoords;
	
		g_open_object=true;
		Return(OBJ_OK);
	}
	else if (0==strcmp(curobj, "VERTEX"))
	{
		if(!g_open_object) Return(OBJ_ERR);
		
#if 0
if ( V_IsInCurve ) fprintf(outfile, "V_IsInCurve\m");
if ( V_HasTangent ) fprintf(outfile, "V_HasTangent\m");
if ( V_IsInSpline ) fprintf(outfile, "V_IsInSpline\m");
if ( V_IsSplineFrame ) fprintf(outfile, "V_IsSplineFrame\m");
if ( V_In3DPolyline ) fprintf(outfile, "V_In3DPolyline\m");
if ( V_In3DPolygMesh ) fprintf(outfile, "V_In3DPolygMesh\m");
if ( V_IsInPolyface ) fprintf(outfile, "V_IsInPolyface\m");
#endif

		if ( !V_defsFace )
		{
/*fprintf(outfile,*/
/*"# VERTEX FACE:\t%f\t%f\t%f\n", xcoords[0], ycoords[0],zcoords[0] );*/
			*g_vertex_ptr++ = xcoords[0];
			*g_vertex_ptr++ = ycoords[0];
			*g_vertex_ptr++ = zcoords[0];

			Return(OBJ_OK);
		}
		else
		{	/* temporary, face = 3DFACE */
		
			for(i=0;i<PFACEVMAX ;i++)
			{
				xcoords[i] = *(VertexCoords + (size_t)(VertexFace(i)));
				ycoords[i] = *(VertexCoords + (size_t)(VertexFace(i)+1));
				zcoords[i] = *(VertexCoords + (size_t)(VertexFace(i)+2));
/*fprintf(outfile,*/
/*"# VERTEX 3DFACE:\t%d\t%f\t%f\t%f\n",i, xcoords[i], ycoords[i],zcoords[i] );*/
			}

			Return(out_p4(outfile));
		}

	
	}
	else if (0==strcmp(curobj, "SEQEND"))	/* End of vertex */
	{

		if(!g_open_object)
		{
			DoError(NO_ERR,DEBUG_ERR,
						"Object not open: %s, line %ld", curobj,g_line);
			Return(OBJ_ERR);
		}

		/* por el momento pasando de todo */
		
		
		if(PL_Closed) fprintf(outfile,"# PL_Closed\n");
		if(PL_CurveFit) fprintf(outfile,"# PL_CurveFit\n");
		if(PL_SplineFit) fprintf(outfile,"# PL_SplineFit\n");
		if(PL_3DPolyline) fprintf(outfile,"# PL_3DPolyline\n");
		if(PL_3DPolygMesh) fprintf(outfile,"# PL_3DPolygMesh\n");
		if(PL_ClosedPMesh) fprintf(outfile,"# PL_ClosedPMesh\n");
		if(PL_PolyfaceMesh) fprintf(outfile,"# PL_PolyfaceMesh\n");
		if(PL_Pattern) fprintf(outfile,"# PL_Pattern\n");
		fprintf(outfile,
		"# Polyline Count M,N:\t%d\t%d\n", PolylineMCount, PolylineNCount );
		fprintf(outfile,
		"# Polyline Dens M,N:\t%d\t%d\n", PolylineMDens, PolylineNDens);
		
		switch(PolylineSurf)
		{
			case PM_NoSmooth:
				fprintf(outfile,"# PM_NoSmooth\n");
				break;
			case PM_Quadratic:
				fprintf(outfile,"# PM_Quadratic\n");
				break;
			case PM_BSpline:
				fprintf(outfile,"# PM_BSpline\n");
				break;
			case PM_Bezier:
				fprintf(outfile,"# PM_Bezier\n");
				break;
			default:
				fprintf(outfile,"# PM of unknow type\n");
				break;
		}
		
		if (PL_3DPolyline)
		{
			i=(int)(g_vertex_ptr-VertexCoords);
			
			fprintf(outfile,
				"\t\tGeneralPolygon [%d]\"P\" [\n\t\t", i/3 );
			
			for( g_vertex_ptr=VertexCoords,j=1;j<=i;j++)
				fprintf(outfile, ""FFMT"%s", *g_vertex_ptr++,
				(j%6 == 0) ? "\n\t\t" : " " );
				
			fprintf(outfile,"]\n");
		}
		else if (PL_3DPolygMesh)
		{
			int ii, jj;
			
			int em, en;		/* efective m,n */
			int eiz, ejz;		/* efective zero */
			
			em = PolylineMCount - (PL_Closed ? 0:1);
			en = PolylineNCount - (PL_ClosedPMesh ? 0:1);
			eiz = (PL_Closed ? 0:-1);
			ejz = (PL_ClosedPMesh ? 0:-1);
						
			fprintf(outfile,
				"\t\tPointsPolygons [\n\t\t" );
			
			for( j=1; j<= em*en ; j++)						/* NumberOfPolygons */
				fprintf(outfile, "%d%s", PFACEVMAX,			/* nverts */
				(j%16 == 0) ? "\n\t\t" : " " );
			fprintf(outfile,"] [\n\t\t");

			for(i=ii=0; ii < em; ii++)						/* verts */
				for(jj=0; jj < en; jj++)
				{
					fprintf(outfile, "%0.3d %0.3d %0.3d %0.3d%s",
					VertexIdx(ii,jj),VertexIdx(PL_UpM(ii),jj),
					VertexIdx(PL_UpM(ii),jj+1),VertexIdx(ii,jj+1),
					(i++%2 != 0) ? "\n\t\t" : " " );
				}
			fprintf(outfile,"] ");

			i=(int)(g_vertex_ptr-VertexCoords);				/* points */
			fprintf(outfile, "\"P\" [\n\t\t");
			for( g_vertex_ptr=VertexCoords,j=1;j<=i;j++)
				fprintf(outfile, ""FFMT"%s", *g_vertex_ptr++,
				(j%6 == 0) ? "\n\t\t" : " " );
			fprintf(outfile,"]\n");
			
			if(Prefs.phong)									/* normals */
			{
				Point3D SumPoint[2];
				Point3D ProdPoint;
				float div;
				
				fprintf(outfile, "\t\t\"N\" [\n\t\t");
				
				/* Normals for faces */

				for( ii=0 ; ii<em ; ii++ )
				for( jj=0 ; jj<en ; jj++ )
				{
					ResV(
						(VertexCoords+3*VertexIdx(ii,jj)),
						(VertexCoords+3*VertexIdx(PL_UpM(ii),jj+1)),
						SumPoint[0]
						);
						
					ResV(
						(VertexCoords+3*VertexIdx(PL_UpM(ii),jj)),
						(VertexCoords+3*VertexIdx(ii,jj+1)),
						SumPoint[1]
						);
							
					ProdvV(	SumPoint[0], SumPoint[1], ProdPoint);
					NormV(ProdPoint,*(SurfN+NormIdx(ii,jj)));
				}

				/* Normals for vertex */

				for( j=ii=0; ii <= PolylineMCount-1; ii++ )
				for( jj=0; jj<=PolylineNCount-1; jj++ )
				{
					div=0;
					SumPoint[0][0]=SumPoint[0][1]=SumPoint[0][2]=0.0;

					if ( ( ii < em ) && ( jj < en ) )
					{
						div+=1;
						SumV(SumPoint[0],
						*(SurfN+NormIdx(ii,jj)),SumPoint[0]);
					}
					if ( ( ii < em ) && ( jj > ejz ) )
					{
						div+=1;
						SumV(SumPoint[0],
						*(SurfN+NormIdx(ii,jj==0?en-1:jj-1)),SumPoint[0]);
					}
					
					if ( ( ii > eiz ) && ( jj < en ) )
					{
						div+=1;
						SumV(SumPoint[0],
						*(SurfN+NormIdx(ii==0?em-1:ii-1,jj)),
						SumPoint[0]);
					}
					if ( ( ii > eiz ) && ( jj > ejz ) )
					{
						div+=1;
						SumV(SumPoint[0],
						*(SurfN+NormIdx(ii==0?em-1:ii-1,jj==0?en-1:jj-1)),
						SumPoint[0]);
					}

					if (div==0.0)
					{
						DoError(INPUT_ERR,WARN_ERR,"Error getting normal");
						div=1.0;
					}
					ConsV(1.0/div,SumPoint[0],SumPoint[0]);

					fprintf(outfile, ""FFMT" "FFMT" "FFMT"%s",
						SumPoint[0][0],SumPoint[0][1],SumPoint[0][2],
						(j%2 == 0) ? "\n\t\t" : " " );
				}

				fprintf(outfile,"]\n");
			}		
		}
        if ((PL_Closed)|(PL_CurveFit)|(PL_SplineFit)|
        	(PL_ClosedPMesh)|(PL_PolyfaceMesh)|(PL_Pattern))
		{
			g_open_object=false;
			g_vertex_ptr=VertexCoords;
			Return(OBJ_NI);
		}
		else
		{
			i=(int)(g_vertex_ptr-VertexCoords);
			g_open_object=false;
			g_vertex_ptr=VertexCoords;

			if(Prefs.radius!=0.0)
			{
				out_AtBegin(outfile);
				for( g_vertex_ptr = VertexCoords,j=1; j<=i; j+=3 )
				{
				
				
				p_aux[0]=g_vertex_ptr[3]-g_vertex_ptr[0];
				p_aux[1]=g_vertex_ptr[4]-g_vertex_ptr[1];
				p_aux[2]=g_vertex_ptr[5]-g_vertex_ptr[2];
			
				fprintf(outfile, "\t\tTranslate "FFMT" "FFMT" "FFMT"\n",
							g_vertex_ptr[0],g_vertex_ptr[1],g_vertex_ptr[2]);
			
				fprintf(outfile, "\t\tRotate "FFMT" 0.0 0.0 1.0\n",
										-DEGS(fatan2(p_aux[0],p_aux[1])));
			
				fprintf(outfile, "\t\tRotate "FFMT" 1.0 0.0 0.0\n",
										-DEGS(fatan2(fpow(
										p_aux[0]*p_aux[0]+p_aux[1]*p_aux[1],
										0.5),p_aux[2])));

				p_f = 	fpow(
								p_aux[0]*p_aux[0] +
								p_aux[1]*p_aux[1] +
								p_aux[2]*p_aux[2] ,
								0.5
							);
							
				fprintf(outfile, "\t\tCylinder "FFMT" 0.0 "FFMT" 360.0\n",
										Prefs.radius, p_f);

				fprintf(outfile, "\t\tDisk "FFMT" "FFMT" 360.0\n",
										0.0, Prefs.radius);

				fprintf(outfile, "\t\tDisk "FFMT" "FFMT" 360.0\n",
										p_f, Prefs.radius);

					g_vertex_ptr += 3;

				}
				Return(OBJ_OK);
			}
			else
				Return(OBJ_NIR);

		}
		
		g_open_object=false;
		g_vertex_ptr=VertexCoords;
		Return(OBJ_OK);
	}
	else if (0==strcmp(curobj, "3DLINE"))	/* cilynder */
	{
		if (	xcoords[0] == xcoords[1] &&
				ycoords[0] == ycoords[1] &&
				zcoords[0] == zcoords[1]	)
		{
			degenerates++;
			Return(OBJ_DEG);
		}

		if(Prefs.radius!=0.0)
		{
			out_AtBegin(outfile);
			
			p_aux[0]=xcoords[1]-xcoords[0];
			p_aux[1]=ycoords[1]-ycoords[0];
			p_aux[2]=zcoords[1]-zcoords[0];
		
			fprintf(outfile, "\t\tTranslate "FFMT" "FFMT" "FFMT"\n",
									xcoords[0],ycoords[0],zcoords[0]);
		
			fprintf(outfile, "\t\tRotate "FFMT" 0.0 0.0 1.0\n",
									-DEGS(fatan2(p_aux[0],p_aux[1])));
		
			fprintf(outfile, "\t\tRotate "FFMT" 1.0 0.0 0.0\n",
									-DEGS(fatan2(fpow(
									p_aux[0]*p_aux[0]+p_aux[1]*p_aux[1],
									0.5),p_aux[2])));

			p_f = 	fpow(
							p_aux[0]*p_aux[0] +
							p_aux[1]*p_aux[1] +
							p_aux[2]*p_aux[2] ,
							0.5
						);
						
			fprintf(outfile, "\t\tCylinder "FFMT" 0.0 "FFMT" 360.0\n",
									Prefs.radius, p_f);

			fprintf(outfile, "\t\tDisk "FFMT" "FFMT" 360.0\n",
									0.0, Prefs.radius);

			fprintf(outfile, "\t\tDisk "FFMT" "FFMT" 360.0\n",
									p_f, Prefs.radius);

			Return(OBJ_OK);
		}
		else
			Return(OBJ_NIR);

	}
	else if (0==strcmp(curobj, "3DFACE"))	/* 1 or 2 triangles */
	{
		out_AtBegin(outfile);
		Return(out_p4(outfile));
	}
	else if (0==strcmp(curobj, "DIMENSION"))	/* not implemented for now */
	{
		Return(OBJ_NI);
	}

ret:

	if(g_at_open && !g_open_object)
	{
		fprintf(outfile, "\tAttributeEnd\n" );
		g_at_open=false;
	}
	switch( done )
	{
		case OBJ_OK:
						primitives++;
						DoError(NO_ERR,DEBUG_ERR,
								"Object ok: %s(%ld), line %ld",
								curobj,primitives,g_line);
						break;
		
		case OBJ_DEG:
						DoError(INPUT_ERR,WARN_ERR,
								"Object degenerated: %s, line %ld",
								curobj,g_line);
						break;
		
		case OBJ_NI:
						DoError(INPUT_ERR,INFO_ERR,
								"Object not implemented: %s, line %ld",
								curobj,g_line);
						break;
		
		case OBJ_NOB:
						DoError(NO_ERR,DEBUG_ERR,
								"Object ???: %s, line %ld",
								curobj,g_line);
						break;
		
		case OBJ_NIR:
						DoError(INPUT_ERR,INFO_ERR,
								"Object 1D (use -R): %s, line %ld",
								curobj,g_line);
						break;
		
		case OBJ_ERR:
						DoError(INPUT_ERR,INFO_ERR,
								"Syntax Error: %s, line %ld",
								curobj,g_line);
						break;
		
		default:
						DoError(PROG_ERR,WARN_ERR,"Done ???: %s, line %ld",
								curobj,g_line);
						break;
	}
	
	
	return(done);
}

static int checkdegen(int a,int b,int c)
/* check for degenerate triangle structure */
{
	return (
	(xcoords[a] == xcoords[b] &&
	 ycoords[a] == ycoords[b] &&
	 zcoords[a] == zcoords[b]) || 
	(xcoords[b] == xcoords[c] &&
	 ycoords[b] == ycoords[c] &&
	 zcoords[b] == zcoords[c]) || 
	(xcoords[a] == xcoords[c] &&
	 ycoords[a] == ycoords[c] &&
	 zcoords[a] == zcoords[c])
	 );
}


/*************************************************************************/

static int out_p4(FILE* outfile)
{
	int result=OBJ_OK;

	if (	xcoords[2] == xcoords[3] &&
			ycoords[2] == ycoords[3] &&
			zcoords[2] == zcoords[3]	)
	{
		return(out_p3(0,1,2,outfile));
		/* one triangle was enough... */
	}


	switch(Prefs.P4)
	{
		case 0:
			/* polygon */
			fprintf(outfile,	"\t\tPolygon \"P\" [\n"
					"\t\t"FFMT" "FFMT" "FFMT"\n"
					"\t\t"FFMT" "FFMT" "FFMT"\n"
					"\t\t"FFMT" "FFMT" "FFMT"\n"
					"\t\t"FFMT" "FFMT" "FFMT" ]\n",
					xcoords[0], ycoords[0], zcoords[0],
					xcoords[1], ycoords[1], zcoords[1],
					xcoords[2], ycoords[2], zcoords[2],
					xcoords[3], ycoords[3], zcoords[3]	);
			break;
			
		case 1:
			/* triangles */
			if ( OBJ_OK == (result=out_p3(0,1,2,outfile)))
				result=out_p3(0,2,3,outfile);
			break;
			
		case 2:
			/* bilinear */

			fprintf(outfile,	"\t\tPatch \"bilinear\" \"P\" [\n"
					"\t\t"FFMT" "FFMT" "FFMT"\n"
					"\t\t"FFMT" "FFMT" "FFMT"\n"
					"\t\t"FFMT" "FFMT" "FFMT"\n"
					"\t\t"FFMT" "FFMT" "FFMT" ]\n",
					xcoords[0], ycoords[0], zcoords[0],
					xcoords[1], ycoords[1], zcoords[1],
					xcoords[3], ycoords[3], zcoords[3],
					xcoords[2], ycoords[2], zcoords[2] );
			break;
			
		default:
			DoError(PROG_ERR,ABORT_ERR,"Selector P4=%d",Prefs.P4);
			break;
	}

	
	return(result);


}

static int out_p3(int i, int j, int k, FILE* outfile)
{
		if (checkdegen(i, j, k))
		{
			degenerates++;
			return(OBJ_DEG);
		}

		fprintf(outfile,	"\t\tPolygon \"P\" [\n"
							"\t\t"FFMT" "FFMT" "FFMT"\n"
							"\t\t"FFMT" "FFMT" "FFMT"\n"
							"\t\t"FFMT" "FFMT" "FFMT" ]\n",
							xcoords[i], ycoords[i], zcoords[i],
							xcoords[j], ycoords[j], zcoords[j],
							xcoords[k], ycoords[k], zcoords[k]	);
							
		return(OBJ_OK);

}

static void out_AtBegin( FILE* outfile)
{
	if(!g_open_object)
	{
		fprintf(outfile, "\tAttributeBegin\n" );

		if(Prefs.structured)
		{
			fprintf(outfile,
				"\t\tAttribute \"identifier\" \"name\" [\"%s_%ld\"]\n",
				curobj,g_line
				);
			if(!Prefs.shaderf)
			{
			fprintf(outfile,
				"\t\tAttribute \"identifier\" \"shadinggroup\""
				"[\"%s_%ld shade\"]\n",curobj,g_line
				);
			/* -> Color and (default) surface properties__________*/
		
			fprintf(outfile,"\t\tColor ["FFMT" "FFMT" "FFMT" ]\n",
				get_red(curcolor),get_green(curcolor),get_blue(curcolor));
			}
		}	
		g_at_open=true;
	}	
}