/* Convert Triangulated Poly files (TPOLY object format) 
 * to indexed polygons in the 3-D Object File Format (OFF)
 *
 * POLY2OFF by Jason Mathews
 *   NASA/Goddard Space Flight Center
 *   Greenbelt, MD 20771
 *   e-mail: mathews@nssdca.gsfc.nasa.gov (Internet)
 *
 * usage: poly2off [max_number_polygons] < tpoly_infile > off_outfile
 *
 * Modification history:
 *   31-Mar-94 Original version (for unix platforms).
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <search.h>

#define DEF_POLYS 10000  /* default # of polygons */
#define MAXPTS     9000  /* maximal allowable data points */
#define MAXEDGES  10000  /* maximal number of edges */

/* types and structures */

typedef unsigned int uint;

typedef struct {
  float x, y, z;
} Point;

typedef struct {
  uint v1, v2;
} Edge;

int ptcmp( void* arg1, void* arg2 )
{
  return memcmp(arg1, arg2, sizeof(Point));
}

int edgecmp( void* arg1, void* arg2 )
{
  return memcmp(arg1, arg2, sizeof(Edge));
}

void usage()
{
  fprintf(stderr, "Usage:    poly2off [max_number_polygons]\n\n");
  fprintf(stderr, "Defaults: number of polygons = %u\n", DEF_POLYS);
  fprintf(stderr, "\t  number of points = %u\n\n", MAXPTS);
  fprintf(stderr, "Example:  poly2off < teapot.tpoly > teapot.off\n");
  exit(1);
}

void MakeEdge( Edge* e, uint v1, uint v2 )
{
  if (v1 > v2)
    {
      uint tmp=v1; v1=v2; v2=tmp;
    }
  e->v1=v1; e->v2=v2;
}

int main( int argc, char** argv )
{
  uint i, j;
  int  npts;
  long nLines    = 0;
  uint nPoints   = 0;
  uint nPolygons = 0;
  uint nEdges    = 0;
  uint maxPlanes = DEF_POLYS;
  uint** planes;
  Point points[MAXPTS];
  Edge  edgeList[MAXEDGES];
  
  if (argc == 2)
    {
      maxPlanes = atoi(argv[1]);
      if (!maxPlanes) usage();
    }
  
  planes = (uint **) malloc( maxPlanes * sizeof(uint*));
  if (!planes)
    {
      perror("poly2off");
      return 1;
    }
  
  while (scanf("%d", &npts) == 1)
    {
     uint old_pt; 
      if (npts <= 0)
	{
	  fprintf(stderr, "error: invalid number of points at line %ld\n",
		  nLines);
	  break;
	}
      nLines++;
      planes[nPolygons] = (uint*) malloc( (npts+1) * sizeof(uint));
      if (!planes[nPolygons]) 
	{
	  perror("poly2off");
	  return 1;
	}

      /* the first element contains the # of verticies in the polygon */
      planes[nPolygons][0] = npts;
      
      for (i=1; i <= npts; i++)
	{
	  Point pt;
	  Point* result;
	  size_t count = nPoints, idx;
	  
	  if (scanf("%f %f %f", &pt.x, &pt.y, &pt.z) != 3)
	    {
	      fprintf(stderr, "error: invalid points at line %ld\n", nLines);
	      return 1;
	    }
	  
	  result = (Point*) lsearch(&pt, points, &count,
				    sizeof(Point), ptcmp);

	  /* vertex index starts from 1, not 0 */

	  if (count == nPoints)
	    {
	      idx = (uint) (result - points)+1; /* index of point */
	    }
	  else {
 	    /* new point added to table */
	    if ((idx = ++nPoints) >= MAXPTS-1)
	      {
		fprintf(stderr, "error: too many points\n\n");
		usage();
	      }
	  }
	  
	  planes[nPolygons][i] = idx;
	  nLines++;
	} /* for */

     /* check edges formed by polygon */
     old_pt = planes[nPolygons][npts];
     for (i=1; nEdges < MAXEDGES && i <= npts; i++)
       {
	 uint idx = planes[nPolygons][i];
	 if (old_pt != idx)
	   {
	     Edge e;
	     size_t count = nEdges;
	     MakeEdge(&e, old_pt, idx);
	     lsearch(&e, edgeList, &count, sizeof(Edge), edgecmp);
	     if (count != nEdges) nEdges++;  /* new edge added */
	   }
	 old_pt = idx;
       } /* for each point */

     if (++nPolygons > maxPlanes)
       {
	 fprintf(stderr, "error: too many polygons: specify max parameter large enough\n\n");
	 usage();
       }
   } /* while */

  /*
   *   print object in Object File Format (OFF)
   */

  printf("%u %u %u\n", nPoints, nPolygons, nEdges);
  for (i=0; i < nPoints; i++)
    {
      printf("%g %g %g\n",  points[i].x, points[i].y, points[i].z);
    }
  
  for (i=0; i < nPolygons; i++)
    {
      printf("%u", planes[i][0]);
      for (j=1; j <= planes[i][0]; j++)
	printf(" %u", planes[i][j]);
      printf("\n");
    }

  return 0; /* okay */
}

