text: /*                          I G E S . C
   * BRL-CAD
   *
   * Copyright (c) 1993-2019 United States Government as represented by
   * the U.S. Army Research Laboratory.
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public License
   * version 2.1 as published by the Free Software Foundation.
   *
   * This program is distributed in the hope that it will be useful, but
   * WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
   * License along with this file; see the file named COPYING for more
   * information.
   */
  /** @file iges/iges.c
   *
   * Code to support the g-iges converter
   *
   * Yet to do:
   *
   * Utilize the IGES Right Angle Wedge entity (may not be worth the effort):
   *	1. arb_is_raw()
   *	2. raw_to_iges()
   *	3. modify arb_to_iges to use above
   *
   * How to handle half-space solids????
   * How to handle xforms with scale factors?????
   */
  
  #include "common.h"
  
  #include <stdlib.h>
  #include <stdio.h>
  #include <math.h>
  #include <string.h>
  #include <ctype.h>
  #include <time.h>
  #include <sys/types.h>
  #include <sys/stat.h>
  
  #include "vmath.h"
  #include "nmg.h"
  #include "rt/geom.h"
  #include "raytrace.h"
  
  #include "./iges.h"
  
  
  /* define defaulted entry for directory entry array */
  #define DEFAULT (-99999)
  #define COMMA ','
  
  extern int verbose;
  static int solids_to_nmg = 0;/* Count of solids that were converted to nmg's in CSG mode */
  static int dir_seq = 0;	/* IGES file directory section sequence number */
  static int param_seq = 0;	/* IGES file parameter section sequence number */
  static int start_len = 0;	/* Length of Start Section */
  static int global_len = 0;	/* Length of Global Section */
  static int attribute_de;	/* DE of attribute definition entity */
  static const char *global_form = "%-72.72s%c%07d\n"; /* format for global section */
  static const char *param_form = "%-64.64s %7d%c%07d\n"; /* format for parameter section */
  static const char *att_string = "BRL-CAD attribute definition:material name, material parameters, region flag, ident number, air code, material code (GIFT), los density, inheritance";
  static struct bn_tol tol;	/* tolerances */
  static struct bg_tess_tol ttol;	/* tolerances */
  static struct db_i *dbip = NULL;
  static const char *unknown = "Unknown";
  static int unknown_count = 0;
  static int de_pointer_number;
  extern char **independent;
  extern size_t no_of_indeps;
  extern int solid_is_brep;
  extern int comb_form;
  extern int do_nurbs;
  extern int mode;
  
  extern int write_dir_entry(FILE *fp, int entry[]);
  extern int write_vertex_list(struct nmgregion *r, struct bu_ptbl *vtab, FILE *fp_dir, FILE *fp_param);
  extern int write_edge_list(struct nmgregion *r, int vert_de, struct bu_ptbl *etab, struct bu_ptbl *vtab, FILE *fp_dir, FILE *fp_param);
  extern int write_shell_face_loop(char *name, struct nmgregion *r, int dependent, int edge_de, struct bu_ptbl *etab, int vert_de, struct bu_ptbl *vtab, FILE *fp_dir, FILE *fp_param);
  extern int write_solid_assembly(char *name, int de_list[], size_t length, int dependent, FILE *fp_dir, FILE *fp_param);
  extern int write_planar_nurb(struct faceuse *fu, vect_t u_dir, vect_t v_dir, fastf_t *u_max, fastf_t *v_max, point_t base_pt, FILE *fp_dir, FILE *fp_param);
  extern int write_name_entity(char *name, FILE *fp_dir, FILE *fp_param);
  extern int write_att_entity(struct iges_properties *props, FILE *fp_dir, FILE *fp_param);
  extern int nmg_to_iges(struct rt_db_internal *ip, char *name, FILE *fp_dir, FILE *fp_param);
  
  #define NO_OF_TYPES 31
  static int type_count[NO_OF_TYPES][2] = {
      { 106, 0 },	/* Copious Data */
      { 110, 0 },	/* Line */
      { 116, 0 },	/* Point */
      { 123, 0 },	/* Direction */
      { 124, 0 },	/* Transformation Matrix */
      { 126, 0 },	/* Rational B-spline Curve */
      { 128, 0 },	/* Rational B-spline Surface */
      { 142, 0 },	/* Curve on a Parametric Surface */
      { 144, 0 },	/* Trimmed Surface */
      { 150, 0 },	/* Block */
      { 152, 0 },	/* Right Angle Wedge */
      { 154, 0 },	/* Right Circular Cylinder */
      { 156, 0 },	/* Right Circular Cone Frustum */
      { 158, 0 },	/* Sphere */
      { 160, 0 },	/* Torus */
      { 164, 0 },	/* Solid of Linear Extrusion */
      { 168, 0 },	/* Ellipsoid */
      { 180, 0 },	/* Boolean Tree */
      { 184, 0 },	/* Solid Assembly */
      { 186, 0 },	/* Manifold Solid BREP Object */
      { 190, 0 },	/* Plane Surface */
      { 314, 0 },	/* Color */
      { 322, 0 },	/* Attribute Table Definition */
      { 406, 0 },	/* Property Entity */
      { 422, 0 },	/* Attribute Table Instance */
      { 430, 0 },	/* Solid Instance */
      { 502, 0 },	/* Vertex List */
      { 504, 0 },	/* Edge List */
      { 508, 0 },	/* Loop */
      { 510, 0 },	/* Face */
      { 514, 0 }	/* Shell */
  };
  
  
  static char *type_label[NO_OF_TYPES] = {
      "CopiusDa",
      "Line",
      "Point",
      "Direction",
      "Matrix",
      "B-spline",
      "NURB",
      "TrimCurv",
      "TrimSurf",
      "Block",
      "RA Wedge",
      "RC Cylin",
      "RC Frust",
      "Sphere",
      "Torus",
      "Extrusion",
      "Ellipsoid",
      "BoolTree",
      "Assembly",
      "BREP Obj",
      "PlaneSur",
      "Color",
      "Att Def",
      "Property",
      "Att Inst",
      "SolInst",
      "VertList",
      "EdgeList",
      "Loop",
      "Face",
      "Shell"
  };
  
  
  static char *type_name[NO_OF_TYPES] = {
      "Copious Data",
      "Line",
      "Point",
      "Direction",
      "Transformation Matrix",
      "Rational B_spline Curve",
      "NURB Surface",
      "Curve on a Parametric Surface",
      "Trimmed Surface",
      "Block",
      "Right Angle Wedge",
      "Right Circular Cylinder",
      "Right Circular Cone Frustum",
      "Sphere",
      "Torus",
      "Linear Sketch Extrusion",
      "Ellipsoid",
      "Boolean Tree",
      "Assembly Primitive",
      "Manifold Boundary Representation",
      "Plane Surface",
      "Color Definition",
      "Attribute Table Definition",
      "Property Entity",
      "Attribute Table Instance",
      "Primitive Instance",
      "Vertex List",
      "Edge List",
      "Loop",
      "Face",
      "Shell"
  };
  
  
  static unsigned char colortab[9][4] = {
      { 0, 217, 217, 217 }, /* index 0 actually represents an undefined color */
      { 1, 0, 0, 0 },
      { 2, 255, 0, 0 },
      { 3, 0, 255, 0 },
      { 4, 0, 0, 255 },
      { 5, 255, 255, 0 },
      { 6, 255, 0, 255 },
      { 7, 0, 255, 255 },
      { 8, 255, 255, 255 }};
  
  
  void
  get_props(struct iges_properties *props,
  	  struct rt_comb_internal *comb)
  {
      char *endp;
  
      RT_CK_COMB(comb);
  
      endp = strchr(bu_vls_addr(&comb->shader), ' ');
      memset(props->material_name, 0, sizeof(props->material_name));
      memset(props->material_params, 0, sizeof(props->material_params));
      if (endp) {
  	size_t len;
  	len = endp - bu_vls_addr(&comb->shader);
  	if (len > sizeof(props->material_name)) len = sizeof(props->material_name);
  	bu_strlcpy(props->material_name, bu_vls_addr(&comb->shader), len);
  	bu_strlcpy(props->material_params, endp+1, sizeof(props->material_params));
      } else {
  	bu_strlcpy(props->material_name, bu_vls_addr(&comb->shader), sizeof(props->material_name));
  	props->material_params[0] = '\0';
      }
      if (comb->region_flag) {
  	props->region_flag = 'R';
  	props->ident = comb->region_id;
  	props->air_code = comb->aircode;
  	props->material_code = comb->GIFTmater;
  	props->los_density = comb->los;
      }
      props->color_defined = (comb->rgb_valid ? 1 : 0);
      if (props->color_defined) {
  	props->color[0] = comb->rgb[0];
  	props->color[1] = comb->rgb[1];
  	props->color[2] = comb->rgb[2];
      } else {
  	props->color[0] = 0;
  	props->color[1] = 0;
  	props->color[2] = 0;
      }
      props->inherit = (comb->inherit ? 1 : 0);
  }
  
  
  int
  lookup_props(struct iges_properties *props,
  	     char *name)
  {
      struct directory *dp;
      struct rt_db_internal intern;
      struct rt_comb_internal *comb;
      int id;
  
      bu_strlcpy(props->name, name, NAMESIZE+1);
      props->material_name[0] = '\0';
      props->material_params[0] = '\0';
      props->region_flag = ' ';
      props->ident = 0;
      props->air_code = 0;
      props->material_code = 0;
      props->los_density = 0;
      props->color[0] = 0;
      props->color[1] = 0;
      props->color[2] = 0;
  
      if (name == NULL)
  	return 1;
  
      dp  = db_lookup(dbip, name, 1);
      if (dp == RT_DIR_NULL)
  	return 1;
  
      if (!(dp->d_flags & RT_DIR_COMB))
  	return 1;
  
      id = rt_db_get_internal(&intern, dp, dbip, (matp_t)NULL, &rt_uniresource);
  
      if (id < 0) {
  	rt_db_free_internal(&intern);
  	bu_log("Could not get internal form of %s\n", dp->d_namep);
  	return 1;
      }
  
      if (id != ID_COMBINATION) {
  	rt_db_free_internal(&intern);
  	bu_log("Directory/Database mismatch! is %s a combination or not???\n", dp->d_namep);
  	return 1;
      }
  
      comb = (struct rt_comb_internal *)intern.idb_ptr;
      RT_CK_COMB(comb);
  
      get_props(props, comb);
      rt_db_free_internal(&intern);
      return 0;
  }
  
  
  int
  write_freeform(FILE *fp,	/* output file */
  	       char *s,		/* the string to be output (must not contain any NL's) */
  	       int de,		/* the directory entry # that this belongs to (ignored for Global section) */
  	       char c)		/* 'G' for global section
  				 * 'P' for parameter section
  				 * 'S' for start section */
  {
      size_t paramlen = 0;
      int start_seq;
      size_t str_len;
      size_t line_start = 0;
      size_t line_end = 0;
      int *seq_no = NULL;
      size_t remaining_chars = 0;
      size_t i;
  
      if (c == 'P') {
  	seq_no = &param_seq;
  	paramlen = 64;
      } else if (c == 'G') {
  	seq_no = &global_len;
  	paramlen = 72;
      } else if (c == 'S') {
  	seq_no = &start_len;
  	paramlen = 72;
      } else
  	bu_exit(1, "Bad section character passed to 'write_freeform' (%c)\n", c);
  
      str_len = strlen(s);
      start_seq = (*seq_no);
  
  
      /* the start section is just one big string, so just print in pieces if necessary */
      if (c == 'S') {
  	line_start = 0;
  	while (line_start < str_len) {
  	    (*seq_no)++;
  	    fprintf(fp, global_form, &s[line_start], c, *seq_no);
  	    line_start += paramlen;
  	}
  	return *seq_no - start_seq;
      }
  
      /* Just print any string that will fit */
      if (str_len <= paramlen) {
  	(*seq_no)++;
  	if (c == 'P')
  	    fprintf(fp, param_form, s, de, c, *seq_no);
  	else
  	    fprintf(fp, global_form, s, c, *seq_no);
  	return *seq_no - start_seq;
      } else {
  	/* break string into lines */
  	size_t curr_loc = 0;
  	size_t field_start = 0;
  
  	while (1) {
  	    line_end = line_start + paramlen - 1;
  	    curr_loc = line_start;
  	    if (line_end >= str_len) {
  		/* write the last line and break */
  		(*seq_no)++;
  		if (c == 'P')
  		    fprintf(fp, param_form, &s[line_start], de, c, *seq_no);
  		else
  		    fprintf(fp, global_form, &s[line_start], c, *seq_no);
  		break;
  	    } else {
  		/* find the end of this line */
  		/* cannot extend numbers across lines,
  		 * but character strings may. The only way
  		 * to be sure is to interpret the entire string */
  
  		if (remaining_chars >= paramlen) {
  		    /* just print more of the string */
  		    (*seq_no)++;
  		    if (c == 'P')
  			fprintf(fp, param_form, &s[line_start], de, c, *seq_no);
  		    else
  			fprintf(fp, global_form, &s[line_start], c, *seq_no);
  		    remaining_chars -= paramlen;
  		} else {
  		    int done = 0;
  
  		    while (!done) {
  			char num[81];
  			int j = 0;
  
  			/* skip over any remainder of a string */
  			if (remaining_chars) {
  			    curr_loc = line_start + remaining_chars;
  			    remaining_chars = 0;
  			}
  			field_start = curr_loc;
  
  			if (s[curr_loc] == COMMA || s[curr_loc] == ';') {
  			    /* empty field */
  			    curr_loc++;
  			} else {
  			    while (isdigit((int)s[curr_loc]))
  				num[j++] = s[curr_loc++];
  			    num[j] = '\0';
  
  			    if (s[curr_loc] == 'H') {
  				/* This is a string */
  				int len;
  
  				len = atoi(num);
  
  				/* skip over the 'H' */
  				curr_loc++;
  
  				if (curr_loc + len >= line_end) {
  				    /* break this line in a string */
  				    (*seq_no)++;
  				    if (c == 'P')
  					fprintf(fp, param_form, &s[line_start], de, c, *seq_no);
  				    else
  					fprintf(fp, global_form, &s[line_start], c, *seq_no);
  				    remaining_chars = curr_loc + len - line_end;
  				    done = 1;
  				} else
  				    curr_loc += len + 1;
  			    } else {
  				/* this is not a string and cannot be continued to next line */
  
  				/* find end of this field */
  				while (curr_loc < str_len && s[curr_loc] != COMMA && s[curr_loc] != ';') {
  				    curr_loc++;
  				}
  
  				if (curr_loc < str_len && (s[curr_loc] == COMMA || s[curr_loc] == ';'))
  				    curr_loc++;
  
  				if (curr_loc > line_end) {
  				    /* end of line must be at start of this field */
  
  				    line_end = field_start-1;
  				    for (i = line_start; i <= line_end; i++)
  					fputc(s[i], fp);
  
  				    /* fill out line with blanks */
  				    for (i = 0; i < paramlen-(line_end-line_start+1); i++)
  					fputc(' ', fp);
  				    if (c == 'P')
  					fprintf(fp, " %7d", de);
  				    /* add columns 73 through 80 */
  				    (*seq_no)++;
  				    fprintf(fp, "%c%07d\n", c, *seq_no);
  				    done = 1;
  				}
  			    }
  			}
  		    }
  		}
  	    }
  	    line_start = line_end + 1;
  	}
  	return *seq_no - start_seq;
      }
  }
  
  
  int
  write_color_entity(unsigned char color[3],
  		   FILE *fp_dir, FILE *fp_param)
  {
      struct bu_vls str = BU_VLS_INIT_ZERO;
      int dir_entry[21];
      float c[3];
      size_t i;
  
      /* initialize directory entry */
      for (i = 0; i < 21; i++)
  	dir_entry[i] = DEFAULT;
  
      /* convert colors to percents */
      for (i = 0; i < 3; i++)
  	c[i] = (float)color[i]/2.55;
  
      bu_vls_printf(&str, "314,%g,%g,%g;", c[0], c[1], c[2]);
  
      dir_entry[1] = 314;
      dir_entry[2] = param_seq + 1;
      dir_entry[8] = 0;
      dir_entry[9] = 10201;
      dir_entry[11] = 314;
      dir_entry[15] = 0;
      dir_entry[14] = write_freeform(fp_param, bu_vls_addr(&str), dir_seq+1, 'P');
  
      bu_vls_free(&str);
  
      return write_dir_entry(fp_dir, dir_entry);
  }
  
  
  int
  get_color(unsigned char color[3],
  	  FILE *fp_dir, FILE *fp_param)
  {
      int color_de;
  
      for (color_de = 0; color_de < 9; color_de++) {
  	if (color[0] == colortab[color_de][1] &&
  	    color[1] == colortab[color_de][2] &&
  	    color[2] == colortab[color_de][3])
  	    break;
      }
  
      if (color_de == 9)
  	color_de = (-write_color_entity(color, fp_dir, fp_param));
  
      return color_de;
  }
  
  
  int
  write_attribute_definition(FILE *fp_dir, FILE *fp_param)
  {
      struct bu_vls str = BU_VLS_INIT_ZERO;
      int dir_entry[21];
      size_t i;
  
      /* initialize directory entry */
      for (i = 0; i < 21; i++)
  	dir_entry[i] = DEFAULT;
  
      /* start with parameter data */
      bu_vls_printf(&str, "322,%ldH%s,5001,9", strlen(att_string), att_string);
      bu_vls_printf(&str, ",1,3,1"); /* material name */
      bu_vls_printf(&str, ",2,3,1"); /* material parameters */
      bu_vls_printf(&str, ",3,6,1"); /* region flag (logical value) */
      bu_vls_printf(&str, ",4,1,1"); /* ident number */
      bu_vls_printf(&str, ",5,1,1"); /* air code number */
      bu_vls_printf(&str, ",6,1,1"); /* material code number */
      bu_vls_printf(&str, ",7,1,1"); /* los density (X100) */
      bu_vls_printf(&str, ",8,1,1"); /* inheritance */
      bu_vls_printf(&str, ",9,6,1;"); /* color_defined (logical value) */
  
  
      /* remember where parameter data is going */
      dir_entry[2] = param_seq + 1;
  
      /* get parameter line count */
      dir_entry[14] =  write_freeform(fp_param, bu_vls_addr(&str), dir_seq+1, 'P');
  
      /* write directory entry for line entity */
      dir_entry[1] = 322;
      dir_entry[8] = 0;
      dir_entry[9] = 10201;
      dir_entry[11] = 322;
      dir_entry[15] = 0;
  
      bu_vls_free(&str);
  
      return write_dir_entry(fp_dir, dir_entry);
  
  }
  
  
  void
  iges_init(struct bn_tol *set_tol,
  	  struct bg_tess_tol *set_ttol,
  	  int set_verbose,
  	  struct db_i *dbip_set)
  {
      BN_CK_TOL(set_tol);
      BG_CK_TESS_TOL(set_ttol);
      tol = (*set_tol);
      ttol = (*set_ttol);
      verbose = set_verbose;
      dbip = dbip_set;
  
      dir_seq = 0;
      param_seq = 0;
      start_len = 0;
      global_len = 0;
  }
  
  
  void
  Print_stats(FILE *fp)
  {
      size_t i;
      int total_entities = 0;
  
      fprintf(fp, "Wrote the following numbers and type of entities:\n");
      for (i = 0; i < NO_OF_TYPES; i++)
  	if (type_count[i][1] > 0) {
  	    total_entities += type_count[i][1];
  	    fprintf(fp, "\t%d - %s\n", type_count[i][1], type_name[i]);
  	}
  
      fprintf(fp, "Total of %d entities written\n", total_entities);
  
      if (solids_to_nmg)
  	fprintf(fp, "%d solids converted to NMG's before exporting to IGES\n", solids_to_nmg);
  }
  
  
  int
  write_dir_entry(FILE *fp, int entry[])
  {
      size_t i, j, type_index;
      const char *label;
  
      for (type_index = 0; type_index < NO_OF_TYPES; type_index++)
  	if (type_count[type_index][0] == entry[1])
  	    break;
      if (type_index == NO_OF_TYPES) {
  	bu_log("Writing directory entry for an unknown entity type (%d)\n", entry[1]);
  	label = unknown;
  	unknown_count++;
  	entry[19] = unknown_count;
      } else {
  	label = type_label[type_index];
  	type_count[type_index][1]++;
  	entry[19] = type_count[type_index][1];
      }
  
      entry[10] = dir_seq + 1;
      entry[20] = dir_seq + 2;
  
      for (j = 0; j < 2; j++) {
  
  	for (i = j*10+1; i < (j+1)*10; i++) {
  	    if (i == 18)
  		fprintf(fp, "%8.8s", label);
  	    else if (entry[i] == DEFAULT)
  		fprintf(fp, "        ");
  	    else if (i == 9)
  		fprintf(fp, "%08d", entry[i]);
  	    else
  		fprintf(fp, "%8d", entry[i]);
  	}
  	fprintf(fp, "D%07d\n", entry[(j+1)*10]);
      }
  
      dir_seq += 2;
      return dir_seq - 1;
  }
  
  
  void
  w_start_global(
      FILE *fp_dir,
      FILE *fp_param,
      const char *db_name,
      const char *prog_name,
      const char *output_file,
      const char *id,
      const char *version)
  {
      struct bu_vls str = BU_VLS_INIT_ZERO;
      time_t now;
      struct tm *timep;
      struct stat db_stat;
  
      /* Write Start Section */
      bu_vls_printf(&str, "This IGES file created by %s from the database %s.", prog_name, db_name);
      write_freeform(fp_dir, bu_vls_addr(&str), 0, 'S');
      bu_vls_free(&str);
  
      /* Write Global Section */
      bu_vls_printf(&str, ",,%zdH%s", strlen(db_name), db_name);
  
      if (output_file == NULL)
  	bu_vls_printf(&str, ",7Hstd_out");
      else
  	bu_vls_printf(&str, ",%zdH%s", strlen(output_file), output_file);
  
      bu_vls_printf(&str, ",%zdH%s,%zdH%s,32,38,6,308,15,%zdH%s,1.0,2,2HMM,,1.0" ,
  		  strlen(version), version ,
  		  strlen(id), id,
  		  strlen(db_name), db_name);
  
      (void)time(&now);
      timep = localtime(&now);
      bu_vls_printf(&str, ",15H%04d%02d%02d.%02d%02d%02d",
  		  timep->tm_year+1900,
  		  timep->tm_mon + 1,
  		  timep->tm_mday,
  		  timep->tm_hour,
  		  timep->tm_min,
  		  timep->tm_sec);
  
      bu_vls_printf(&str, ",%g,100000.0,7HUnknown,7HUnknown,9,0" ,
  		  RT_LEN_TOL);
  
      if (stat(db_name, &db_stat)) {
  	bu_log("Cannot stat %s\n", db_name);
  	perror(prog_name);
  	bu_vls_strcat(&str, ",15H00000101.000000;");
      } else {
  	timep = localtime(&db_stat.st_mtime);
  	bu_vls_printf(&str, ",15H%04d%02d%02d.%02d%02d%02d;",
  		      timep->tm_year+1900,
  		      timep->tm_mon + 1,
  		      timep->tm_mday,
  		      timep->tm_hour,
  		      timep->tm_min,
  		      timep->tm_sec);
      }
  
      write_freeform(fp_dir, bu_vls_addr(&str), 0, 'G');
  
      /* write attribute definition entity */
  
      if (mode != TRIMMED_SURF_MODE)
  	attribute_de = write_attribute_definition(fp_dir, fp_param);
  
      bu_vls_free(&str);
  }
  
  
  int
  nmgregion_to_iges(char *name,
  		  struct nmgregion *r,
  		  int dependent,
  		  FILE *fp_dir, FILE *fp_param)
  {
      struct nmgregion *new_r;		/* temporary nmgregion */
      struct shell *s_new;		/* shell made by nmg_mrsv */
      struct bu_ptbl vtab;		/* vertex table */
      struct bu_ptbl etab;		/* edge table */
      struct bu_ptbl **shells;	/* array of tables of shells */
      int *brep_de;	/* Directory entry sequence # for BREP Object(s) */
      int vert_de;	/* Directory entry sequence # for vertex list */
      int edge_de;	/* Directory entry sequence # for edge list */
      size_t outer_shell_count; /* number of outer shells in nmgregion */
      size_t face_count = 0;	/* number of faces in nmgregion */
      size_t i;
  
      NMG_CK_REGION(r);
  
      {
  	struct shell *s;
  	for (BU_LIST_FOR(s, shell, &r->s_hd)) {
  	    struct faceuse *fu;
  
  	    for (BU_LIST_FOR(fu, faceuse, &s->fu_hd)) {
  		struct loopuse *lu;
  
  		if (fu->orientation != OT_SAME)
  		    continue;
  
  		face_count++;
  
  		for (BU_LIST_FOR(lu, loopuse, &fu->lu_hd)) {
  		    struct edgeuse *eu;
  
  		    if (BU_LIST_FIRST_MAGIC(&lu->down_hd) != NMG_EDGEUSE_MAGIC)
  			continue;
  
  		    for (BU_LIST_FOR(eu, edgeuse, &lu->down_hd)) {
  			struct vertex *v;
  
  			v = eu->vu_p->v_p;
  			NMG_CK_VERTEX(v);
  			if (!v->vg_p)
  			    bu_log("vertex with no geometry!\n");
  		    }
  		}
  	    }
  	}
      }
  
      if (face_count == 0)
  	return 0;
  
      /* Find outer shells and void shells and their associations */
      outer_shell_count = nmg_find_outer_and_void_shells(r, &shells, &RTG.rtg_vlfree, &tol);
  
      brep_de = (int *)bu_calloc(outer_shell_count, sizeof(int), "nmgregion_to_iges: brep_de");
  
      for (i = 0; i < outer_shell_count; i++) {
  	int j;
  	int tmp_dependent;
  	struct shell *s;
  	char *tmp_name;
  
  	if (outer_shell_count == 1) {
  	    new_r = r;
  	    s_new = NULL;
  	    tmp_name = name;
  	    tmp_dependent = dependent;
  	} else {
  	    new_r = nmg_mrsv(r->m_p);
  	    s_new = BU_LIST_FIRST(shell, &new_r->s_hd);
  	    tmp_name = NULL;
  	    tmp_dependent = 1;
  
  	    for (j = BU_PTBL_LEN(shells[i])-1; j >= 0; j--) {
  		s = (struct shell *)BU_PTBL_GET(shells[i], j);
  		nmg_mv_shell_to_region(s, new_r);
  	    }
  	    (void)nmg_ks(s_new);
  	}
  
  
  	/* Make the vertex list entity */
  	vert_de = write_vertex_list(new_r, &vtab, fp_dir, fp_param);
  
  	/* Make the edge list entity */
  	edge_de = write_edge_list(new_r, vert_de, &etab, &vtab, fp_dir, fp_param);
  
  	/* Make the face, loop, shell entities */
  	brep_de[i] = write_shell_face_loop(tmp_name, new_r, tmp_dependent, edge_de, &etab, vert_de, &vtab, fp_dir, fp_param);
  
  	/* Clear the tables */
  	(void)bu_ptbl_reset(&vtab);
  	(void)bu_ptbl_reset(&etab);
  
  	if (outer_shell_count != 1)
  	    (void)nmg_kr(new_r);
      }
  
      /* Free the tables */
      (void)bu_ptbl_free(&vtab);
      (void)bu_ptbl_free(&etab);
  
      if (outer_shell_count != 1)
  	return write_solid_assembly(name, brep_de, outer_shell_count, dependent, fp_dir, fp_param);
      else
  	return brep_de[0];
  }
  
  
  int
  verts_to_copious_data(point_t *pts,
  		      size_t vert_count,
  		      size_t pt_size,
  		      FILE *fp_dir, FILE *fp_param)
  {
      struct bu_vls str = BU_VLS_INIT_ZERO;
      int dir_entry[21];
      size_t i;
  
      if (vert_count < 2)
  	return 0;
  
      /* initialize directory entry */
      for (i = 0; i < 21; i++)
  	dir_entry[i] = DEFAULT;
  
      bu_vls_printf(&str, "106,%zu,%zu", pt_size-1, vert_count+1);
      if (pt_size == 2) {
  	bu_vls_printf(&str, ",0.0");
  	for (i = 0; i < vert_count; i++)
  	    bu_vls_printf(&str, ",%f,%f", pts[i][0], pts[i][1]);
  	bu_vls_printf(&str, ",%f,%f", pts[0][0], pts[0][1]);
      } else if (pt_size == 3) {
  	for (i = 0; i < vert_count; i++)
  	    bu_vls_printf(&str, ",%f,%f,%f", V3ARGS(pts[i]));
  	bu_vls_printf(&str, ",%f,%f,%f", V3ARGS(pts[0]));
      }
  
      bu_vls_strcat(&str, ";");
  
      /* remember where parameter data is going */
      dir_entry[2] = param_seq + 1;
  
      /* get parameter line count */
      dir_entry[14] = write_freeform(fp_param, bu_vls_addr(&str), dir_seq+1, 'P');
  
      /* write directory entry for vertex list entity */
      dir_entry[1] = 106;
      dir_entry[8] = 0;
      if (pt_size == 2) {
  	dir_entry[9] = 10500;
  	dir_entry[15] = 63;
      } else {
  	dir_entry[9] = 10000;
  	dir_entry[15] = 12;
      }
      dir_entry[11] = 106;
  
  
      bu_vls_free(&str);
  
      return write_dir_entry(fp_dir, dir_entry);
  }
  
  
  int
  nmg_loop_to_tcurve(
      struct loopuse *lu,
      int surf_de,
      vect_t u_dir, vect_t v_dir,
      fastf_t u_max, fastf_t v_max,
      point_t base_pt,
      FILE *fp_dir, FILE *fp_param)
  {
      struct bu_vls str = BU_VLS_INIT_ZERO;
      int dir_entry[21];
      struct edgeuse *eu;
      point_t *model_pts;
      point_t *param_pts;
      size_t vert_count = 0;
      size_t i;
  
      NMG_CK_LOOPUSE(lu);
  
      if (BU_LIST_FIRST_MAGIC(&lu->down_hd) != NMG_EDGEUSE_MAGIC)
  	return 0;
  
      /* count vertices */
      for (BU_LIST_FOR(eu, edgeuse, &lu->down_hd))
  	vert_count++;
  
      if (vert_count < 3)
  	return 0;
  
      /* Allocate memory for points */
      model_pts = (point_t *)bu_calloc(vert_count, sizeof(point_t), "nmg_loop_to_tcurve: model_pts");
      param_pts = (point_t *)bu_calloc(vert_count, sizeof(point_t), "nmg_loop_to_tcurve: param_pts");
  
      /* initialize directory entry */
      for (i = 0; i < 21; i++)
  	dir_entry[i] = DEFAULT;
  
      bu_vls_printf(&str, "142,0,%d", surf_de);
  
      i = 0;
      for (BU_LIST_FOR(eu, edgeuse, &lu->down_hd)) {
  	struct vertex_g *vg;
  	vect_t from_base;
  	fastf_t u, v;
  
  	NMG_CK_EDGEUSE(eu);
  	NMG_CK_VERTEXUSE(eu->vu_p);
  	NMG_CK_VERTEX(eu->vu_p->v_p);
  	NMG_CK_VERTEX_G(eu->vu_p->v_p->vg_p);
  	vg = eu->vu_p->v_p->vg_p;
  
  	if (i >= vert_count)
  	    bu_exit(1, "nmg_loop_to_tcurve: too many vertices in loop!\n");
  
  	VMOVE(model_pts[i], vg->coord);
  
  	VSUB2(from_base, vg->coord, base_pt);
  	u = VDOT(u_dir, from_base)/u_max;
  	v = VDOT(v_dir, from_base)/v_max;
  	CLAMP(u, 0.0, 1.0);
  	CLAMP(v, 0.0, 1.0);
  
  	VSET(param_pts[i], u, v, 0.0);
  
  	i++;
      }
  
      bu_vls_printf(&str, ",%d", verts_to_copious_data(param_pts, vert_count, 2, fp_dir, fp_param));
      bu_vls_printf(&str, ",%d,0;", verts_to_copious_data(model_pts, vert_count, 3, fp_dir, fp_param));
  
      /* remember where parameter data is going */
      dir_entry[2] = param_seq + 1;
  
      /* get parameter line count */
      dir_entry[14] = write_freeform(fp_param, bu_vls_addr(&str), dir_seq+1, 'P');
  
      /* write directory entry for vertex list entity */
      dir_entry[1] = 142;
      dir_entry[8] = 0;
      dir_entry[9] = 10500;
      dir_entry[11] = 142;
      dir_entry[15] = 0;
  
  
      bu_vls_free(&str);
  
      return write_dir_entry(fp_dir, dir_entry);
  }
  
  
  void
  nmg_fu_to_tsurf(struct faceuse *fu,
  		FILE *fp_dir, FILE *fp_param)
  {
      struct bu_vls str = BU_VLS_INIT_ZERO;
      int dir_entry[21];
      struct loopuse *lu;
      int surf_de;
      vect_t u_dir = VINIT_ZERO;
      vect_t v_dir = VINIT_ZERO;
      point_t base_pt = VINIT_ZERO;
      fastf_t u_max = 0.0;
      fastf_t v_max = 0.0;
      int *curve_de;
      size_t loop_count = 0;
      size_t i;
  
      NMG_CK_FACEUSE(fu);
  
      /* initialize directory entry */
      for (i = 0; i < 21; i++)
  	dir_entry[i] = DEFAULT;
  
      /* count loops */
      for (BU_LIST_FOR(lu, loopuse, &fu->lu_hd)) {
  	NMG_CK_LOOPUSE(lu);
  
  	if (BU_LIST_FIRST_MAGIC(&lu->down_hd) != NMG_EDGEUSE_MAGIC)
  	    continue;
  
  	loop_count++;
      }
  
      if (loop_count < 1)	/* nothing to output */
  	return;
  
      /* write underlying surface */
      surf_de = write_planar_nurb(fu, u_dir, v_dir, &u_max, &v_max, base_pt, fp_dir, fp_param);
  
      /* allocate space to hold DE for each trimming curve */
      curve_de = (int *)bu_calloc(loop_count, sizeof(int), "nmg_fu_to_tsurf: curve_de");
  
      i = (-1);
      for (BU_LIST_FOR(lu, loopuse, &fu->lu_hd)) {
  	if (BU_LIST_FIRST_MAGIC(&lu->down_hd) != NMG_EDGEUSE_MAGIC)
  	    continue;
  
  	curve_de[++i] = nmg_loop_to_tcurve(lu, surf_de, u_dir, v_dir, u_max, v_max, base_pt, fp_dir, fp_param);
      }
  
      bu_vls_printf(&str, "144,%d,0,%zu", surf_de, loop_count-1);
      for (i = 0; i < loop_count; i++)
  	bu_vls_printf(&str, ",%d", curve_de[i]);
      bu_vls_strcat(&str, ";");
  
      bu_free((char *)curve_de, "nmg_fu_to_tsurf: curve_de");
  
      /* remember where parameter data is going */
      dir_entry[2] = param_seq + 1;
  
      /* get parameter line count */
      dir_entry[14] = write_freeform(fp_param, bu_vls_addr(&str), dir_seq+1, 'P');
  
      /* write directory entry for vertex list entity */
      dir_entry[1] = 144;
      dir_entry[8] = 0;
      dir_entry[9] = 0;
      dir_entry[11] = 144;
      dir_entry[15] = 0;
  
      bu_vls_free(&str);
  
      (void)write_dir_entry(fp_dir, dir_entry);
  
  }
  
  
  int nmgregion_to_tsurf(char *UNUSED(name), struct nmgregion *r, FILE *fp_dir, FILE *fp_param)
  {
      struct shell *s;
  
      NMG_CK_REGION(r);
  
      for (BU_LIST_FOR(s, shell, &r->s_hd)) {
  	struct faceuse *fu;
  
  	NMG_CK_SHELL(s);
  	for (BU_LIST_FOR(fu, faceuse, &s->fu_hd)) {
  	    NMG_CK_FACEUSE(fu);
  
  	    if (fu->orientation != OT_SAME)
  		continue;
  
  	    nmg_fu_to_tsurf(fu, fp_dir, fp_param);
  	}
      }
  
      return -1;
  }
  
  
  int
  write_vertex_list(struct nmgregion *r,
  		  struct bu_ptbl *vtab,   /* vertex table */
  		  FILE *fp_dir, FILE *fp_param)
  {
      struct bu_vls str = BU_VLS_INIT_ZERO;
      int dir_entry[21];
      size_t i;
  
      NMG_CK_REGION(r);
  
      /* initialize directory entry */
      for (i = 0; i < 21; i++)
  	dir_entry[i] = DEFAULT;
  
      /* Built list of vertex structs */
      nmg_vertex_tabulate(vtab, &r->l.magic, &RTG.rtg_vlfree);
  
      /* write parameter data for vertex list entity */
      bu_vls_printf(&str, "502,%ld", (long int)BU_PTBL_LEN(vtab));
  
      for (i = 0; i < BU_PTBL_LEN(vtab); i++) {
  	struct vertex *v;
  	struct vertex_g *vg;
  
  	v = (struct vertex *)BU_PTBL_GET(vtab, i);
  	NMG_CK_VERTEX(v);
  	vg = v->vg_p;
  	if (!vg) {
  	    bu_log("No geometry for vertex %p #%zu in table\n", (void *)v, i);
  	} else {
  	    NMG_CK_VERTEX_G(vg);
  	    bu_vls_printf(&str, ",%g,%g,%g",
  			  vg->coord[X],
  			  vg->coord[Y],
  			  vg->coord[Z]);
  	}
      }
      bu_vls_strcat(&str, ";");
  
      /* remember where parameter data is going */
      dir_entry[2] = param_seq + 1;
  
      /* get parameter line count */
      dir_entry[14] = write_freeform(fp_param, bu_vls_addr(&str), dir_seq+1, 'P');
  
      /* write directory entry for vertex list entity */
      dir_entry[1] = 502;
      dir_entry[8] = 0;
      dir_entry[9] = 10001;
      dir_entry[11] = 502;
      dir_entry[15] = 1;
  
  
      bu_vls_free(&str);
  
      return write_dir_entry(fp_dir, dir_entry);
  }
  
  
  int
  write_line_entity(struct vertex_g *start_vg,
  		  struct vertex_g *end_vg,
  		  FILE *fp_dir, FILE *fp_param)
  {
      struct bu_vls str = BU_VLS_INIT_ZERO;
      int dir_entry[21];
      size_t i;
  
      NMG_CK_VERTEX_G(start_vg);
      NMG_CK_VERTEX_G(end_vg);
  
      /* initialize directory entry */
      for (i = 0; i < 21; i++)
  	dir_entry[i] = DEFAULT;
  
      /* start with parameter data */
      bu_vls_printf(&str, "110,%g,%g,%g,%g,%g,%g;" ,
  		  start_vg->coord[X],
  		  start_vg->coord[Y],
  		  start_vg->coord[Z],
  		  end_vg->coord[X],
  		  end_vg->coord[Y],
  		  end_vg->coord[Z]);
  
      /* remember where parameter data is going */
      dir_entry[2] = param_seq + 1;
  
      /* get parameter line count */
      dir_entry[14] =  write_freeform(fp_param, bu_vls_addr(&str), dir_seq+1, 'P');
  
      /* write directory entry for line entity */
      dir_entry[1] = 110;
      dir_entry[8] = 0;
      dir_entry[9] = 10001;
      dir_entry[11] = 110;
      dir_entry[15] = 0;
  
      bu_vls_free(&str);
  
      return write_dir_entry(fp_dir, dir_entry);
  }
  
  
  int
  write_linear_bspline(struct vertex_g *start_vg,
  		     struct vertex_g *end_vg,
  		     FILE *fp_dir, FILE *fp_param)
  {
      struct bu_vls str = BU_VLS_INIT_ZERO;
      int dir_entry[21];
      size_t i;
  
      NMG_CK_VERTEX_G(start_vg);
      NMG_CK_VERTEX_G(end_vg);
  
      /* initialize directory entry */
      for (i = 0; i < 21; i++)
  	dir_entry[i] = DEFAULT;
  
      /* start with parameter data */
      bu_vls_printf(&str, "126,1,1,0,0,1,0,0.,0.,1.,1.,1.,1.,%g,%g,%g,%g,%g,%g,0.,1.;" ,
  		  start_vg->coord[X],
  		  start_vg->coord[Y],
  		  start_vg->coord[Z],
  		  end_vg->coord[X],
  		  end_vg->coord[Y],
  		  end_vg->coord[Z]);
  
      /* remember where parameter data is going */
      dir_entry[2] = param_seq + 1;
  
      /* get parameter line count */
      dir_entry[14] =  write_freeform(fp_param, bu_vls_addr(&str), dir_seq+1, 'P');
  
      /* write directory entry for line entity */
      dir_entry[1] = 126;
      dir_entry[8] = 0;
      dir_entry[9] = 10001;
      dir_entry[11] = 126;
      dir_entry[15] = 1;
  
      bu_vls_free(&str);
  
      return write_dir_entry(fp_dir, dir_entry);
  }
  
  
  int
  write_edge_list(struct nmgregion *r,
  		int vert_de,		/* DE# for vertex list */
  		struct bu_ptbl *etab,	/* edge table */
  		struct bu_ptbl *vtab,	/* vertex table (already filled in) */
  		FILE *fp_dir, FILE *fp_param)
  {
      struct bu_vls str = BU_VLS_INIT_ZERO;
      int dir_entry[21];
      size_t i;
  
      NMG_CK_REGION(r);
  
      /* initialize directory entry */
      for (i = 0; i < 21; i++)
  	dir_entry[i] = DEFAULT;
  
      /* Build list of edge structures */
      nmg_edge_tabulate(etab, &r->l.magic, &RTG.rtg_vlfree);
  
      bu_vls_printf(&str, "504,%ld", (long int)BU_PTBL_LEN(etab));
  
      /* write parameter data for edge list entity */
      for (i = 0; i < BU_PTBL_LEN(etab); i++) {
  	struct edge *e;
  	struct edgeuse *eu;
  	struct vertexuse *vu;
  	struct vertex *start_v, *end_v;
  	struct vertex_g *start_vg, *end_vg;
  	int line_de; /* directory entry # for line entity */
  
  	e = (struct edge *)BU_PTBL_GET(etab, i);
  	NMG_CK_EDGE(e);
  	eu = e->eu_p;
  	NMG_CK_EDGEUSE(eu);
  	vu = eu->vu_p;
  	NMG_CK_VERTEXUSE(vu);
  	start_v = vu->v_p;
  	NMG_CK_VERTEX(start_v);
  	start_vg = start_v->vg_p;
  	NMG_CK_VERTEX_G(start_vg);
  	vu = eu->eumate_p->vu_p;
  	NMG_CK_VERTEXUSE(vu);
  	end_v = vu->v_p;
  	NMG_CK_VERTEX(end_v);
  	end_vg = end_v->vg_p;
  	NMG_CK_VERTEX_G(end_vg);
  	if (do_nurbs)
  	    line_de = write_linear_bspline(start_vg, end_vg, fp_dir, fp_param);
  	else
  	    line_de = write_line_entity(start_vg, end_vg, fp_dir, fp_param);
  	bu_vls_printf(&str, ",%d,%d,%jd,%d,%jd",
  		      line_de,
  		      vert_de, bu_ptbl_locate(vtab, (long *)start_v) + 1,
  		      vert_de, bu_ptbl_locate(vtab, (long *)end_v) + 1);
      }
      bu_vls_strcat(&str, ";");
  
      /* remember where parameter data is going */
      dir_entry[2] = param_seq + 1;
  
      /* get parameter line count */
      dir_entry[14] = write_freeform(fp_param, bu_vls_addr(&str), dir_seq+1, 'P');
  
      /* write directory entry for edge list entity */
      dir_entry[1] = 504;
      dir_entry[8] = 0;
      dir_entry[9] = 10001;
      dir_entry[11] = 504;
      dir_entry[15] = 1;
  
      bu_vls_free(&str);
  
      return write_dir_entry(fp_dir, dir_entry);
  }
  
  
  int
  write_point_entity(point_t pt,
  		   FILE *fp_dir, FILE *fp_param)
  {
      struct bu_vls str = BU_VLS_INIT_ZERO;
      int dir_entry[21];
      size_t i;
  
      for (i = 0; i < 21; i++)
  	dir_entry[i] = DEFAULT;
  
      bu_vls_printf(&str, "116,%g,%g,%g,0;" ,
  		  pt[X],
  		  pt[Y],
  		  pt[Z]);
  
      /* remember where parameter data is going */
      dir_entry[2] = param_seq + 1;
  
      /* get parameter line count */
      dir_entry[14] = write_freeform(fp_param, bu_vls_addr(&str), dir_seq+1, 'P');
  
      dir_entry[1] = 116;
      dir_entry[8] = 0;
      dir_entry[9] = 10001;
      dir_entry[11] = 116;
      dir_entry[15] = 0;
  
      bu_vls_free(&str);
  
      return write_dir_entry(fp_dir, dir_entry);
  }
  
  
  int
  write_direction_entity(point_t pt,
  		       FILE *fp_dir, FILE *fp_param)
  {
      struct bu_vls str = BU_VLS_INIT_ZERO;
      int dir_entry[21];
      size_t i;
  
      for (i = 0; i < 21; i++)
  	dir_entry[i] = DEFAULT;
  
      bu_vls_printf(&str, "123,%g,%g,%g;" ,
  		  pt[X],
  		  pt[Y],
  		  pt[Z]);
  
      /* remember where parameter data is going */
      dir_entry[2] = param_seq + 1;
  
      /* get parameter line count */
      dir_entry[14] = write_freeform(fp_param, bu_vls_addr(&str), dir_seq+1, 'P');
  
      dir_entry[1] = 123;
      dir_entry[8] = 0;
      dir_entry[9] = 10001;
      dir_entry[11] = 123;
      dir_entry[15] = 0;
  
      bu_vls_free(&str);
  
      return write_dir_entry(fp_dir, dir_entry);
  }
  
  
  int
  write_plane_entity(plane_t plane,
  		   FILE *fp_dir, FILE *fp_param)
  {
      struct bu_vls str = BU_VLS_INIT_ZERO;
      point_t pt_on_plane;	/* a point on the plane */
      int dir_entry[21];
      size_t i;
  
      for (i = 0; i < 21; i++)
  	dir_entry[i] = DEFAULT;
  
      VSCALE(pt_on_plane, plane, plane[W]);
  
      bu_vls_printf(&str, "190,%d,%d;" ,
  		  write_point_entity(pt_on_plane, fp_dir, fp_param),
  		  write_direction_entity(plane, fp_dir, fp_param));
  
      /* remember where parameter data is going */
      dir_entry[2] = param_seq + 1;
  
      /* get parameter line count */
      dir_entry[14] = write_freeform(fp_param, bu_vls_addr(&str), dir_seq+1, 'P');
  
      dir_entry[1] = 190;
      dir_entry[8] = 0;
      dir_entry[9] = 10001;
      dir_entry[11] = 190;
      dir_entry[15] = 0;
  
      bu_vls_free(&str);
  
      return write_dir_entry(fp_dir, dir_entry);
  
  }
  
  
  int
  write_planar_nurb(struct faceuse *fu,
  		  vect_t u_dir, vect_t v_dir,	   /* output */
  		  fastf_t *u_max, fastf_t *v_max,  /* output */
  		  point_t base_pt,                 /* output (point at u, v==0) */
  		  FILE *fp_dir,
  		  FILE *fp_param)
  {
      struct loopuse *lu;
      struct edgeuse *eu, *eu_next;
      struct vertex_g *vg, *vg_next;
      point_t ctl_pt;
      fastf_t umin, umax, vmin, vmax;
      struct bu_vls str = BU_VLS_INIT_ZERO;
      int dir_entry[21];
      size_t i;
  
      NMG_CK_FACEUSE(fu);
  
      for (i = 0; i < 21; i++)
  	dir_entry[i] = DEFAULT;
  
      /* create direction vectors in u and v directions in plane */
      lu = BU_LIST_FIRST(loopuse, &fu->lu_hd);
      NMG_CK_LOOPUSE(lu);
      while (BU_LIST_FIRST_MAGIC(&lu->down_hd) != NMG_EDGEUSE_MAGIC &&
  	   BU_LIST_NOT_HEAD(lu, &fu->lu_hd)) {
  	lu = BU_LIST_PNEXT(loopuse, lu);
  	NMG_CK_LOOPUSE(lu);
      }
      if (BU_LIST_FIRST_MAGIC(&lu->down_hd) != NMG_EDGEUSE_MAGIC) {
  	bu_log("Write_planar_nurb: could not find a loop (with edges) in face\n");
  	return 0;
      }
  
      eu = BU_LIST_FIRST(edgeuse, &lu->down_hd);
      NMG_CK_EDGEUSE(eu);
      vg = eu->vu_p->v_p->vg_p;
      NMG_CK_VERTEX_G(vg);
      eu_next = BU_LIST_PNEXT(edgeuse, eu);
      NMG_CK_EDGEUSE(eu_next);
      vg_next = eu_next->vu_p->v_p->vg_p;
      NMG_CK_VERTEX_G(vg_next);
  
      VSUB2(u_dir, vg_next->coord, vg->coord);
      VUNITIZE(u_dir);
      VCROSS(v_dir, fu->f_p->g.plane_p->N, u_dir);
      VUNITIZE(v_dir);
  
      /* find the max and min distances from vg along u_dir and v_dir in the face */
      umin = MAX_FASTF;
      vmin = MAX_FASTF;
      umax = (-umin);
      vmax = (-vmin);
  
      for (BU_LIST_FOR(lu, loopuse, &fu->lu_hd)) {
  	NMG_CK_LOOPUSE(lu);
  	if (BU_LIST_FIRST_MAGIC(&lu->down_hd) != NMG_EDGEUSE_MAGIC)
  	    continue;
  
  	for (BU_LIST_FOR(eu, edgeuse, &lu->down_hd)) {
  	    vect_t tmp_vect;
  	    fastf_t distu, distv;
  
  	    NMG_CK_EDGEUSE(eu);
  
  	    VSUB2(tmp_vect, eu->vu_p->v_p->vg_p->coord, vg->coord);
  	    distu = VDOT(tmp_vect, u_dir);
  	    V_MIN(umin, distu);
  	    V_MAX(umax, distu);
  	    distv = VDOT(tmp_vect, v_dir);
  	    V_MIN(vmin, distv);
  	    V_MAX(vmax, distv);
  	}
      }
  
      *u_max = umax - umin;
      *v_max = vmax - vmin;
  
      /* Put preliminary stuff for planar nurb in string */
      bu_vls_printf(&str, "128,1,1,1,1,0,0,1,0,0,0.,0.,1.,1.,0.,0.,1.,1.,1.,1.,1.,1.");
  
      /* Now put control points in string */
      VJOIN2(ctl_pt, vg->coord, umin, u_dir, vmin, v_dir);
      VMOVE(base_pt, ctl_pt);
      bu_vls_printf(&str, ",%g,%g,%g", V3ARGS(ctl_pt));
      VJOIN1(ctl_pt, ctl_pt, umax-umin, u_dir);
      bu_vls_printf(&str, ",%g,%g,%g", V3ARGS(ctl_pt));
      VJOIN2(ctl_pt, vg->coord, umin, u_dir, vmax, v_dir);
      bu_vls_printf(&str, ",%g,%g,%g", V3ARGS(ctl_pt));
      VJOIN1(ctl_pt, ctl_pt, umax-umin, u_dir);
      bu_vls_printf(&str, ",%g,%g,%g", V3ARGS(ctl_pt));
  
      /* Now put parameter ranges last */
      bu_vls_printf(&str, ",0.,1.,0.,1.;");
  
      /* remember where parameter data is going */
      dir_entry[2] = param_seq + 1;
  
      /* get parameter line count */
      dir_entry[14] = write_freeform(fp_param, bu_vls_addr(&str), dir_seq+1, 'P');
  
      dir_entry[1] = 128;
      dir_entry[8] = 0;
      dir_entry[9] = 10001;
      dir_entry[11] = 128;
      dir_entry[15] = 1;
  
      bu_vls_free(&str);
  
      return write_dir_entry(fp_dir, dir_entry);
  
  }
  
  
  int
  write_shell_face_loop(char *name,
  		      struct nmgregion *r,
  		      int dependent,
  		      int edge_de,		/* directory entry # for edge list */
  		      struct bu_ptbl *etab,	/* Table of edge pointers */
  		      int vert_de,		/* directory entry # for vertex list */
  		      struct bu_ptbl *vtab,	/* Table of vertex pointers */
  		      FILE *fp_dir, FILE *fp_param)
  {
      struct edgeuse *eu;
      struct faceuse *fu;
      struct loopuse *lu;
      struct shell *s;
      struct vertex *v;
      struct bu_vls str = BU_VLS_INIT_ZERO;
      struct iges_properties props;
      int *shell_list;
      size_t i;
      size_t shell_count = 0;
      int dir_entry[21];
      int name_de;
      int prop_de;
      int color_de = DEFAULT;
  
      NMG_CK_REGION(r);
  
      /* count the shells */
      for (BU_LIST_FOR(s, shell, &r->s_hd))
  	shell_count++;
  
      /* make space for the list of shell DE's */
      shell_list = (int *)bu_calloc(shell_count, sizeof(int), "write_shell_face_loop: shell_list");
  
      shell_count = 0;
      for (BU_LIST_FOR(s, shell, &r->s_hd)) {
  	int *face_list;
  	size_t face_count = 0;
  
  
  	/* Count faces */
  	for (BU_LIST_FOR(fu, faceuse, &s->fu_hd)) {
  	    NMG_CK_FACEUSE(fu);
  	    if (fu->orientation != OT_SAME)
  		continue;
  	    face_count++;
  	}
  	face_list = (int *)bu_calloc(face_count, sizeof(int), "face_list");
  	face_count = 0;
  
  	/* Shell is made of faces. */
  	for (BU_LIST_FOR(fu, faceuse, &s->fu_hd)) {
  	    int *loop_list;
  	    size_t loop_count = 0;
  	    long exterior_loop = (-1);	/* index of outer loop (in loop_list) */
  	    int outer_loop_flag = 1;	/* IGES flag to indicate a selected outer loop */
  
  	    if (fu->orientation != OT_SAME)
  		continue;
  
  	    /* Count loops */
  	    for (BU_LIST_FOR(lu, loopuse, &fu->lu_hd)) {
  		NMG_CK_LOOPUSE(lu);
  		if (lu->orientation == OT_SAME)
  		    exterior_loop = loop_count;
  		loop_count++;
  	    }
  	    loop_list = (int *)bu_calloc(loop_count, sizeof(int), "loop_list");
  
  	    loop_count = 0;
  	    for (BU_LIST_FOR(lu, loopuse, &fu->lu_hd)) {
  		int edge_count = 0;
  
  		/* Count edges */
  		if (BU_LIST_FIRST_MAGIC(&lu->down_hd) == NMG_EDGEUSE_MAGIC) {
  		    for (BU_LIST_FOR(eu, edgeuse, &lu->down_hd)) {
  			NMG_CK_EDGEUSE(eu);
  			NMG_CK_EDGE(eu->e_p);
  			NMG_CK_VERTEXUSE(eu->vu_p);
  			NMG_CK_VERTEX(eu->vu_p->v_p);
  			NMG_CK_VERTEX_G(eu->vu_p->v_p->vg_p);
  			edge_count++;
  		    }
  		} else if (BU_LIST_FIRST_MAGIC(&lu->down_hd) == NMG_VERTEXUSE_MAGIC) {
  		    v = BU_LIST_PNEXT(vertexuse, &lu->down_hd)->v_p;
  		    NMG_CK_VERTEX(v);
  		    NMG_CK_VERTEX_G(v->vg_p);
  		    edge_count++;
  		} else
  		    bu_log("write_shell_face_loop: loopuse mess up! (1)\n");
  
  		bu_vls_printf(&str, "508,%d", edge_count);
  
  		if (BU_LIST_FIRST_MAGIC(&lu->down_hd) == NMG_EDGEUSE_MAGIC) {
  		    for (BU_LIST_FOR(eu, edgeuse, &lu->down_hd)) {
  			int orientation;
  			struct edge *e;
  			struct edgeuse *eu_tab;
  
  			e = eu->e_p;
  			eu_tab = e->eu_p;
  			if (eu_tab->vu_p->v_p == eu->vu_p->v_p)
  			    orientation = 1;
  			else
  			    orientation = 0;
  
  			bu_vls_printf(&str, ",0,%d,%jd,%d,0",
  				      edge_de ,
  				      bu_ptbl_locate(etab, (long *)(e)) + 1,
  				      orientation);
  
  			edge_count++;
  		    }
  		} else if (BU_LIST_FIRST_MAGIC(&lu->down_hd) == NMG_VERTEXUSE_MAGIC) {
  		    v = BU_LIST_PNEXT(vertexuse, &lu->down_hd)->v_p;
  		    bu_vls_printf(&str, ",1,%d,%jd,1,0",
  				  vert_de,
  				  bu_ptbl_locate(vtab, (long *)v)+1);
  		} else
  		    bu_log("write_shell_face_loop: loopuse mess up! (2)\n");
  
  		bu_vls_strcat(&str, ";");
  
  		/* write loop entry */
  		/* initialize directory entry */
  		for (i = 0; i < 21; i++)
  		    dir_entry[i] = DEFAULT;
  
  		/* remember where parameter data is going */
  		dir_entry[2] = param_seq + 1;
  
  		/* get parameter line count */
  		dir_entry[14] = write_freeform(fp_param, bu_vls_addr(&str), dir_seq+1, 'P');
  
  		/* write directory entry for loop entity */
  		dir_entry[1] = 508;
  		dir_entry[8] = 0;
  		dir_entry[9] = 10001;
  		dir_entry[11] = 508;
  		dir_entry[15] = 1;
  		loop_list[loop_count++] = write_dir_entry(fp_dir, dir_entry);
  
  		bu_vls_free(&str);
  
  	    }
  
  	    if (exterior_loop < 0) {
  		bu_log("No outside loop found for face\n");
  		outer_loop_flag = 0;
  	    } else if (exterior_loop != 0) {
  		/* move outside loop to start of list */
  		int tmp;
  
  		tmp = loop_list[0];
  		loop_list[0] = loop_list[exterior_loop];
  		loop_list[exterior_loop] = tmp;
  	    }
  
  	    if (do_nurbs) {
  		vect_t u_dir, v_dir;
  		point_t base_pt;
  		fastf_t u_max, v_max;
  
  		bu_vls_printf(&str, "510,%d,%zu,%d" ,
  			      write_planar_nurb(fu, u_dir, v_dir, &u_max, &v_max, base_pt, fp_dir, fp_param),
  			      loop_count,
  			      outer_loop_flag);
  	    } else
  		bu_vls_printf(&str, "510,%d,%zu,%d" ,
  			      write_plane_entity(fu->f_p->g.plane_p->N, fp_dir, fp_param),
  			      loop_count,
  			      outer_loop_flag);
  
  	    for (i = 0; i < loop_count; i++)
  		bu_vls_printf(&str, ",%d", loop_list[i]);
  
  	    bu_vls_strcat(&str, ";");
  
  	    for (i = 0; i < 21; i++)
  		dir_entry[i] = DEFAULT;
  
  	    dir_entry[1] = 510;
  	    dir_entry[2] = param_seq + 1;
  	    dir_entry[8] = 0;
  	    dir_entry[9] = 10001;
  	    dir_entry[11] = 510;
  	    dir_entry[15] = 1;
  	    dir_entry[14] = write_freeform(fp_param, bu_vls_addr(&str), dir_seq+1, 'P');
  
  	    face_list[face_count++] = write_dir_entry(fp_dir, dir_entry);
  
  	    bu_free((char *)loop_list, "loop list");
  	    bu_vls_free(&str);
  	}
  
  	/* write shell entity */
  	bu_vls_printf(&str, "514,%zu", face_count);
  	for (i = 0; i < face_count; i++)
  	    bu_vls_printf(&str, ",%d,1", face_list[i]);
  	bu_vls_strcat(&str, ";");
  
  	/* initialize directory entry */
  	for (i = 0; i < 21; i++)
  	    dir_entry[i] = DEFAULT;
  
  	dir_entry[1] = 514;
  	dir_entry[2] = param_seq + 1;
  	dir_entry[8] = 0;
  	dir_entry[9] = 10001;
  	dir_entry[11] = 514;
  	dir_entry[15] = 1;
  	dir_entry[14] = write_freeform(fp_param, bu_vls_addr(&str), dir_seq+1, 'P');
  
  	shell_list[shell_count++] = write_dir_entry(fp_dir, dir_entry);
  
  	bu_free((char *)face_list, "face list");
  	bu_vls_free(&str);
      }
  
      /* write BREP object entity */
  
      /* write name entity */
      if (name != NULL)
  	name_de = write_name_entity(name, fp_dir, fp_param);
      else
  	name_de = 0;
  
      /* write color and attributes entities, if appropriate */
      if (!name || lookup_props(&props, name)) {
  	prop_de = 0;
  	color_de = 0;
      } else {
  	prop_de = write_att_entity(&props, fp_dir, fp_param);
  	if (props.color_defined)
  	    color_de = get_color(props.color, fp_dir, fp_param);
      }
  
      /* Put outer shell in BREP object first */
      bu_vls_printf(&str, "186,%d,1,%zu", shell_list[0], shell_count-1);
  
      /* Add all other shells */
      for (i = 1; i < shell_count; i++) {
  	bu_vls_printf(&str, ",%d,1", shell_list[i]);
      }
  
      if (prop_de || name_de) {
  	if (prop_de && name_de)
  	    bu_vls_strcat(&str, ",0,2");
  	else
  	    bu_vls_printf(&str, ",0,1");
  	if (prop_de)
  	    bu_vls_printf(&str, ",%d", prop_de);
  	if (name_de)
  	    bu_vls_printf(&str, ",%d", name_de);
  
      }
  
      bu_vls_strcat(&str, ";");
  
      /* initialize directory entry */
      for (i = 0; i < 21; i++)
  	dir_entry[i] = DEFAULT;
  
      dir_entry[1] = 186;
      dir_entry[2] = param_seq + 1;
      dir_entry[8] = 0;
      if (dependent)
  	dir_entry[9] = 10001;
      else
  	dir_entry[9] = 1;
      dir_entry[11] = 186;
      dir_entry[13] = color_de;
      dir_entry[15] = 0;
      dir_entry[14] = write_freeform(fp_param, bu_vls_addr(&str), dir_seq+1, 'P');
  
      bu_free((char *)shell_list, "shell list");
      bu_vls_free(&str);
  
      return write_dir_entry(fp_dir, dir_entry);
  }
  
  
  void
  w_terminate(FILE *fp)
  {
      fprintf(fp, "S%07dG%07dD%07dP%07d%40.40sT0000001\n", start_len, global_len, dir_seq, param_seq, " ");
  }
  
  
  int
  arb_is_rpp(struct rt_arb_internal *arb)
  {
      vect_t v0, v1, v2;
      size_t i;
  
      RT_ARB_CK_MAGIC(arb);
  
      /* for an rpp, all the height edge vectors must be equal,
         all the width edge vectors must be equal, all the
         depth edge vectors must be equal, and at least one
         vertex must have three right angles */
  
      /* check the height vectors */
      VSUB2(v0, arb->pt[4], arb->pt[0]);
      for (i = 5; i < 8; i++) {
  	VSUB2(v1, arb->pt[i], arb->pt[i-4]);
  	if (!VNEAR_EQUAL(v0, v1, tol.dist))
  	    return 0;
      }
  
      /* check the width vectors */
      VSUB2(v0, arb->pt[1], arb->pt[0]);
      VSUB2(v1, arb->pt[2], arb->pt[3]);
      if (!VNEAR_EQUAL(v0, v1, tol.dist))
  	return 0;
      VSUB2(v1, arb->pt[6], arb->pt[7]);
      if (!VNEAR_EQUAL(v0, v1, tol.dist))
  	return 0;
      VSUB2(v1, arb->pt[5], arb->pt[4]);
      if (!VNEAR_EQUAL(v0, v1, tol.dist))
  	return 0;
  
      /* check the depth vectors */
      VSUB2(v0, arb->pt[3], arb->pt[0]);
      VSUB2(v1, arb->pt[2], arb->pt[1]);
      if (!VNEAR_EQUAL(v0, v1, tol.dist))
  	return 0;
      VSUB2(v1, arb->pt[6], arb->pt[5]);
      if (!VNEAR_EQUAL(v0, v1, tol.dist))
  	return 0;
      VSUB2(v1, arb->pt[7], arb->pt[4]);
      if (!VNEAR_EQUAL(v0, v1, tol.dist))
  	return 0;
  
      /* check for a right angle corner */
      VSUB2(v0, arb->pt[3], arb->pt[0]);
      VSUB2(v1, arb->pt[1], arb->pt[0]);
      VSUB2(v2, arb->pt[4], arb->pt[0]);
      VUNITIZE(v0);
      VUNITIZE(v1);
      VUNITIZE(v2);
      if (!BN_VECT_ARE_PERP(VDOT(v0, v1), &tol))
  	return 0;
      if (!BN_VECT_ARE_PERP(VDOT(v0, v2), &tol))
  	return 0;
      if (!BN_VECT_ARE_PERP(VDOT(v1, v2), &tol))
  	return 0;
  
      return 1;
  }
  
  
  int
  write_name_entity(char *name,
  		  FILE *fp_dir, FILE *fp_param)
  {
      struct bu_vls str = BU_VLS_INIT_ZERO;
      int dir_entry[21];
      size_t i;
      size_t name_len;
  
      name_len = strlen(name);
      if (!name_len)
  	return 0;
  
      /* initialize directory entry */
      for (i = 0; i < 21; i++)
  	dir_entry[i] = DEFAULT;
  
      /* write parameter data into a string */
      if (name_len >= NAMESIZE)
  	bu_vls_printf(&str, "406,1,16H%16.16s;", name);
      else
  	bu_vls_printf(&str, "406,1,%zdH%s;", strlen(name), name);
  
      /* remember where parameter data is going */
      dir_entry[2] = param_seq + 1;
  
      /* get parameter line count */
      dir_entry[14] = write_freeform(fp_param, bu_vls_addr(&str), dir_seq+1, 'P');
      bu_vls_free(&str);
  
      /* write directory entry for name property entity */
      dir_entry[1] = 406;
      dir_entry[8] = 0;
      dir_entry[9] = 1010301;
      dir_entry[11] = 406;
      dir_entry[15] = 15;
      return write_dir_entry(fp_dir, dir_entry);
  }
  
  
  int
  tor_to_iges(struct rt_db_internal *ip,
  	    char *name,
  	    FILE *fp_dir, FILE *fp_param)
  {
      struct rt_tor_internal *tor;
      struct bu_vls str = BU_VLS_INIT_ZERO;
      int dir_entry[21];
      int name_de;
      size_t i;
  
      if (ip->idb_type != ID_TOR)
  	bu_log("tor_to_iges called for non-torus (type=%d)\n", ip->idb_type);
  
      tor = (struct rt_tor_internal *)ip->idb_ptr;
  
      RT_TOR_CK_MAGIC(tor);
  
      /* initialize directory entry */
      for (i = 0; i < 21; i++)
  	dir_entry[i] = DEFAULT;
  
      /* write a name entity for this solid */
      name_de = write_name_entity(name, fp_dir, fp_param);
  
      /* write parameter data into a string */
      bu_vls_printf(&str, "160,%g,%g,%g,%g,%g,%g,%g,%g,0,1,%d;",
  		  tor->r_a,
  		  tor->r_h,
  		  tor->v[X], tor->v[Y], tor->v[Z],
  		  tor->h[X], tor->h[Y], tor->h[Z],
  		  name_de);
  
      /* remember where parameter data is going */
      dir_entry[2] = param_seq + 1;
  
      /* get parameter line count */
      dir_entry[14] = write_freeform(fp_param, bu_vls_addr(&str), dir_seq+1, 'P');
      bu_vls_free(&str);
  
      /* write directory entry for torus entity */
      dir_entry[1] = 160;
      dir_entry[8] = 0;
      dir_entry[9] = 10001;
      dir_entry[11] = 160;
      dir_entry[15] = 0;
      return write_dir_entry(fp_dir, dir_entry);
  
  }
  
  
  int
  sph_to_iges(struct rt_db_internal *ip,
  	    char *name,
  	    FILE *fp_dir, FILE *fp_param)
  {
      struct rt_ell_internal *sph;
      struct bu_vls str = BU_VLS_INIT_ZERO;
      double radius;
      int dir_entry[21];
      int name_de;
      size_t i;
  
      if (ip->idb_type != ID_SPH)
  	bu_log("sph_to_iges called for non-sph (type=%d)\n", ip->idb_type);
  
      sph = (struct rt_ell_internal *)ip->idb_ptr;
  
      RT_ELL_CK_MAGIC(sph);
  
      /* write name entity */
      name_de = write_name_entity(name, fp_dir, fp_param);
  
      /* initialize directory entry */
      for (i = 0; i < 21; i++)
  	dir_entry[i] = DEFAULT;
  
      radius = MAGNITUDE(sph->a);
  
      /* write parameter data into a string */
      bu_vls_printf(&str, "158,%g,%g,%g,%g,0,1,%d;",
  		  radius,
  		  sph->v[X], sph->v[Y], sph->v[Z] ,
  		  name_de);
  
      /* remember where parameter data is going */
      dir_entry[2] = param_seq + 1;
  
      /* get parameter line count */
      dir_entry[14] = write_freeform(fp_param, bu_vls_addr(&str), dir_seq+1, 'P');
      bu_vls_free(&str);
  
      /* write directory entry for sphere entity */
      dir_entry[1] = 158;
      dir_entry[8] = 0;
      dir_entry[9] = 10001;
      dir_entry[11] = 158;
      dir_entry[15] = 0;
      return write_dir_entry(fp_dir, dir_entry);
  
  }
  
  
  int
  ell_to_iges(struct rt_db_internal *ip,
  	    char *name,
  	    FILE *fp_dir, FILE *fp_param)
  {
      struct rt_ell_internal *ell;
      struct bu_vls str = BU_VLS_INIT_ZERO;
      double radius_a;
      double radius_b;
      double radius_c;
      vect_t a_dir;
      vect_t b_dir;
      vect_t c_dir;
      int dir_entry[21];
      int name_de;
      size_t i;
  
      if (ip->idb_type != ID_ELL)
  	bu_log("ell_to_iges called for non-ell (type=%d)\n", ip->idb_type);
  
      ell = (struct rt_ell_internal *)ip->idb_ptr;
  
      RT_ELL_CK_MAGIC(ell);
  
      /* write name entity */
      name_de = write_name_entity(name, fp_dir, fp_param);
  
      /* initialize directory entry */
      for (i = 0; i < 21; i++)
  	dir_entry[i] = DEFAULT;
  
      radius_a = MAGNITUDE(ell->a);
      radius_b = MAGNITUDE(ell->b);
      radius_c = MAGNITUDE(ell->c);
  
      VMOVE(a_dir, ell->a);
      VMOVE(b_dir, ell->b);
      VMOVE(c_dir, ell->c);
  
      VUNITIZE(a_dir);
      VUNITIZE(b_dir);
      VUNITIZE(c_dir);
  
      /* write parameter data into a string */
      bu_vls_printf(&str, "168,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,0,1,%d;",
  		  radius_a, radius_b, radius_c ,
  		  ell->v[X], ell->v[Y], ell->v[Z] ,
  		  a_dir[X], a_dir[Y], a_dir[Z] ,
  		  c_dir[X], c_dir[Y], c_dir[Z] ,
  		  name_de);
  
      /* remember where parameter data is going */
      dir_entry[2] = param_seq + 1;
  
      /* get parameter line count */
      dir_entry[14] = write_freeform(fp_param, bu_vls_addr(&str), dir_seq+1, 'P');
      bu_vls_free(&str);
  
      /* write directory entry for ellipsoid entity */
      dir_entry[1] = 168;
      dir_entry[8] = 0;
      dir_entry[9] = 10001;
      dir_entry[11] = 168;
      dir_entry[15] = 0;
      return write_dir_entry(fp_dir, dir_entry);
  
  }
  
  
  int
  rpp_to_iges(struct rt_db_internal *ip,
  	    char *name,
  	    FILE *fp_dir, FILE *fp_param)
  {
      struct rt_arb_internal *arb;
      struct bu_vls str = BU_VLS_INIT_ZERO;
      double length_a;
      double length_b;
      double length_c;
      vect_t a_dir;
      vect_t b_dir;
      vect_t c_dir;
      vect_t tmp_dir;
      int dir_entry[21];
      int name_de;
      size_t i;
  
      if (ip->idb_type != ID_ARB8)
  	bu_log("rpp_to_iges called for non-arb (type=%d)\n", ip->idb_type);
  
      arb = (struct rt_arb_internal *)ip->idb_ptr;
  
      RT_ARB_CK_MAGIC(arb);
  
      /* write name entity */
      name_de = write_name_entity(name, fp_dir, fp_param);
  
      /* initialize directory entry */
      for (i = 0; i < 21; i++)
  	dir_entry[i] = DEFAULT;
  
      VSUB2(a_dir, arb->pt[1], arb->pt[0]);
      VSUB2(b_dir, arb->pt[3], arb->pt[0]);
      VSUB2(c_dir, arb->pt[4], arb->pt[0]);
  
      length_a = MAGNITUDE(a_dir);
      length_b = MAGNITUDE(b_dir);
      length_c = MAGNITUDE(c_dir);
  
      VUNITIZE(a_dir);
      VUNITIZE(b_dir);
      VUNITIZE(c_dir);
  
      /* c_dir cross a_dir must give b_dir for IGES */
      VCROSS(tmp_dir, c_dir, a_dir);
      if (VDOT(b_dir, tmp_dir) < 0.0) {
  	/* not a right-handed system, so exchange a_dir with c_dir */
  	double tmp_length;
  
  	VMOVE(tmp_dir, a_dir);
  	VMOVE(a_dir, c_dir);
  	VMOVE(c_dir, tmp_dir);
  	tmp_length = length_a;
  	length_a = length_c;
  	length_c = tmp_length;
      }
  
      /* write parameter data into a string */
      bu_vls_printf(&str, "150,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,0,1,%d;",
  		  length_a, length_b, length_c ,
  		  arb->pt[0][X], arb->pt[0][Y], arb->pt[0][Z] ,
  		  a_dir[X], a_dir[Y], a_dir[Z] ,
  		  c_dir[X], c_dir[Y], c_dir[Z] ,
  		  name_de);
  
      /* remember where parameter data is going */
      dir_entry[2] = param_seq + 1;
  
      /* get parameter line count */
      dir_entry[14] = write_freeform(fp_param, bu_vls_addr(&str), dir_seq+1, 'P');
      bu_vls_free(&str);
  
      /* write directory entry for block entity */
      dir_entry[1] = 150;
      dir_entry[8] = 0;
      dir_entry[9] = 10001;
      dir_entry[11] = 150;
      dir_entry[15] = 0;
      return write_dir_entry(fp_dir, dir_entry);
  }
  
  
  int
  arb_to_iges(struct rt_db_internal *ip,
  	    char *name,
  	    FILE *fp_dir, FILE *fp_param)
  {
      struct rt_arb_internal *arb;
  
      if (ip->idb_type != ID_ARB8) {
  	bu_log("arb_to_iges called for non-arb (type=%d)\n", ip->idb_type);
  	return 0;
      }
  
      arb = (struct rt_arb_internal *)ip->idb_ptr;
  
      RT_ARB_CK_MAGIC(arb);
  
      if (arb_is_rpp(arb))
  	return rpp_to_iges(ip, name, fp_dir, fp_param);
      else
  	return nmg_to_iges(ip, name, fp_dir, fp_param);
  }
  
  
  int
  tgc_to_iges(struct rt_db_internal *ip,
  	    char *name,
  	    FILE *fp_dir, FILE *fp_param)
  {
      struct rt_tgc_internal *tgc;
      fastf_t h_len, a_len, b_len, c_len, d_len;
      vect_t h_dir, a_dir, b_dir;
      int iges_type;
      struct bu_vls str = BU_VLS_INIT_ZERO;
      int dir_entry[21];
      int name_de;
      size_t i;
  
      if (ip->idb_type != ID_TGC) {
  	bu_log("tgc_to_iges called for non-tgc (type=%d)\n", ip->idb_type);
  	return 0;
      }
  
      tgc = (struct rt_tgc_internal *)ip->idb_ptr;
  
      RT_TGC_CK_MAGIC(tgc);
  
      h_len = MAGNITUDE(tgc->h);
      a_len = MAGNITUDE(tgc->a);
      b_len = MAGNITUDE(tgc->b);
      c_len = MAGNITUDE(tgc->c);
      d_len = MAGNITUDE(tgc->d);
  
      /* Use VSCALE rather than VUNITIZE, since we have
         already done the sqrt */
  
      VMOVE(h_dir, tgc->h);
      VSCALE(h_dir, h_dir, 1.0/h_len);
  
      VMOVE(a_dir, tgc->a);
      VSCALE(a_dir, a_dir, 1.0/a_len);
  
      VMOVE(b_dir, tgc->b);
      VSCALE(b_dir, b_dir, 1.0/b_len);
  
      if (!BN_VECT_ARE_PERP(VDOT(h_dir, a_dir), &tol) ||
  	!BN_VECT_ARE_PERP(VDOT(h_dir, b_dir), &tol)) {
  	/* this is not an rcc or a trc */
  	return nmg_to_iges(ip, name, fp_dir, fp_param);
      }
  
      if (NEAR_EQUAL(a_len, b_len, tol.dist) &&
  	NEAR_EQUAL(c_len, d_len, tol.dist)) {
  	/* this tgc is either an rcc or a trc */
  
  	/* write name entity */
  	name_de = write_name_entity(name, fp_dir, fp_param);
  
  	/* initialize directory entry */
  	for (i = 0; i < 21; i++)
  	    dir_entry[i] = DEFAULT;
  
  	if (NEAR_EQUAL(a_len, c_len, tol.dist)) {
  	    /* it's an rcc */
  	    iges_type = 154;
  	    bu_vls_printf(&str, "154,%g,%g,%g,%g,%g,%g,%g,%g" ,
  			  h_len, a_len ,
  			  tgc->v[X], tgc->v[Y], tgc->v[Z] ,
  			  h_dir[X], h_dir[Y], h_dir[Z]);
  	} else {
  	    /* it's a trc */
  
  	    fastf_t bigger_r, smaller_r;
  	    vect_t base;
  
  	    iges_type = 156;
  	    if (a_len > c_len) {
  		bigger_r = a_len;
  		smaller_r = c_len;
  		VMOVE(base, tgc->v);
  	    } else {
  		bigger_r = c_len;
  		smaller_r = a_len;
  		VADD2(base, tgc->v, tgc->h);
  		VREVERSE(h_dir, h_dir);
  	    }
  	    bu_vls_printf(&str, "156,%g,%g,%g,%g,%g,%g,%g,%g,%g" ,
  			  h_len, bigger_r, smaller_r ,
  			  base[X], base[Y], base[Z] ,
  			  h_dir[X], h_dir[Y], h_dir[Z]);
  	}
  
  	bu_vls_printf(&str, ",0,1,%d;", name_de);
  
  	/* remember where parameter data is going */
  	dir_entry[2] = param_seq + 1;
  
  	/* get parameter line count */
  	dir_entry[14] = write_freeform(fp_param, bu_vls_addr(&str), dir_seq+1, 'P');
  	bu_vls_free(&str);
  
  	/* fill in directory entry */
  	dir_entry[1] = iges_type;
  	dir_entry[8] = 0;
  	dir_entry[9] = 10001;
  	dir_entry[11] = iges_type;
  	dir_entry[15] = 0;
  
  	return write_dir_entry(fp_dir, dir_entry);
      } else
  	return nmg_to_iges(ip, name, fp_dir, fp_param);
  }
  
  
  int
  write_tree_of_unions(char *name,
  		     int de_list[],
  		     size_t length,
  		     int dependent,
  		     FILE *fp_dir,
  		     FILE *fp_param)
  {
      struct bu_vls str = BU_VLS_INIT_ZERO;
      struct iges_properties props;
      int dir_entry[21];
      int name_de;
      int prop_de;
      int color_de = DEFAULT;
      size_t i;
  
      /* write name entity */
      if (name != NULL)
  	name_de = write_name_entity(name, fp_dir, fp_param);
      else
  	name_de = 0;
  
      /* write color and attributes entities, if appropriate */
      if (lookup_props(&props, name)) {
  	prop_de = 0;
  	color_de = 0;
      } else {
  	prop_de = write_att_entity(&props, fp_dir, fp_param);
  	if (props.color_defined)
  	    color_de = get_color(props.color, fp_dir, fp_param);
      }
  
      /* write parameter data into a string */
      bu_vls_printf(&str, "180,%zu,%d", 2*length-1, -de_list[0]);
      for (i = 1; i < length; i++)
  	bu_vls_printf(&str, ",%d,1", -de_list[i]);
  
      if (prop_de || name_de) {
  	if (prop_de && name_de)
  	    bu_vls_strcat(&str, ",0,2");
  	else
  	    bu_vls_printf(&str, ",0,1");
  	if (prop_de)
  	    bu_vls_printf(&str, ",%d", prop_de);
  	if (name_de)
  	    bu_vls_printf(&str, ",%d", name_de);
  
      }
  
      bu_vls_strcat(&str, ";");
  
      /* initialize directory entry */
      for (i = 0; i < 21; i++)
  	dir_entry[i] = DEFAULT;
  
      dir_entry[1] = 180;
      dir_entry[2] = param_seq + 1;
      dir_entry[8] = 0;
      if (dependent)
  	dir_entry[9] = 10001;
      else
  	dir_entry[9] = 1;
      dir_entry[11] = 180;
      dir_entry[13] = color_de;
      dir_entry[15] = 0;
      dir_entry[14] = write_freeform(fp_param, bu_vls_addr(&str), dir_seq+1, 'P');
  
      bu_vls_free(&str);
  
      return write_dir_entry(fp_dir, dir_entry);
  }
  
  
  int
  write_solid_assembly(char *name,
  		     int de_list[],
  		     size_t length,
  		     int dependent,
  		     FILE *fp_dir,
  		     FILE *fp_param)
  {
      struct bu_vls str = BU_VLS_INIT_ZERO;
      struct iges_properties props;
      int dir_entry[21];
      int name_de;
      int prop_de;
      int color_de = DEFAULT;
      size_t i;
  
      /* write name entity */
      if (name != NULL)
  	name_de = write_name_entity(name, fp_dir, fp_param);
      else
  	name_de = 0;
  
      /* write color and attributes entities, if appropriate */
      if (lookup_props(&props, name)) {
  	prop_de = 0;
  	color_de = 0;
      } else {
  	prop_de = write_att_entity(&props, fp_dir, fp_param);
  	if (props.color_defined)
  	    color_de = get_color(props.color, fp_dir, fp_param);
      }
  
      /* write parameter data into a string */
      bu_vls_printf(&str, "184,%zu", length);
      for (i = 0; i < length; i++)
  	bu_vls_printf(&str, ",%d", de_list[i]);
      for (i = 0; i < length; i++)
  	bu_vls_printf(&str, ",0");
  
      if (prop_de || name_de) {
  	if (prop_de && name_de)
  	    bu_vls_strcat(&str, ",0,2");
  	else
  	    bu_vls_printf(&str, ",0,1");
  	if (prop_de)
  	    bu_vls_printf(&str, ",%d", prop_de);
  	if (name_de)
  	    bu_vls_printf(&str, ",%d", name_de);
  
      }
  
      bu_vls_strcat(&str, ";");
  
      /* initialize directory entry */
      for (i = 0; i < 21; i++)
  	dir_entry[i] = DEFAULT;
  
      dir_entry[1] = 184;
      dir_entry[2] = param_seq + 1;
      dir_entry[8] = 0;
      if (dependent)
  	dir_entry[9] = 10001;
      else
  	dir_entry[9] = 1;
      dir_entry[11] = 184;
      dir_entry[13] = color_de;
      dir_entry[15] = 0;
      dir_entry[14] = write_freeform(fp_param, bu_vls_addr(&str), dir_seq+1, 'P');
  
      bu_vls_free(&str);
  
      return write_dir_entry(fp_dir, dir_entry);
  }
  
  
  int
  nmg_to_iges(struct rt_db_internal *ip,
  	    char *name,
  	    FILE *fp_dir, FILE *fp_param)
  {
      struct model *model;
      struct nmgregion *r;
      int *region_de;
      int brep_de;
      int dependent;
      size_t region_count;
      size_t i;
  
      RT_CK_DB_INTERNAL(ip);
  
      dependent = 1;
      for (i = 0; i < no_of_indeps; i++) {
  	if (!bu_strncmp(name, independent[i], NAMESIZE+1)) {
  	    dependent = 0;
  	    break;
  	}
      }
  
      solid_is_brep = 1;
      comb_form = 1;
      if (ip->idb_type == ID_NMG) {
  	model = (struct model *)ip->idb_ptr;
  	NMG_CK_MODEL(model);
  
  	/* count the number of nmgregions */
  	region_count = 0;
  	for (BU_LIST_FOR(r, nmgregion, &model->r_hd)) {
  	    NMG_CK_REGION(r);
  	    region_count++;
  	}
  
  	if (region_count == 0)
  	    return 0;
  	else if (region_count == 1)
  	    return (nmgregion_to_iges(name, BU_LIST_FIRST(nmgregion, &model->r_hd) ,
  				     dependent, fp_dir, fp_param));
  	else {
  	    /* make a boolean tree unioning all the regions */
  
  	    /* space to save the iges location of each nmgregion */
  	    region_de = (int *)bu_calloc(region_count, sizeof(int), "nmg_to_iges");
  
  	    /* loop through all nmgregions in the model */
  	    region_count = 0;
  	    for (BU_LIST_FOR(r, nmgregion, &model->r_hd))
  		region_de[region_count++] = nmgregion_to_iges((char *)NULL, r, 1 ,
  							      fp_dir, fp_param);
  
  	    /* now make the boolean tree */
  	    brep_de = write_tree_of_unions(name, region_de, region_count, dependent, fp_dir, fp_param);
  
  	    bu_free((char *)region_de, "nmg_to_iges");
  	    return brep_de;
  	}
      } else {
  	if (ip->idb_type == ID_BOT) {
  	    struct rt_bot_internal *bot = (struct rt_bot_internal *)ip->idb_ptr;
  	    if (bot->mode != RT_BOT_SOLID) {
  		bu_log("%s is a plate mode primitive, and cannot be converted to IGES format!\n", name);
  		return 0;
  	    }
  	}
  	model = nmg_mm();
  	if (OBJ[ip->idb_type].ft_tessellate(&r, model, ip, &ttol, &tol)) {
  	    nmg_km(model);
  	    return 0;
  	} else {
  	    solids_to_nmg++;
  	    brep_de =  nmgregion_to_iges(name, r, dependent, fp_dir, fp_param);
  	    nmg_km(model);
  	    return brep_de;
  	}
      }
  }
  
  
  int
  sketch_to_iges(struct rt_db_internal *ip, char *name, FILE *fp_dir, FILE *fp_param)
  {
      struct rt_sketch_internal *sketch;
  
      if (ip->idb_type != ID_SKETCH)
  	bu_log("sketch_to_iges called for non-sketch (type=%d)\n", ip->idb_type);
  
      sketch = (struct rt_sketch_internal *)ip->idb_ptr;
  
      RT_SKETCH_CK_MAGIC(sketch);
  
      /* write name entity */
      (void)write_name_entity(name, fp_dir, fp_param);
  
      bu_log("The 'sketch' primitive is not yet supported for IGES export.\n");
      bu_log("Unable to export %s\n", name);
  
      return 0;
  }
  
  
  int
  null_to_iges(struct rt_db_internal *UNUSED(ip), char *UNUSED(name), FILE *UNUSED(fp_dir), FILE *UNUSED(fp_param))
  {
      return 0;
  }
  
  
  int
  write_xform_entity(mat_t mat,
  		   FILE *fp_dir, FILE *fp_param)
  {
      struct bu_vls str = BU_VLS_INIT_ZERO;
      int dir_entry[21];
      size_t i;
  
      /* initialize directory entry */
      for (i = 0; i < 21; i++)
  	dir_entry[i] = DEFAULT;
  
      /* write parameter data into a string */
      bu_vls_strcpy(&str, "124");
      for (i = 0; i < 12; i++) {
  	bu_vls_printf(&str, ",%g", mat[i]);
      }
      bu_vls_strcat(&str, ";");
  
      dir_entry[1] = 124;
      dir_entry[2] = param_seq + 1;
      dir_entry[8] = 0;
      dir_entry[9] = 10001;
      dir_entry[11] = 124;
      dir_entry[15] = 0;
      dir_entry[14] = write_freeform(fp_param, bu_vls_addr(&str), dir_seq+1, 'P');
  
      bu_vls_free(&str);
  
      return write_dir_entry(fp_dir, dir_entry);
  }
  
  
  int
  write_solid_instance(int orig_de,
  		     mat_t mat,
  		     FILE *fp_dir, FILE *fp_param)
  {
      struct bu_vls str = BU_VLS_INIT_ZERO;
      int dir_entry[21];
      size_t i;
  
      /* initialize directory entry */
      for (i = 0; i < 21; i++)
  	dir_entry[i] = DEFAULT;
  
      /* write the transformation matrix and make the link */
      dir_entry[7] = write_xform_entity(mat, fp_dir, fp_param);
  
      /* write parameter data into a string */
      bu_vls_printf(&str, "430,%d;", orig_de);
  
      dir_entry[1] = 430;
      dir_entry[2] = param_seq + 1;
      dir_entry[8] = 0;
      dir_entry[9] = 10001;
      dir_entry[11] = 430;
      dir_entry[15] = solid_is_brep ? 1 : 0;
      dir_entry[14] = write_freeform(fp_param, bu_vls_addr(&str), dir_seq+1, 'P');
  
      bu_vls_free(&str);
  
      return write_dir_entry(fp_dir, dir_entry);
  }
  
  
  int
  write_att_entity(struct iges_properties *props,
  		 FILE *fp_dir, FILE *fp_param)
  {
      struct bu_vls str = BU_VLS_INIT_ZERO;
      int dir_entry[21];
      size_t str_len;
      size_t i;
  
      /* initialize directory entry */
      for (i = 0; i < 21; i++)
  	dir_entry[i] = DEFAULT;
  
      /* write parameter data into a string */
      bu_vls_strcpy(&str, "422");
  
      /* material name */
      str_len = strlen(props->material_name);
      if (str_len)
  	bu_vls_printf(&str, ",%zuH%s", str_len, props->material_name);
      else
  	bu_vls_strcat(&str, ",");
  
      /* material parameters */
      str_len = strlen(props->material_params);
      if (str_len)
  	bu_vls_printf(&str, ",%zuH%s", str_len, props->material_params);
      else
  	bu_vls_strcat(&str, ",");
  
      /* region flag */
      if (props->region_flag == 'R')
  	bu_vls_strcat(&str, ",1");
      else
  	bu_vls_strcat(&str, ",0");
  
      /* ident number, air code, material code, los density */
      bu_vls_printf(&str, ",%d,%d,%d,%d,%d,%d;" ,
  		  props->ident ,
  		  props->air_code ,
  		  props->material_code ,
  		  props->los_density,
  		  props->inherit,
  		  props->color_defined);
  
      dir_entry[1] = 422;
      dir_entry[2] = param_seq + 1;
      dir_entry[3] = (-attribute_de);
      dir_entry[8] = 0;
      dir_entry[9] = 10301;
      dir_entry[11] = 422;
      dir_entry[15] = 0;
      dir_entry[14] = write_freeform(fp_param, bu_vls_addr(&str), dir_seq+1, 'P');
  
      bu_vls_free(&str);
  
      return write_dir_entry(fp_dir, dir_entry);
  }
  
  
  /*
   * Write a region or group of only one member as a solid instance
   *
   */
  int
  short_comb_to_iges(struct iges_properties *props,
  		   int dependent,
  		   int de_pointers[],
  		   FILE *fp_dir, FILE *fp_param)
  {
      struct bu_vls str = BU_VLS_INIT_ZERO;
      int dir_entry[21];
      int name_de;
      int props_de;
      int color_de = DEFAULT;
      int status = 1;
      size_t i;
  
      /* if member has not been converted, don't try to write the tree */
      if (de_pointers[0] == 0)
  	return 0;
  
      if (dependent)
  	status = 10001;
  
      /* initialize directory entry */
      for (i = 0; i < 21; i++)
  	dir_entry[i] = DEFAULT;
  
      /* write name entity */
      name_de = write_name_entity(props->name, fp_dir, fp_param);
  
      /* write attributes entity */
      props_de = write_att_entity(props, fp_dir, fp_param);
  
      /* get color */
      if (props->color_defined)
  	color_de = get_color(props->color, fp_dir, fp_param);
      else
  	color_de = 0;
  
      /* write parameter data into a string */
      bu_vls_printf(&str, "430,%d", de_pointers[0]);
  
      if (props_de || name_de) {
  	if (props_de && name_de)
  	    bu_vls_strcat(&str, ",0,2");
  	else
  	    bu_vls_printf(&str, ",0,1");
  	if (props_de)
  	    bu_vls_printf(&str, ",%d", props_de);
  	if (name_de)
  	    bu_vls_printf(&str, ",%d", name_de);
  
      }
      bu_vls_strcat(&str, ";");
  
      dir_entry[1] = 430;
      dir_entry[2] = param_seq + 1;
      dir_entry[8] = 0;
      dir_entry[9] = status;
      dir_entry[11] = 430;
      dir_entry[13] = color_de;
      dir_entry[15] = comb_form;
      dir_entry[14] = write_freeform(fp_param, bu_vls_addr(&str), dir_seq+1, 'P');
  
      bu_vls_free(&str);
  
      return write_dir_entry(fp_dir, dir_entry);
  
  }
  
  
  int
  has_non_union_ops(union tree *tp)
  {
      RT_CK_TREE(tp);
  
      if (tp->tr_op == OP_SOLID || tp->tr_op == OP_REGION || tp->tr_op == OP_NOP ||
  	tp->tr_op == OP_NMG_TESS || tp->tr_op == OP_DB_LEAF) {
  	return 0;
      }
  
      if (tp->tr_op != OP_UNION) {
  	return 1;
      }
  
      return has_non_union_ops(tp->tr_b.tb_left) || has_non_union_ops(tp->tr_b.tb_right);
  }
  
  
  void
  igs_tree(struct bu_vls *str,
  	 union tree *tp,
  	 int length,
  	 int *de_pointers)
  {
      RT_CK_TREE(tp);
      BU_CK_VLS(str);
  
      switch (tp->tr_op) {
  	case OP_UNION:
  	    igs_tree(str, tp->tr_b.tb_left, length, de_pointers);
  	    igs_tree(str, tp->tr_b.tb_right, length, de_pointers);
  	    bu_vls_strcat(str, ",1");
  	    break;
  	case OP_INTERSECT:
  	    igs_tree(str, tp->tr_b.tb_left, length, de_pointers);
  	    igs_tree(str, tp->tr_b.tb_right, length, de_pointers);
  	    bu_vls_strcat(str, ",2");
  	    break;
  	case OP_SUBTRACT:
  	    igs_tree(str, tp->tr_b.tb_left, length, de_pointers);
  	    igs_tree(str, tp->tr_b.tb_right, length, de_pointers);
  	    bu_vls_strcat(str, ",3");
  	    break;
  	case OP_DB_LEAF:
  	    bu_vls_printf(str, ",%d", -de_pointers[de_pointer_number]);
  	    de_pointer_number++;
  	    break;
  	default:
  	    bu_log("Unrecognized operation in combination!\n");
  	    break;
      }
  }
  
  
  void
  write_igs_tree(struct bu_vls *str,
  	       struct rt_comb_internal *comb,
  	       int length,
  	       int *de_pointers)
  {
      BU_CK_VLS(str);
      RT_CK_COMB(comb);
  
      bu_vls_printf(str, "180,%d", 2*length-1);
  
      de_pointer_number = 0;
      igs_tree(str, comb->tree, length, de_pointers);
  }
  
  
  int
  tree_to_iges(struct rt_comb_internal *comb,
  	     size_t length, int dependent,
  	     struct iges_properties *props,
  	     int de_pointers[],
  	     FILE *fp_dir, FILE *fp_param)
  {
      struct bu_vls str = BU_VLS_INIT_ZERO;
      int dir_entry[21];
      size_t actual_length = 0;
      int name_de;
      int props_de;
      int color_de = DEFAULT;
      int status = 1;
      int entity_type;
      size_t i;
  
      RT_CK_COMB(comb);
  
      /* if any part of this tree has not been converted, don't try to
         write the tree */
      for (i = 0; i < length; i++) {
  	if (de_pointers[i])
  	    actual_length++;
      }
      if (actual_length != length)
  	return 0;
  
      if (dependent)
  	status = 10001;
  
      /* initialize directory entry */
      for (i = 0; i < 21; i++)
  	dir_entry[i] = DEFAULT;
  
      /* write name entity */
      name_de = write_name_entity(props->name, fp_dir, fp_param);
  
      /* write attributes entity */
      props_de = write_att_entity(props, fp_dir, fp_param);
  
      /* get color */
      if (props->color_defined)
  	color_de = get_color(props->color, fp_dir, fp_param);
      else
  	color_de = 0;
  
      if (mode == CSG_MODE || has_non_union_ops(comb->tree)) {
  	/* write the combination as a Boolean tree */
  	entity_type = 180;
  	write_igs_tree(&str, comb, length, de_pointers);
      } else {
  	/* write the combination as a solid assembly */
  	entity_type = 184;
  
  	bu_vls_printf(&str, "%d,%zu", entity_type, length);
  	for (i = 0; i < length; i++)
  	    bu_vls_printf(&str, ",%d", de_pointers[i]);
  	for (i = 0; i < length; i++)
  	    bu_vls_strcat(&str, ",0");
      }
  
      if (props_de || name_de) {
  	if (props_de && name_de)
  	    bu_vls_strcat(&str, ",0,2");
  	else
  	    bu_vls_printf(&str, ",0,1");
  	if (props_de)
  	    bu_vls_printf(&str, ",%d", props_de);
  	if (name_de)
  	    bu_vls_printf(&str, ",%d", name_de);
  
      }
      bu_vls_strcat(&str, ";");
  
      dir_entry[1] = entity_type;
      dir_entry[2] = param_seq + 1;
      dir_entry[8] = 0;
      dir_entry[9] = status;
      dir_entry[11] = entity_type;
      dir_entry[13] = color_de;
      dir_entry[15] = comb_form;
      dir_entry[14] = write_freeform(fp_param, bu_vls_addr(&str), dir_seq+1, 'P');
  
      bu_vls_free(&str);
  
      return write_dir_entry(fp_dir, dir_entry);
  
  }
  
  
  int
  comb_to_iges(struct rt_comb_internal *comb, size_t length, int dependent, struct iges_properties *props, int de_pointers[], FILE *fp_dir, FILE *fp_param)
  {
      RT_CK_COMB(comb);
  
      if (length == 1)
  	return short_comb_to_iges(props, dependent, de_pointers, fp_dir, fp_param);
      else
  	return tree_to_iges(comb, length, dependent, props, de_pointers, fp_dir, fp_param);
  }
  
  
  /*
   * Local Variables:
   * mode: C
   * tab-width: 8
   * indent-tabs-mode: t
   * c-file-style: "stroustrup"
   * End:
   * ex: shiftwidth=4 tabstop=8
   */
  