/*
 *  DXF to V-library (obv) file converter
 */

#define DEBUG

#ifdef HAVE_STDLIB
#include <stdlib.h>
#endif
#include <stdio.h>
#include <stdarg.h>
#include <Vlib.h>
#include <string.h>
#include <math.h>

//#undef printf
//#undef fprintf


char *colors[] =
{
	"black",
	"red",
	"yellow",
	"green",
	"cyan",
	"blue",
	"magenta",
	"white"
};

#define Colors	8

extern VPolygon *ScalePolygon(VPolygon * in, VPoint * origin, VPoint * scale,
							  VPoint *, double);
extern VObject *VExtrudeObject(VObject * obj, VPoint * e);

#define POLY_MAX	(10 * 1024)
#define BOBJECT_MAX	128
#define POINT_MAX	32768

VObject *bobject[BOBJECT_MAX];
int btop = 0;

#ifdef DEBUG
#define PDEBUG(a)	printf a
#else
#define PDEBUG(a)
#endif

int lineno = 0;

#define COMPARE(a,b)	strcmp(a,b)

enum _token_id {
		DXF_NULL,
		DXF_EOF,
		DXF_X_COORD,
		DXF_Y_COORD,
		DXF_Z_COORD,
		DXF_X_SCALE,
		DXF_Y_SCALE,
		DXF_Z_SCALE,
		DXF_ROTATE,
		DXF_SECTION,
		DXF_ENDSEC,
		DXF_SEQEND,
		DXF_TITLE,
		DXF_POLYLINE,
		DXF_3DFACE,
		DXF_ENTITIES,
		DXF_VERTEX,
		DXF_BLOCKS,
		DXF_INSERT,
		DXF_X_EXTRUDE,
		DXF_Y_EXTRUDE,
		DXF_Z_EXTRUDE,
		DXF_M_COUNT,
		DXF_N_COUNT,
		DXF_THIRD_VERTEX,
		DXF_FOURTH_VERTEX,
		DXF_FLAGS,
		DXF_ITEM,
		DXF_TABLES,
		DXF_TABLE,
		DXF_ENDTAB,
		DXF_LAYER,
		DXF_STYLE,
		DXF_LTYPE,
		DXF_ATTDEF,
		DXF_ATTRIB,
		DXF_DICTIONARY,	/* found in OBJECTS section */
		DXF_MLINESTYLE,
		DXF_ACAD_GROUP,
		DXF_ACAD_MLINESTYLE,
		DXF_APPID,		/* found in TABLES and BLOCKS section */
		DXF_BLOCK,
		DXF_ENDBLK,
		DXF_VPORT,
		DXF_VIEW,
		DXF_COLOR_INDEX
};

typedef enum _token_id dxf_token_id;

typedef struct {
    dxf_token_id id;
    char *name;
} token_table;

token_table a[] =
{
    {DXF_SECTION,    "SECTION"},
    {DXF_ENDSEC,     "ENDSEC"},
    {DXF_POLYLINE,   "POLYLINE"},
    {DXF_3DFACE,     "3DFACE"},
    {DXF_VERTEX,     "VERTEX"},
    {DXF_SEQEND,     "SEQEND"},
    {DXF_EOF,        "EOF"},
    {DXF_BLOCK,      "BLOCK"},
    {DXF_ENDBLK,     "ENDBLK"},
    {DXF_INSERT,     "INSERT"},
    {DXF_TABLE,      "TABLE"},
    {DXF_ENDTAB,     "ENDTAB"},
    {DXF_LAYER,      "LAYER"},
    {DXF_STYLE,      "STYLE"},
    {DXF_LTYPE,      "LTYPE"},
    {DXF_VPORT,      "VPORT"},
    {DXF_DICTIONARY, "DICTIONARY"},
    {DXF_MLINESTYLE, "MLINESTYLE"},
    {DXF_APPID,      "APPID"},
    {DXF_ACAD_GROUP, "ACAD_GROUP"},
    {DXF_ACAD_MLINESTYLE, "ACAD_MLINESTYLE"},
    {DXF_ATTDEF,    "ATTDEF"},
    {DXF_ATTRIB,     "ATTRIB"},
    {DXF_EOF, NULL}
};

