/**************************************************************************
  Copyright (C) 1992 Guy Moreillon
  All rights reserved.

  This software may be freely copied, modified, and redistributed
  provided that this copyright notice is preserved on all copies.

  You may not distribute this software, in whole or in part, as part of
  any commercial product without the express consent of the authors.

  There is no warranty or other guarantee of fitness of this software
  for any purpose.  It is provided solely "as is".
**************************************************************************/
/**************************************************************************

			      CAD2RAD
		     (AutoCad to Rad converter)
		    	    Version 1.0
			       1992		    	    
		    	    
		  
		    	  Guy Moreillon
		    	  
**************************************************************************/

/*--------------------------------------------------------------------*/ 
/* AutoCAD DXF file to RAD Data File Converter                        */
/* Version 1.0 By G. Moreillon					      */
/* Based on :							      */
/* AutoCAD DXF file to DKB Data File Converter                        */
/* Version 1.0 By Aaron A. Collins.  Written 8/13/90                  */
/* Version 1.01 Modified to work with DKB 2.10 By Drew Wells 3/20/91  */
/*--------------------------------------------------------------------*/
/* Version History                                                    */	
/*--------------------------------------------------------------------*/ 
/* 
 * 07/24/92 GM Created first version
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BUFSIZE	2048
#define FABS(x) ((x<0.0)?-x:x)

typedef float VECTOR[3];

typedef char String[255];

typedef struct {
	int	layer;
	VECTOR	p[3];
	} TRI, *TRIP;

#define V_init(V, X, Y, Z) \
{ \
  V[0] = (X); \
  V[1] = (Y); \
  V[2] = (Z); \
}

#define	P_init(T, L, X0, Y0, Z0, X1, Y1, Z1, X2, Y2, Z2) \
{ \
  if ((L) < 0) \
    (T).layer = find_layer_by_name("Thing"); \
  else \
    (T).layer = (L); \
  V_init((T).p[0], (X0), (Y0), (Z0)); \
  V_init((T).p[1], (X1), (Y1), (Z1)); \
  V_init((T).p[2], (X2), (Y2), (Z2)); \
}

int   groupcode;
char  linbuf[BUFSIZE];
FILE  *infile, *outfile;
long  primitives = 0L, degenerates = 0L;
long  num_triangles = 0L, num_points = 0L, num_circles = 0L, num_traces = 0L,
      num_solids = 0L, num_lines = 0L, num_3dlines = 0L, num_3dfaces = 0L;
char  curobj[80];
int   curcolor;
float curthick;
float xcoords[10];
float ycoords[10];
float zcoords[10];
float floats[10];
float angles[10];
int   layer;
int   ints[10];
float max_x, max_y, max_z, min_x, min_y, min_z;

String	layers[500];
int	nblayers = 0;
TRI	triangles[20000];
int	cur_tri = 0;

int find_layer_by_name(char *name)
{
  int i;

  for(i = 0; i < nblayers; i++)
    if (strcmp(layers[i], name) == 0)
      return i;

  strcpy(layers[nblayers++], name);
  return nblayers;
}

void write_scene(FILE *outfile, char **argv)
{
  int i, j;

  fprintf(outfile, "; This file is for use with the Rad renderer.\n");
  fprintf(outfile, "; %s: Converted from AutoCad DXF File: %s \n\n", argv[2], argv[1]);
  fprintf(outfile, "; Each object in this scene correspond to a layer in the AutoCad file\n");
  fprintf(outfile, "; The scene does not include the hierarchy descriptions necessary\n");
  fprintf(outfile, "; to speed up the computation of shadows. Furthermore, no emmitting\n");
  fprintf(outfile, "; surface is defined\n\n");
        
  fprintf(outfile, "\"%s\"\n", argv[2]);
  fprintf(outfile, "UpAxis z_axis\n\n");

  for(i = 0; i < nblayers; i++)
    {
      int count;

      printf("Writing object '%s'....", layers[i]);

      for(count = 0, j = 0; j < cur_tri; j++)
	if (triangles[j].layer == i)
	  count++;

      if (count == 0)
	{
	  printf("Empty Object\n");
	  continue;
	}
      else
	printf("\n");

      fprintf(outfile, "Object \"%s\" Pure {\n", layers[i]);
      fprintf(outfile, "  Reflectivity 0.6\n");
      fprintf(outfile, "  Color\t\t< 1.0 1.0 1.0 >\n");
      fprintf(outfile, "  Emission\t 0.0\n");
      fprintf(outfile, "  Shadows\tOff\n");
      fprintf(outfile, "  Data {\n");

      for(j = 0; j < cur_tri; j++)
	if (triangles[j].layer == i)
	  fprintf(outfile, "<%9f %9f %9f> <%9f %9f %9f> <%9f %9f %9f>\n",
			   triangles[j].p[0][0],
			   triangles[j].p[0][1],
			   triangles[j].p[0][2], 
			   triangles[j].p[1][0],
			   triangles[j].p[1][1],
			   triangles[j].p[1][2], 
			   triangles[j].p[2][0],
			   triangles[j].p[2][1],
			   triangles[j].p[2][2]);

      fprintf(outfile, "  }\n");
      fprintf(outfile, "}\n");
    }
}

void lookupcolor(int color,
		 float *red,
		 float *green,
		 float *blue)	/* basic AutoCAD 9-color pallette */
{
	switch (color)
	{
		case 0:	/* black */
			*red = *green = *blue = 0.0;
			break;
		case 1: /* red */
			*red = 1.0;
			*blue = *green = 0.0;
			break;
		case 2: /* yellow */
			*red = *green = 1.0;
			*blue = 0.0;
			break;
		case 3:	/* green */
			*green = 1.0;
			*red = *blue = 0.0;
			break;
		case 4: /* cyan */
			*blue = *green = 1.0;
			*red = 0.0;
			break;
		case 5: /* blue */
			*blue = 1.0;
			*red = *green = 0.0;
			break;
		case 6: /* magenta */
			*blue = *red = 1.0;
			*green = 0.0;
			break;
		case 8:	/* dk. grey */
			*red = *green = *blue = 0.5;
			break;
		case 9: /* lt. grey */
			*red = *green = *blue = 0.75;
			break;
		case 7: /* white */
		default: /* make anything else white (?) */
			*red = *green = *blue = 1.0;
	}
	return;
}