static int int_value;


static dxf_token_id
ReadToken(FILE *f, double *fp_value, char *cp_value, int *code, char *string)
{
    long i;
    int len;
    token_table *p;
    char buf1[512], buf2[512], *r1, *r2;
	
    r1 = fgets(buf1, sizeof(buf1), f);
    r2 = fgets(buf2, sizeof(buf2), f);
    lineno += 2;
	
	/*
	 *  Remove the trailing newline ...
	 */
	
    len = strlen(buf1);
    if (len > 0) {
		buf1[len - 1] = '\0';
    }
	
	/*
	 *  File in MSDOS format?
	 */
	
    if (len >= 2 && buf1[len - 2] == '\r') {
		buf1[len - 2] = '\0';
    }
	
	/*
	 *  Remove the trailing newline ...
	 */
	
    len = strlen(buf2);
    if (len > 0) {
		buf2[len - 1] = '\0';
    }
	
	/*
	 *  File in MSDOS format?
	 */
	
    if (len >= 2 && buf2[len - 2] == '\r') {
		buf2[len - 2] = '\0';
    }
	
    strcpy(cp_value, buf2);
    strcpy(string, buf2);
	
    if (r1 != (char *) NULL && r2 != (char *) NULL) {
		i = strtol(buf1, (char **) NULL, 0);
		*code = i;
		if (i == 0) {
			for (p = a; p->name != (char *) NULL; ++p) {
				if (COMPARE(p->name, buf2) == 0) {
					/*	    PDEBUG(("token: %s  --> ", p->name)); */
					return p->id;
				}
			}
			printf ("Warning: unrecognized directive, \"%s\"\n",
				buf2);
		}
		else if (i == 2) {
			if (COMPARE("ENTITIES", buf2) == 0) {
				return DXF_ENTITIES;
			}
			else if (COMPARE("BLOCKS", buf2) == 0) {
				return DXF_BLOCKS;
			}
			else if (COMPARE("TABLES", buf2) == 0) {
				return DXF_TABLES;
			}
			else {
				return DXF_TITLE;
			}
		}
		else if (i >= 10 && i <= 19) {
			*fp_value = strtod(buf2, (char **) NULL);
			return DXF_X_COORD;
		}
		else if (i >= 20 && i <= 29) {
			*fp_value = strtod(buf2, (char **) NULL);
			return DXF_Y_COORD;
		}
		else if (i >= 30 && i <= 39) {
			*fp_value = strtod(buf2, (char **) NULL);
			return DXF_Z_COORD;
		}
		else if (i == 41) {
			*fp_value = strtod(buf2, (char **) NULL);
			return DXF_X_SCALE;
		}
		else if (i == 42) {
			*fp_value = strtod(buf2, (char **) NULL);
			return DXF_Y_SCALE;
		}
		else if (i == 43) {
			*fp_value = strtod(buf2, (char **) NULL);
			return DXF_Z_SCALE;
		}
		else if (i == 50) {
			*fp_value = strtod(buf2, (char **) NULL);
			return DXF_ROTATE;
		}
		else if (i == 62) {
			int_value = strtol(buf2, (char **) NULL, 0);
			return DXF_COLOR_INDEX;
		}
		else if (i == 70) {
			int_value = strtol(buf2, (char **) NULL, 0);
			return DXF_FLAGS;
		}
		else if (i == 71) {
			int_value = strtol(buf2, (char **) NULL, 0);
			return DXF_M_COUNT;
		}
		else if (i == 72) {
			int_value = strtol(buf2, (char **) NULL, 0);
			return DXF_N_COUNT;
		}
		else if (i == 73) {
			int_value = strtol(buf2, (char **) NULL, 0);
			return DXF_THIRD_VERTEX;
		}
		else if (i == 74) {
			int_value = strtol(buf2, (char **) NULL, 0);
			return DXF_FOURTH_VERTEX;
		}
		else if (i == 210) {
			*fp_value = strtod(buf2, (char **) NULL);
			return DXF_X_EXTRUDE;
		}
		else if (i == 220) {
			*fp_value = strtod(buf2, (char **) NULL);
			return DXF_Y_EXTRUDE;
		}
		else if (i == 230) {
			*fp_value = strtod(buf2, (char **) NULL);
			return DXF_Z_EXTRUDE;
		}
		else {
			return DXF_ITEM;
		}
    }
    return DXF_EOF;
}

static int lookahead_valid = 0;
static double fp_la;
static dxf_token_id token_id_la;
static char cp_la[256], string_la[256];
static int code_la;

static dxf_token_id
NextToken(FILE * f, double *fp_value, char *cp_value, int * code, char *string)
{
    if (lookahead_valid) {
		lookahead_valid = 0;
		*fp_value = fp_la;
		strcpy(cp_value, cp_la);
		strcpy(string, string_la);
		*code = code_la;
		return token_id_la;
	}
    else {
		return ReadToken(f, fp_value, cp_value, code, string);
    }
}

static void
PushToken(dxf_token_id id, double fp_value, char *cp_value, int code, char *string)
{
    lookahead_valid = 1;
    fp_la = fp_value;
    token_id_la = id;
    strcpy(cp_la, cp_value);
    strcpy(string_la, string);
    code_la = code;
}

static void
InsertBlock(char *name, VPoint * o, VPoint * scale, VPoint * extrude, double r,
			VPolygon **poly, int *ptop)
{
    VObject **p;
    int i, j, extrusion = 0;
	
    PDEBUG(("looking for \"%s\"; offset %g, %g, %g; scale %g, %g, %g; extrude %g, %g, %g; rotate %g\n", name,
		o->x, o->y, o->z, scale->x, scale->y, scale->z,
		extrude->x, extrude->y, extrude->z, r));
	
    for (p = bobject, i = 0; i < btop; ++i, ++p) {
		if (strcmp((*p)->name, name) == 0) {
			
		/*
		 *  If this is an extrusion, create a temporary object representing the
		 *  given extrusion direction.
		 */
			
#ifdef notdef
			if (extrude->x != 0.0 || extrude->y != 0.0 || extrude->z != 0.0) {
				PDEBUG(("extrusion (%f, %f, %f)\n", extrude->x,
					extrude->y, extrude->z));
				extrusion = 1;
				ex_tmp = VExtrudeObject(*p, extrude);
				p = &ex_tmp;
			}
#endif
			
			/*
			 *  Add points to the block ...
			 */
			
			PDEBUG(("adding %d polygons to %d existing\n",
				(*p)->numPolys, (*ptop)));
			for (j = 0; j < (*p)->numPolys; ++j) {
				poly[(*ptop)++] = ScalePolygon((*p)->polygon[j], o, scale, extrude, r);
			}
			
			/*
			 *  Delete the temporarily created extrusion object
			 */
			
			if (extrusion) {
				for (j = 0; j < (*p)->numPolys; ++j) {
					VDestroyPolygon((*p)->polygon[j]);
					free((char *) (*p));
				}
			}
			
			return;
		}
    }
}

#define STATE_NORMAL		0
#define STATE_ENTITIES		1
#define STATE_POLYLINE		2
#define STATE_3DFACE		3
#define STATE_VERTEX		4
#define STATE_BLOCK		5
#define STATE_INSERT		6
#define STATE_TABLES		7
#define STATE_GENERAL_ENTITY	8

VObject  *
VReadDXFObject(FILE *f)
{
	return VReadDepthCueuedObject(f, 0);
}