void finishobj(int color)	/* conclude a triangle definition */
{
	float red, green, blue;

/*	lookupcolor(color, &red, &green, &blue); */
	primitives++;		/* count another output file primitive */
}

int getline()		/* read a group code and the next line from infile */
{
	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(1);
	return(0);
}

int checkdegen(int a, int b, int c)	/* check for degenerate triangle structure */
{
	if (
	(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]))
		return(1);
	return(0);
}

void storeprim()	/* dump out current object we should have all info on */
{
	if (strstr(curobj, "LINE"))		/* Nothing */
	{
		return;
	}
	else if (strstr(curobj, "POINT"))	/* Nothing */
	{
		return;
	}
	else if (strstr(curobj, "CIRCLE"))	/* Nothing */
	{
		return;
	}
	else if (strstr(curobj, "ARC"))		/* not implemented for now */
	{
		return;
	}
	else if (strstr(curobj, "TRACE"))	/* 2 back-to-back triangles */
	{
		if (checkdegen(0, 1, 2))
		{
			degenerates++;
			return;
		}

		P_init(triangles[cur_tri],
		       layer,
		       xcoords[0], ycoords[0], zcoords[0],
		       xcoords[1], ycoords[1], zcoords[1],
		       xcoords[2], ycoords[2], zcoords[2]);
		cur_tri++;
                num_traces++;
		finishobj(curcolor);

		if (checkdegen(0, 3, 2))
		{
			degenerates++;
			return;
		}

		P_init(triangles[cur_tri],
		       layer,
		       xcoords[0], ycoords[0], zcoords[0],
		       xcoords[2], ycoords[2], zcoords[2],
		       xcoords[3], ycoords[3], zcoords[3]);
		cur_tri++;
		finishobj(curcolor);

		return;
	}
	else if (strstr(curobj, "SOLID"))	/* 1 or 2 triangles */
	{
		if (checkdegen(0, 1, 2))
		{
			degenerates++;
			return;
		}

		P_init(triangles[cur_tri],
		       layer,
		       xcoords[0], ycoords[0], zcoords[0],
		       xcoords[1], ycoords[1], zcoords[1],
		       xcoords[2], ycoords[2], zcoords[2]);
		cur_tri++;
		finishobj(curcolor);
                num_solids++;
		if (xcoords[2] == xcoords[3] && ycoords[2] == ycoords[3] && zcoords[2] == zcoords[3])
			return;	/* one triangle was enough... */

		if (checkdegen(0, 3, 2))
		{
			degenerates++;
			return;
		}

		P_init(triangles[cur_tri],
		       layer,
		       xcoords[0], ycoords[0], zcoords[0],
		       xcoords[2], ycoords[2], zcoords[2],
		       xcoords[3], ycoords[3], zcoords[3]);
		cur_tri++;
		finishobj(curcolor);
		return;
	}
	else if (strstr(curobj, "TEXT"))	/* not implemented for now */
	{
		return;
	}
	else if (strstr(curobj, "SHAPE"))	/* these look very hard */
	{
		return;
	}
	else if (strstr(curobj, "BLOCK"))	/* these look very hard */
	{
		return;
	}
	else if (strstr(curobj, "ENDBLK"))	/* these look very hard */
	{
		return;
	}
	else if (strstr(curobj, "INSERT"))	/* these look very hard */
	{
		return;
	}
	else if (strstr(curobj, "ATTDEF"))	/* not implemented for now */
	{
		return;
	}
	else if (strstr(curobj, "ATTRIB"))	/* not implemented for now */
	{
		return;
	}
	else if (strstr(curobj, "POLYLINE")) /* these look fairly hard */
	{
		return;
	}
	else if (strstr(curobj, "VERTEX"))	/* these look fairly hard */
	{
		return;
	}
	else if (strstr(curobj, "SEQEND"))	/* these look fairly hard */
	{
		return;
	}
	else if (strstr(curobj, "3DLINE"))	/* Nothing */
	{
		return;
	}
	else if (strstr(curobj, "3DFACE"))	/* 1 or 2 triangles */
	{
		if (checkdegen(0, 1, 2))
		{
			degenerates++;
			return;
		}

		P_init(triangles[cur_tri],
		       layer,
		       xcoords[0], ycoords[0], zcoords[0],
		       xcoords[1], ycoords[1], zcoords[1],
		       xcoords[2], ycoords[2], zcoords[2]);
		cur_tri++;
		finishobj(curcolor);
                num_3dfaces++;
		if (xcoords[2] == xcoords[3] && ycoords[2] == ycoords[3] && zcoords[2] == zcoords[3])
			return;	/* one triangle was enough... */

		if (checkdegen(0, 3, 2))
		{
			degenerates++;
			return;
		}

		P_init(triangles[cur_tri],
		       layer,
		       xcoords[0], ycoords[0], zcoords[0],
		       xcoords[2], ycoords[2], zcoords[2],
		       xcoords[3], ycoords[3], zcoords[3]);
		cur_tri++;
		finishobj(curcolor);
		return;
	}
	else if (strstr(curobj, "DIMENSION"))	/* not implemented for now */
	{
		return;
	}
	return;	/* no current object defined... */
}