VObject  *
VReadDepthCueuedDXFObject(FILE *f, int flag)
{
	
    double value, rotate = 0;
    dxf_token_id id, saved_id = DXF_NULL;
    int i, j, m0 = 0, n0 = 0, vertices_listed_by_index = 0;
    VPoint temp[POINT_MAX], temp1[4], *p, scale, pt, bpt, extrude;
    VPolygon template;
    VObject *object;
    int top = 0;
    int state = STATE_NORMAL, submode = 0;
    char cp[256], title[256], insert_title[256], *stop_block = "<none>";
    int order = 0;
    int indices[4], num_indices = 0;
    VMatrix m;
    int code, polymode;
    char string[256];

	VColor *blackColor = VAllocColor("black");

	VPolygon *poly[POLY_MAX];
	int ptop = 0;
	
	/*
	 *  Start with the first of our selected colors.  If ordering was reqested,
	 *  make the same color the "backface color".
	 */
	
	memset(&template, 0, sizeof(VPolygon));

    template.color = blackColor;
    template.backColor = NULL;
    if (order) {
        template.backColor = template.color;
    }
	
	/*
	 *  I'm not sure about backface clipping, yet.
	 */
    template.flags = 0;
	/*  template.flags = PolyClipBackface; */
    template.assignedDepth = -1;
	
    p = &temp[top];
	
    while (1) {
		id = NextToken(f, &value, cp, &code, string);
		switch (state) {
		case STATE_NORMAL:
			///    PDEBUG(("NORMAL: %s\n", string));
			switch (id) {
			case DXF_ENTITIES:
				submode = 0;
				state = STATE_ENTITIES;
				break;
			case DXF_BLOCKS:
				submode = 1;
				state = STATE_ENTITIES;
				break;
			case DXF_TABLES:
				state = STATE_TABLES;
				break;
			case DXF_EOF:
				
			/*
			 *  Build the object structure
		     */
				
				object = (VObject *) Vmalloc(sizeof(VObject));
				memset (object, 0, sizeof(VObject));
				object->name = strdup("name");
				object->numPolys = ptop;
				object->polygon = (VPolygon **) Vmalloc(ptop * sizeof(VPolygon *));
				memcpy(object->polygon, poly, ptop * sizeof(VPolygon *));
				object->order = (unsigned short *) NULL;
				VComputeObjectExtent(object);

				if (VObjectNeedsOrdering(object)) {
					VComputePolygonOrdering(object);
				}

				/*
				 *  Change to V library axes (Z-axis = down);
				 */
				
				VIdentMatrix(&m);
#ifdef notdef
				m.m[2][1] = 1.0;
				m.m[2][2] = 0.0;
				m.m[1][1] = 0.0;
				m.m[1][2] = -1.0;
#endif
				m.m[2][2] = -1.0;
				m.m[1][1] = -1.0;
				
				for (i = 0; i < object->numPolys; ++i) {
					for (j = 0; j < object->polygon[i]->numVtces; ++j) {
						VTransform_(&object->polygon[i]->vertex[j], &m, &pt);
						object->polygon[i]->vertex[j] = pt;
					}
				}
				
				return object;
				
			default:
				break;
			}
			break;
			
			/*
			 *  Skip table definitions (for now) ...
			 */
			
			case STATE_TABLES:
				switch (id) {
				case DXF_ENDSEC:
					state = STATE_NORMAL;
					break;
				default:
					break;
				}
				break;
				
				case STATE_ENTITIES:
					if (code == 0) {
						// close-out last entity
						saved_id = DXF_NULL;
#ifdef notdef
						printf ("Entity closed 0, \"%s\"\n", string);
#endif
					}
					// PDEBUG(("ENTITIES: %s\n", string));
					switch (id) {
					case DXF_ATTDEF:
					case DXF_ATTRIB:
						saved_id = id;
						state = STATE_GENERAL_ENTITY;
						break;
					case DXF_POLYLINE:
						state = STATE_POLYLINE;
						p = &temp[0];
						top = 0;
						m0 = n0 = 0;
						vertices_listed_by_index = num_indices = 0;
						polymode = 0;
						break;
					case DXF_3DFACE:
						state = STATE_3DFACE;
						p = &temp[0];
						top = 0;
						break;
					case DXF_BLOCK:
						state = STATE_BLOCK;
						strcpy (title, "*none*");
						bpt.x = bpt.y = bpt.z = 0.0;
						break;
					case DXF_ENDBLK:
						state = STATE_BLOCK;
						PushToken(id, value, cp, code, string);
						break;
					case DXF_INSERT:
						pt.x = pt.y = pt.z = 0.0;
						scale.x = 1.0;
						scale.y = 1.0;
						scale.z = 1.0;
						rotate = 0.0;
						extrude = pt;
						extrude.z = 1.0;
						state = STATE_INSERT;
						strcpy (insert_title, "*not-specified*");
						break;
					case DXF_ENDSEC:
						state = STATE_NORMAL;
						break;
					default:
						if (code != 0) {
							printf ("hmm, %d \"%s\"\n", code, string);
						}
						else {
							saved_id = id;
							state = STATE_GENERAL_ENTITY;
						}
						break;
					case DXF_EOF:
						break;
					}
					break;
					
					case STATE_POLYLINE:
						//PDEBUG(("POLYLINE: %s\n", string));
						switch (id) {
						case DXF_COLOR_INDEX:
							if (int_value < 8) {
								template.color = VAllocColor (colors[i]);
								if (order) {
									template.backColor = template.color;
								}
							}
							else {
								printf ("color index %d\n", int_value);
							}
							break;

						case DXF_SEQEND:
							
						/*
						 * Mesh?
						 */
							
							if (vertices_listed_by_index == 0) {
								if (m0 != 0 || n0 != 0) {
									for (i = 1; i < m0; ++i) {
										for (j = 1; j < n0; ++j) {
											temp1[0] = temp[(i - 1) * n0 + j - 1];
											temp1[1] = temp[(i) * n0 + j - 1];
											temp1[2] = temp[(i) * n0 + j];
											poly[ptop++] = VCreatePolygonFromTemplate(3, temp1,
												&template);
											
											temp1[0] = temp[(i) * n0 + j];
											temp1[1] = temp[(i - 1) * n0 + j];
											temp1[2] = temp[(i - 1) * n0 + j - 1];
											poly[ptop++] = VCreatePolygonFromTemplate(3, temp1,
												&template);
										}
									}
									PDEBUG(("mesh %d x %d\n", m0, n0));
								}
								else {
									poly[ptop++] = VCreatePolygonFromTemplate(top, temp,
										&template);
								}
							}
							PDEBUG(("added POLYLINE polygon number %d\n", ptop - 1));
#ifdef notdef
							VPrintPolygon(stdout, poly[ptop - 1]);
#endif
							p = &temp[0];
							top = 0;
							state = STATE_ENTITIES;
							break;
						case DXF_FLAGS:
							if ((int_value & 2) || (int_value & 4)) {
								printf ("Warning: POLYLINE splines or curves not supported by Vlib (line %d).\n", lineno);
							}
							if (int_value & 64) {
								polymode = 2;
							}
							else if (int_value & 16) {
								polymode = 1;
							}
							break;
						case DXF_VERTEX:
							state = STATE_VERTEX;
							break;
						case DXF_M_COUNT:
							m0 = int_value;
							break;
						case DXF_N_COUNT:
							n0 = int_value;
							break;
						default:
							break;
						}
						break;
						
						case STATE_3DFACE:
							// PDEBUG(("3DFACE: %s\n", string));
							switch (id) {
							case DXF_COLOR_INDEX:
								if (int_value < 8) {
									template.color = VAllocColor (colors[int_value]);
									if (order) {
										template.backColor = template.color;
									}
								}
								else {
									printf ("color index %d\n", int_value);
								}
								break;

							case DXF_ENDBLK:
								state = STATE_BLOCK;
								PushToken(id, value, cp, code, string);
								/* create polygon */
								poly[ptop++] = VCreatePolygonFromTemplate(top, temp,
									&template);
								p = &temp[0];
								top = 0;
								break;
								
							case DXF_X_COORD:
								p->x = value;
								if ((id = NextToken(f, &p->y, cp, &code, string)) != DXF_Y_COORD) {
									printf("syntax error4 %d\n", id);
								}
								
								if ((id = NextToken(f, &p->z, cp, &code, string)) != DXF_Z_COORD) {
									printf("syntax error5 %d\n", id);
								}
								
								++p;
								++top;
								if (top == POINT_MAX) {
									printf ("Point overflow, increase POINT_MAX.\n");
								}
								break;
								
							case DXF_3DFACE:
							case DXF_POLYLINE:
							case DXF_ENDSEC:
							default:
								if (code == 0) {
								/*
								*  Turbocad bug
									*/
									if (temp[top-1].x == temp[top-2].x &&
										temp[top-1].y == temp[top-2].y &&
										temp[top-1].z == temp[top-2].z) {
										-- top;
									}
									/* create polygon */
									poly[ptop++] = VCreatePolygonFromTemplate(top, temp,
										&template);
#ifdef notdef
									printf("polygon %d\n", ptop - 1);
									VPrintPolygon(stdout, poly[ptop - 1]);
#endif
									p = &temp[0];
									top = 0;
									PushToken(id, value, cp, code, string);
									state = STATE_ENTITIES;
								}
								
								break;
							}
							break;
							
							/*
							*  Get X,Y,Z components following a VERTEX directive
							*/
							
							case STATE_VERTEX:
								// PDEBUG(("VERTEX: %d\n", id));
								switch (id) {
								case DXF_COLOR_INDEX:
								if (int_value < 8) {
									template.color = VAllocColor (colors[i]);
									if (order) {
										template.backColor = template.color;
									}
								}
								else {
									printf ("color index %d\n", int_value);
								}
								break;

								case DXF_X_COORD:
									p->x = value;
									break;
								case DXF_Y_COORD:
									p->y = value;
									break;
								case DXF_Z_COORD:
									p->z = value;
									break;
									
									/*
									*  M_COUNT and N_COUNT really are the first and second vertex indices in
									*  a VERTEX.
									*/
								case DXF_M_COUNT:
									vertices_listed_by_index = 1;
									if (int_value < 0) {
										int_value = - int_value;
									}
									indices[0] = int_value - 1;
									num_indices = 1;
									break;
								case DXF_N_COUNT:
									vertices_listed_by_index = 1;
									if (int_value < 0) {
										int_value = - int_value;
									}
									indices[1] = int_value - 1;
									num_indices = 2;
									break;
								case DXF_THIRD_VERTEX:
									vertices_listed_by_index = 1;
									if (int_value < 0) {
										int_value = - int_value;
									}
									indices[2] = int_value - 1;
									num_indices = 3;
									break;
								case DXF_FOURTH_VERTEX:
									vertices_listed_by_index = 1;
									if (int_value < 0) {
										int_value = - int_value;
									}
									indices[3] = int_value - 1;
									num_indices = 4;
									break;
								case DXF_SEQEND:
								case DXF_VERTEX:
									if (vertices_listed_by_index) {
										for (i=0; i<num_indices; ++i) {
											if (indices[i] >= top) {
												printf ("internal error polygon vertex out of range: %d (max %d) -- %d\n",
													indices[i], top, i);
											}
											temp1[i] = temp[indices[i]];
										}
										poly[ptop++] = VCreatePolygonFromTemplate(num_indices, temp1,
											&template);
									}
									else {
										++p;
										++top;
										if (top == POINT_MAX) {
											printf ("Point overflow, increase POINT_MAX.\n");
										}
									}
									PushToken(id, value, cp, code, string);
									state = STATE_POLYLINE;
									break;
								default:
									break;
								}
								
								break;
								
								case STATE_INSERT:
									// PDEBUG(("INSERT: %s\n", string));
									switch (id) {
										
										break;
									case DXF_ENDBLK:
										state = STATE_BLOCK;
										PushToken(id, value, cp, code, string);
										
										InsertBlock(insert_title, &pt, &scale,
											&extrude, rotate, poly, &ptop);
										
										break;
									case DXF_TITLE:
										strcpy(insert_title, cp);
										break;
									case DXF_X_COORD:
										pt.x = value;
										break;
									case DXF_Y_COORD:
										pt.y = value;
										break;
									case DXF_Z_COORD:
										pt.z = value;
										break;
									case DXF_X_SCALE:
										scale.x = value;
										break;
									case DXF_Y_SCALE:
										scale.y = value;
										break;
									case DXF_Z_SCALE:
										scale.z = value;
										break;
									case DXF_ROTATE:
										rotate = value;
										break;
									case DXF_X_EXTRUDE:
										extrude.x = value;
										break;
									case DXF_Y_EXTRUDE:
										extrude.y = value;
										break;
									case DXF_Z_EXTRUDE:
										extrude.z = value;
										break;
									case DXF_INSERT:
									case DXF_POLYLINE:
									case DXF_3DFACE:
									case DXF_ENDSEC:
									default:
										if (code == 0) {
											state = STATE_ENTITIES;
											PushToken(id, value, cp, code, string);
											
											InsertBlock(insert_title, &pt, &scale,
												&extrude, rotate, poly, &ptop);
										}
										break;
									}
									break;
									
									case STATE_GENERAL_ENTITY:
										if (code == 0) {
											state = STATE_ENTITIES;
											PushToken(id, value, cp, code, string);
										}
										break;
										
									case STATE_BLOCK:
										PDEBUG(("BLOCK: %s\n", string));
										switch (id) {
										case DXF_ENDBLK:
										/*
										 *  Build the object structure and place it on the block list
									     */
											object = (VObject *) Vmalloc(sizeof(VObject));
											object->name = malloc(strlen(title) + 1);
											strcpy(object->name, title);
											PDEBUG(("added block \"%s\" %d polygons (line %d)\n", title, ptop, lineno));
											object->numPolys = ptop;
											object->polygon = (VPolygon **)
												Vmalloc(ptop * sizeof(VPolygon *));
											memcpy((char *) object->polygon,
												(char *) poly, ptop * sizeof(VPolygon *));
											ptop = 0;
											object->order = (unsigned short *) NULL;
											PDEBUG(("Block offset %f, %f, %f\n",
												bpt.x, bpt.y, bpt.z));
											
											for (i = 0; i < object->numPolys; ++i) {
												for (j = 0; j < object->polygon[i]->numVtces; ++j) {
													object->polygon[i]->vertex[j].x += bpt.x;
													object->polygon[i]->vertex[j].y += bpt.y;
													object->polygon[i]->vertex[j].z += bpt.z;
												}
											}
											
											bobject[btop++] = object;
											
											state = STATE_ENTITIES;
											if (strcmp(stop_block, object->name) == 0) {
												VIdentMatrix(&m);
#ifdef notdef
												m.m[2][1] = 1.0;
												m.m[2][2] = 0.0;
												m.m[1][1] = 0.0;
												m.m[1][2] = -1.0;
#endif
												m.m[2][2] = -1.0;
												m.m[1][1] = -1.0;
												
												for (i = 0; i < object->numPolys; ++i) {
													for (j = 0; j < object->polygon[i]->numVtces; ++j) {
														VTransform(&object->polygon[i]->vertex[j], &m, &pt);
														object->polygon[i]->vertex[j] = pt;
													}
												}
												
												return object;
											}
											
											break;
										case DXF_TITLE:
											strcpy(title, cp);
											break;
										case DXF_X_COORD:
											bpt.x = value;
											break;
										case DXF_Y_COORD:
											bpt.y = value;
											break;
										case DXF_Z_COORD:
											bpt.z = value;
											break;
										case DXF_POLYLINE:
										case DXF_3DFACE:
										case DXF_INSERT:
										default:
											if (code == 0) {
												state = STATE_ENTITIES;
												PushToken(id, value, cp, code, string);
											}
											break;
										}
	}
    }
	return 0;
}

static void
ArbitraryAxis (VPoint *normal, VMatrix *out)
{
	double minval = 1.0 / 64.0, d;
	VPoint Ax, Ay, Az;

	d = sqrt (normal->x * normal->x + normal->y * normal->y + normal->z * normal->z);
	Az.x = normal->x / d;
	Az.y = normal->y / d;
	Az.z = normal->z / d;

	if (fabs (Az.x) < minval && fabs (Az.y) < minval) {
		VCrossProd(&_VUnitVectorJ, &Az, &Ax);
	}
	else {
		VCrossProd(&_VUnitVectorK, &Az, &Ax);
	}

	VCrossProd (&Az, &Ax, &Ay);

	out->m[0][0] = Ax.x;
	out->m[0][1] = Ax.y;
	out->m[0][2] = Ax.z;

	out->m[1][0] = Ay.x;
	out->m[1][1] = Ay.y;
	out->m[1][2] = Ay.z;

	out->m[2][0] = Az.x;
	out->m[2][1] = Az.y;
	out->m[2][2] = Az.z;

	out->m[3][0] = out->m[3][1] = out->m[3][2] = 0.0;
	out->m[0][3] = out->m[1][3] = out->m[2][3] = 0.0;
	out->m[3][3] = 1.0;
}