void main(int argc, char **argv)
{
	char *index;

	printf("\n\nAutoCad DXF to RAD Data file Translator\n");
	printf("Version 1.0  Written by Guy Moreillon,  07/24/92\n");
	printf("Based on :\n");
	printf("AutoCad DXF to DKB Data file Translator\n");
	printf("Version 1.0  Written By Aaron A. Collins, 8/13/90\n");
	printf("Version 1.01 Modifications  By Drew Wells 3/20/91\n");
	printf("Version 1.02 Modifications By Aaron A. Collins 3/20/91\n");
	printf("Version 1.03 Modifications By Aaron A. Collins 5/15/91\n");
	printf("* Type cad2rad with no arguments for more info *\n");
	if (argc != 3)
	{
		printf("Usage:  %s InputFile OutputFile\n\n", argv[0]);
                
		printf("cad2rad does a minimal conversion of AutoCad DXF files\n");
		printf("into RAD data format.\n");
		printf("The following DXF primitives are currently supported:\n");
		printf("\t3D Faces\tSolids\tTraces\n");
		printf("\n\n");
		exit(1);
	}
	if (!(infile = fopen(argv[1], "r")))
	{
		printf("Cannot open input file %s!\n", argv[1]);
		exit(1);
	}

	if (!(outfile = fopen(argv[2], "w")))
	{
		printf("Cannot create output file %s!\n", argv[2]);
		fclose(infile);
		exit(1);
	}

	printf("\nPlease wait; Processing...\n");

	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 =  -10000000.0;	/* init bounding limits */
	min_x = min_y = min_z =   10000000.0;


find:	while (!feof(infile))	/* run file up to the "LAYER" table */
	{			/* or the "ENTITIES" section */
		if (getline())		/* get a group code and a line */
			goto stopit;
		if (groupcode == 0)	/* file section mark */
		{
			if (strstr(linbuf, "EOF"))
				goto stopit;
			if (strstr(linbuf, "TABLE"))
			{
				if (getline())
					goto stopit;
				if (groupcode != 2)
					continue;
				if (strstr(linbuf, "LAYER"))
					goto Layer;
			}
			else if (strstr(linbuf, "SECTION"))
			{
				if (getline())
					goto stopit;
				if (groupcode != 2)
					continue;
				if (strstr(linbuf, "ENTITIES"))
					goto ent;
			}
		}
	}
Layer:	while (!feof(infile))		/* scan LAYER table */
	{
		if (getline())		/* get a group code and a line */
			goto stopit;
		if (groupcode == 0)
		{
			if (strstr(linbuf, "EOF"))
			  goto stopit;
			if (strstr(linbuf, "ENDTAB"))
			  break;
			if (strstr(linbuf, "LAYER"))
			{
			    do {
				if (getline())
					goto stopit;
			    } while ((groupcode != 2) && (groupcode != 0));
			    if (groupcode == 2)
			      {
				linbuf[strlen(linbuf)-1] = '\0';
			        strcpy(layers[nblayers++], linbuf);
			      }
			}
		}
	}
ent:	layer = -1;
	while (!feof(infile))		/* scan ENTITIES section */
	{
		if (getline())		/* get a group code and a line */
			break;
		if (groupcode < 10)	/* cardinal group codes */
		{
			switch(groupcode)
			{
				case 0: /* start of entity, table, file sep */
					if (strstr(linbuf, "EOF"))
					{
						storeprim();
						goto stopit;
					}
					if (strstr(linbuf, "ENDSEC"))
					{
						storeprim();
						goto find;
					}
					storeprim();
					curobj[0] = '\0'; /* reset object */
					curcolor = 7;
					strcpy(curobj, linbuf);	/* get new */
					break;
				case 1:	/* primary text value for entity (?)*/
					break;
				case 2: /* block name, attribute tag, etc */
				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 */
					linbuf[strlen(linbuf)-1] = '\0';
					layer = find_layer_by_name(linbuf);
					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 */
		{
		}
		else if (groupcode >= 70 && groupcode < 79) /* misc ints */
		{
			sscanf(linbuf, "%f", &(ints[groupcode-70]));
		}
		else if (groupcode == 210 || groupcode == 220 || groupcode == 230)
		{	/* X, Y, Z components of extrusion direction */
		}
	}
stopit: write_scene(outfile, argv);
	fclose(infile);
	fflush(outfile);
	fclose(outfile);
	printf("Finished.\nTotal triangles (?) written to output file: %ld\n\n", primitives);
	printf("Total degenerate triangles removed from scene: %ld\n\n", degenerates);
        
	printf ("X bounding values range from %f to %f\n", min_x, max_x);
	printf ("Y bounding values range from %f to %f\n", min_y, max_y);
	printf ("Z bounding values range from %f to %f\n", min_z, max_z);
	printf("\n\t\t\t--- DXF Stats ---\n");
	printf("3D Faces = \t%ld\tSolids = \t%ld\tTraces =\t%ld\n",
	       num_3dfaces,num_solids,num_traces);
	exit(0);
}


