/***********************************************************************/
/* Open Visualization Data Explorer                                    */
/* (C) Copyright IBM Corp. 1989,1999                                   */
/* ALL RIGHTS RESERVED                                                 */
/* This code licensed under the                                        */
/*    "IBM PUBLIC LICENSE - Open Visualization Data Explorer"          */
/***********************************************************************/

#include <dxconfig.h>


#include "dx/dx.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <math.h>
#define MAX_PARAMS 21
#define MAX_RANK 16
#define NO 0
#define YES 1

static FILE *fp=NULL;
static int text_as_string = NO;
static char *tab[]  = {"","  ","    ","      ","        ","          ","          ", "              ","                ","                  "}; 
static char *fontstyle=NULL;

Error _dxfExportVRML(Object dxobject, char *filename);
Error parse_dx(Object dxobject,int *index,Matrix *mat,char *name,Point *box);
Error write_solid(Object dxobject,int triangle);
static Error write_positions(Object dxobject,int itab,int pos_dim);
static Error write_connections(Object dxobject,int triangle,int itab);
static Error write_colors(Object dxobject,int itab,RGBColor **rgb,float *op);
static Error write_normals(Object dxobject, int itab);
static Error write_elevation(Object dxobject);
static void write_appearence(RGBColor *rgb, float op,int lines, int itab);
static int write_transform(Matrix mat, int *trans_two);
Error SetViewPoint(Object dxobject);
static Error write_caption(Object o,Point *box);
static Error write_text(Object dxobject);
static void DGetRotationOfMatrix(Matrix mat, Vector rotvec, float *sinhrot);

Error _dxfExportVRML(Object dxobject, char *filename)
{
   char wrlname[256];
   int i=0, index = 0;
   Matrix default_mat = {1,0,0,0,1,0,0,0,1,0,0,0};
   char *cp;
   int texture;
   int number = 0, tiffcount = 0;
   Point box[8];
   char *string;

   /* open the vrml file */
   if (((cp=strrchr(filename, '.'))!=NULL) &&
		!strcmp(cp,".wrl") )
     strcpy(wrlname,filename);
   else{
     strcpy(wrlname,filename);
     strcat(wrlname,".wrl");
   }
   fp = fopen(wrlname,"w");

   if (!fp){
      DXSetError(ERROR_INTERNAL, "unable to open output file base on %s",
                                              filename);
          goto error;
   }

   /* Write out the parameters */
   fprintf(fp,"%s\n","#VRML V2.0 utf8");
   /* set Navigation default */
   fprintf(fp,"%s\n","NavigationInfo {");
   if (DXGetStringAttribute((Object)dxobject,"VRML Set NavigationInfo type",&string))
     fprintf(fp,"  type [%s]\n",string);
   else
     fprintf(fp,"%s\n","  type [\"EXAMINE\",\"WALK\",\"FLY\"] ");
   fprintf(fp,"%s\n","}");
   fprintf(fp,"%s\n","Group {");
   fprintf(fp,"%s\n"," children [");

   /* add first child node ?  */
   if (DXGetStringAttribute((Object)dxobject,"VRML Insert First Child Node",&string))
     fprintf(fp,"  %s\n",string);

   /* Calculate the Bounding Box of whole object so can position */
   /* Caption is exists */
   DXBoundingBox(dxobject,box);
   
   /* do we export strings are geometry? */
   if (!DXGetIntegerAttribute((Object)dxobject,"VRML text as string",
			&text_as_string)) 
     text_as_string = NO;

   /* if there a fontstyle */
   if (!DXGetStringAttribute((Object)dxobject,"VRML FontStyle",&fontstyle))
     fontstyle=NULL;

   /* now export object */
   if (!parse_dx(dxobject,&index,NULL,NULL,box))
      goto error;

   /* find the initial viewpoint based on bounding box of object */
   /* and a 45 degree field of view */
   SetViewPoint(dxobject);
     
   fprintf(fp,"%s\n","]");
   fprintf(fp,"%s\n","}");
   fclose(fp);

   return OK;

error:
   if (fp) fclose(fp);
   return ERROR;
}


/* if group recurse through the objects in a group */
Error parse_dx(Object dxobject, int *index,Matrix *mat,char *name,Point *box)
{
  Object o,o1;
  int i;
  Array a;
  Matrix default_mat = {1,0,0,0,1,0,0,0,1,0,0,0};
  Matrix tempmat,newmat;
  char *string;
  int simple_transform,screen_pos,trans_two=0;
  ModuleInput modin[2];
  ModuleOutput modout[1];

  switch(DXGetObjectClass((Object)dxobject)){
    case CLASS_GROUP:
    case CLASS_MULTIGRID:
      for (i=0; (o = DXGetEnumeratedMember((Group)dxobject,i,&name)); i++){
	    if (!parse_dx(o,index,mat,name,box))
	       goto error;
	 }
	 break;
    case CLASS_XFORM:
	 /* use Apply transform for any transformed object 
	    this can be changed to use write_transform if I could
	    figure out how to break the matrix into individual translations,
	    rotations, and scales correctly
	  */
	 DXGetXformInfo((Xform)dxobject,&o,&tempmat);
	 simple_transform = write_transform(tempmat,&trans_two);
	 if (simple_transform == 1) {
 	   if (!parse_dx(o,index,&tempmat,name,box))
	     goto error;
	   if (trans_two == 0) {
	     fprintf(fp,"%s\n"," ]");
	     fprintf(fp,"%s\n","}");
	   }
	   if (trans_two == 1) {
	     fprintf(fp,"%s\n"," ]");
	     fprintf(fp,"%s\n","}");
	     fprintf(fp,"%s\n"," ]");
	     fprintf(fp,"%s\n","}");
	   }
	 }
	 else {
	   o = DXApplyTransform((Object)dxobject,NULL);
	   if (!o)
	     goto error;
	   if (!parse_dx(o,index,&tempmat,name,box))
	     goto error;
	 }
	 break;
    case CLASS_SCREEN:
	 if (DXGetAttribute((Object)dxobject,"Caption string")) {
	   if (!write_caption(dxobject,box))
	     goto error;
	   break;
	 }
	 DXGetScreenInfo((Screen)dxobject,&o,&screen_pos,NULL);
	 /* Autoglyph text */
	 if (screen_pos == SCREEN_WORLD) {
	   if (DXGetXformInfo((Xform)o,&o1,&tempmat)
	           && DXGetAttribute((Object)o1,"Text string")) {
	     /* make these Billboard objects */
	     fprintf(fp,"%s\n","Billboard {");
	     fprintf(fp,"%s\n"," axisOfRotation 0.0 0.0 0.0");
	     fprintf(fp,"%s\n"," children [");
	     if (text_as_string == YES) {
	       if (!write_text(o1))
	         goto error;
	     }
	     else {
	       if (!parse_dx(o1,index,mat,name,box))
	         goto error;
	     }
	     fprintf(fp,"%s\n"," ]");
	     fprintf(fp,"%s\n","}");
	   }
	   else if (DXGetAttribute((Object)o,"Text string")) {
	     if (!write_text(o))
	       goto error;
	   }
	 }
	 else
    	   DXWarning("This screen objects are ignored");
	 break;
    case CLASS_FIELD:
	 if (!DXGetComponentValue((Field)dxobject,"positions")) {
	    DXWarning("Field does not contain positions, skipping");
	    break;
	 }

	 /* is this text ? */
	 if (text_as_string == YES) {
	   if (DXGetStringAttribute((Object)dxobject,"Text string",&string)){
	     if (!write_text(dxobject))
	       goto error;
	     break;
	   }
	 }

	 /* check connections component to get type of output 	*/
	 /* surface, lines, or points ?				*/
         if (!(a = (Array)DXGetComponentValue((Field)dxobject,"connections"))){
	    if (!write_solid(dxobject,0))
	       goto error;
	    break;
	 }
         else {

	 /* check element type and call solid with correct index */
	 if (!DXGetStringAttribute((Object)a,"element type",&string))
	    goto error;
	 if (!strcmp("lines",string)){
	    if (!write_solid(dxobject,1))
	       goto error;
	 }
	 else if (!strcmp("quads",string)){	
	   if (!write_solid(dxobject,3))
	      goto error;
	 }
	 else if (!strcmp("triangles",string)){  
	   if (!write_solid(dxobject,2))
	     goto error;
	 }
	 else {			/* not quad,triangle,lines, must be volume */
				/* call showboundary first 		   */
	   DXWarning("volume data is converted to boundary");
	   DXModSetObjectInput(&modin[0],"input",dxobject);
	   DXModSetIntegerInput(&modin[1],"validity",1);
	   DXModSetObjectOutput(&modout[0],NULL,&o);
	   if (!DXCallModule("ShowBoundary",2,modin,1,modout))
	     goto error;
	   if (!parse_dx(o,index,mat,name,box))
	     goto error;
	 }

	 break;
    }
    default:
	DXSetError(ERROR_INVALID_DATA,"not recognized type class"); 
	goto error;
  }

  return OK;
error:
  return ERROR;
}

/* write out each solid object */
Error write_solid(Object dxobject,int triangle)
{
  int i;
  Type type;
  int rank,shape[MAX_RANK];
  Array a;
  int np;
  float *f;
  int *d;
  char *string;
  float angle;
  int sided;
  int itab=1;
  float op;	/* constant color and opacity */
  RGBColor *rgb;
  int pos_dim=3;
  float deltas[4];

  rgb =NULL; op = -1;
  /* get positions array and check dimensionality */
  a = (Array)DXGetComponentValue((Field)dxobject,"positions");
  if (!DXGetArrayInfo(a,&np,&type,NULL,&rank,shape))
    goto error;
  if (rank != 1 || shape[0] < 2){
    DXSetError(ERROR_INVALID_DATA,"positions must be 3-d or 2-d\n");
    goto error;
  }
  if (shape[0]==2){
    pos_dim = 2;
    /* regular orthogonal 2-d grid use ElevationGrid node */
    if (DXQueryGridPositions(a,NULL,NULL,NULL,deltas)){
      if ((deltas[0]==0 || deltas[1]==0) || (deltas[2]==0 || deltas[3]==0)) 
        return (write_elevation(dxobject));
    }
  }
   
  /* what type of shape is it ?		*/
  /* PointSet - no connections		*/
  /* IndexedFaceSet - connections of quads or triangles	*/
  /* IndexedLineSet - connections of lines		*/
  fprintf(fp,"%s%s\n",tab[itab++],"Shape {");
  if (triangle == 0) 
    fprintf(fp,"%s%s \n",tab[itab++],"geometry PointSet {");
  else if (triangle == 1)
    fprintf(fp,"%s%s \n",tab[itab++],"geometry IndexedLineSet {");
  else{
    fprintf(fp,"%s%s \n",tab[itab++],"geometry IndexedFaceSet {");
    fprintf(fp,"%s%s \n",tab[itab],"solid	FALSE");
    if (triangle == 3) 		/* quads don't have counterclockwise connect */
      fprintf(fp,"%s%s \n",tab[itab],"ccw	FALSE");
  }

  /* Write out the positions */
  if (!write_positions(dxobject,itab,pos_dim))
    goto error;

  /* Write out the connections */
  if (triangle > 0 ){ 
    if (!write_connections(dxobject,triangle,itab))
      goto error;
  }
  
  /* Get colors and opacity info */
  if (!write_colors(dxobject,itab,&rgb,&op))
    goto error;

  /* Write out normals  (normals should be unit length) */
  if (triangle > 1 && !write_normals(dxobject,itab))
    goto error;

  fprintf(fp,"%s}\n",tab[--itab]);	/* geometry */

  /* always write appearance node to get default lighting */
  write_appearence(rgb,op,triangle,itab);

  fprintf(fp,"%s}\n",tab[--itab]);	/* Shape */
done:
  return OK;

error: return ERROR;
}

/* Write out the positions */
static Error write_positions(Object dxobject,int itab,int pos_dim)
{
  int i;
  Array a;
  int np,rank,shape[MAX_RANK];
  Type type;
  float *f;

  a = (Array)DXGetComponentValue((Field)dxobject,"positions");
  if (!DXGetArrayInfo(a,&np,&type,NULL,&rank,shape))
    goto error;
  fprintf(fp,"%s%s\n",tab[itab++],"coord Coordinate{ ");
  fprintf(fp,"%s%s\n",tab[itab++],"point [ ");
  f = (float *)DXGetArrayData(a);
  if (pos_dim == 2){
    for (i=0; i<np; i++)
      fprintf(fp,"%s%f %f 0.0\n", tab[itab],f[i*2], f[i*2 +1] ); 
  }
  else{
    for (i=0; i<np; i++)
      fprintf(fp,"%s%f %f %f\n", tab[itab],f[i*3], f[i*3 +1], f[i*3 +2]); 
  }
  fprintf(fp,"%s%s\n",tab[--itab],"]");
  fprintf(fp,"%s%s\n",tab[--itab],"}");
 
  return OK;
error:
  return ERROR;
}

/* Write out the connections */
static Error write_connections(Object dxobject,int triangle,int itab)
{
  int i,end=-1;
  Array a;
  int np,rank,shape[MAX_RANK];
  Type type;
  int *d;

      fprintf(fp,"%s%s\n",tab[itab++],"coordIndex [");
      a = (Array)DXGetComponentValue((Field)dxobject,"connections");
      if (!DXGetArrayInfo(a,&np,&type,NULL,&rank,shape))
        goto error;
      d = (int *)DXGetArrayData(a);

      if (triangle == 1) {
        for (i=0; i<np; i++)
          fprintf(fp,"%s%d %d %d\n", tab[itab],d[i*2],d[i*2 +1],end);
      }
      else if (triangle == 2) {
        for (i=0; i<np; i++)
          fprintf(fp,"%s%d %d %d %d\n", tab[itab],d[i*3],d[i*3 +1],d[i*3 +2],end);
      }
      else {
        for (i=0; i<np; i++) 	/* quads don't have counterclockwise connect */
        fprintf(fp,"%s%d %d %d %d %d\n",tab[itab],d[i*4],d[i*4 +1],d[i*4 +3],d[i*4 +2],end);
      }

      fprintf(fp,"%s%s\n",tab[--itab],"]");
  
  return OK;
error:
  return ERROR;
}

/* write colors using Color node if colors array exists		*/
/* set flag COLORS_PER_VERTEX :TRUE if dep positions 		*/
/*				 FALSE if dep connections	*/
/* if colors are constant return rgb set			*/
/* if opacities exist set op					*/
static 
Error write_colors(Object dxobject,int itab,RGBColor **rgb,float *op_avg)
{
  int i;
  Array a;
  int np,rank,shape[MAX_RANK];
  Type type;
  float *f,*op;
  char *string;

  if ((a = (Array)DXGetComponentValue((Field)dxobject,"colors"))){
    if (DXGetArrayClass(a) != CLASS_CONSTANTARRAY) {
      fprintf(fp,"%s%s\n",tab[itab++],"color Color {");
      fprintf(fp,"%s%s\n",tab[itab++],"color [");
      if (!DXGetArrayInfo(a,&np,&type,NULL,&rank,shape))
	goto error;
      f = (float *)DXGetArrayData(a);
      for (i=0; i<np; i++)
        fprintf(fp,"%s%f %f %f,\n",tab[itab],f[i*3], f[i*3 +1], f[i*3 +2]);
      fprintf(fp,"%s%s\n",tab[--itab],"]");
      fprintf(fp,"%s%s\n",tab[--itab],"}");
      if (!DXGetStringAttribute((Object)a,"dep",&string))
        goto error;
      if (!strcmp("connections",string)) 
        fprintf(fp,"%s%s\n",tab[itab],"colorPerVertex FALSE");
      else
        fprintf(fp,"%s%s\n",tab[itab],"colorPerVertex TRUE");
    }
    else 
      *rgb = (RGBColor *)DXGetConstantArrayData(a);
  }

  /* check for opacities */
  /* if constant array, use that value. if array, use average? 	*/
  /* in vrml 0.0-> opaque, 1.0-> transparent			*/
  if ((a = (Array)DXGetComponentValue((Field)dxobject,"opacities"))){
    if (DXGetArrayClass(a) != CLASS_CONSTANTARRAY){ 
      if (!DXStatistics(dxobject,"opacities",NULL,NULL,op_avg,NULL))
        goto error;
      *op_avg = 1.0 - *op_avg;
    }
    else{
      op = (float *)DXGetConstantArrayData(a);
      *op_avg = 1.0 - op[0];
    }
  }

  return OK;
error:
  return ERROR;
}

/* Write out normals  (normals should be unit length) */
static 
Error write_normals(Object dxobject, int itab)
{
  Array a;
  int i;
  int np,rank,shape[MAX_RANK];
  Type type;
  float *f;
  char *string;

  if ((a = (Array)DXGetComponentValue((Field)dxobject,"normals")) != NULL) {
    if (!DXGetArrayInfo(a,&np,&type,NULL,&rank,shape))
      goto error;
    f = (float *)DXGetArrayData(a);
    fprintf(fp,"%s%s\n",tab[itab++],"normal Normal {");
    fprintf(fp,"%s%s\n",tab[itab++],"vector [");
    for (i=0; i<np; i++)
      fprintf(fp,"%s%f %f %f\n", tab[itab],f[i*3], f[i*3 +1], f[i*3 +2]);
    fprintf(fp,"%s%s\n",tab[--itab],"]");
    fprintf(fp,"%s%s\n",tab[--itab],"}");
    if (!DXGetStringAttribute((Object)a,"dep",&string))
	    goto error;
    if (!strcmp("connections",string)) 
      fprintf(fp,"%s%s\n",tab[itab],"normalPerVertex	FALSE");
  }  
  else
    DXWarning("this surface has no normals");
  
  return OK;
error:
  return ERROR;
}
 
/* write out an Elevation Grid node of 2-d regular surfaces (flat) 	*/
/* height is constant zero 						*/
/* VRML positions this at [0,0] in x,z plane, so must be transformed	*/
static Error write_elevation(Object dxobject)
{
  int i=0;
  Array a,new_a;
  int counts[3];
  float origin[3],delta[4];
  int itab=1,np,data_np;
  float op,*f;
  RGBColor *rgb;
  ModuleInput modin[1];
  ModuleOutput modout[1];
  Object o;
  float scale=0,min;

  rgb=NULL; op=-1.0;
  /* call Reorient to make sure grid is x varies fastest	*/
  /* with positive deltas					*/
  DXReference(dxobject);
  DXModSetObjectInput(&modin[0],"image",dxobject);
  DXModSetObjectOutput(&modout[0],NULL,&o);
  if (!DXCallModule("Reorient",1,modin,1,modout))
    goto error;
  
  a = (Array)DXGetComponentValue((Field)o,"positions");
  if (!DXGetArrayInfo(a,&np,NULL,NULL,NULL,NULL))
      goto error;
  if (!DXQueryGridPositions(a,NULL,counts,origin,delta))
    goto error;

  /* what is the transformation need to place this surface */
  fprintf(fp,"%s%s\n",tab[itab++],"Transform {");
  fprintf(fp,"%s%s %g %g %g\n",tab[itab],"translation",origin[0],origin[1],0.0);
  fprintf(fp,"%s%s %g %g %g %g\n",tab[itab],"rotation",1.0,0.0,0.0,-1.57);
  fprintf(fp,"%s%s\n",tab[itab],"children [");
  fprintf(fp,"%s%s\n",tab[itab++],"Shape {");
  fprintf(fp,"%s%s\n",tab[itab++],"geometry ElevationGrid {");
  fprintf(fp,"%s%s %d\n",tab[itab],"xDimension",counts[1]);
  fprintf(fp,"%s%s %d\n",tab[itab],"zDimension",counts[0]);
  fprintf(fp,"%s%s %g\n",tab[itab],"xSpacing",delta[2]);
  fprintf(fp,"%s%s %g\n",tab[itab],"zSpacing",delta[1]);
  fprintf(fp,"%s%s\n",tab[itab],"solid FALSE");

  /* Look for special VRML attributes to place data into height field */
  fprintf(fp,"%s%s ",tab[itab],"height [");
  if (DXGetFloatAttribute((Object)o,"VRML use data as height",&scale) ||
      DXGetIntegerAttribute((Object)o,"VRML use data as height",&i)){ 
    if (i!=0) scale = (float)i;
    a = (Array)DXGetComponentValue((Field)o,"data");
    if (!DXGetArrayInfo(a,&data_np,NULL,NULL,NULL,NULL))
      goto error;
    if (data_np!=np){	
      DXSetError(ERROR_INVALID_DATA,
	"data not dep positions, can't use as height field");
      goto error;
    }
    new_a = (Array)DXArrayConvert(a,TYPE_FLOAT,CATEGORY_REAL,0);
    if (!new_a)
      goto error;
    if (!DXStatistics((Object)new_a,"data",&min,NULL,NULL,NULL))
      goto error;
    f = (float *)DXGetArrayData(new_a);
    fprintf(fp,"%g",f[0]*scale);
    for (i=1; i<np; i++){
      fprintf(fp,",%g",f[i]+(f[i]-min)*scale);
      if (i % 5 == 0) fprintf(fp,"%s%s","\n",tab[itab]);
    }
    DXDelete((Object)new_a);
  }
  else{
    /* create height data containing all zeros for flat surface */
    fprintf(fp,"%g",0.0);
    for (i=1; i<np; i++){
      fprintf(fp,",%g",0.0);
      if (i % 5 == 0) fprintf(fp,"%s%s","\n",tab[itab]);
    }
  }
  fprintf(fp," %s\n","]");
  
  /* Get colors and opacity info */
  if (!write_colors(o,itab,&rgb,&op))
    goto error;

  /* Write out normals  (normals should be unit length) */
  if (!write_normals(o,itab))
    goto error;
  
  fprintf(fp,"%s%s\n",tab[--itab],"}");	/* ElevationGrid */
  
  /* always write appearance node to get default lighting */
  write_appearence(rgb,op,2,itab);
  
  fprintf(fp,"%s%s\n",tab[--itab],"}"); 	/* Shape */
  fprintf(fp,"%s%s\n",tab[itab],"]");	/* Children */
  fprintf(fp,"%s%s\n",tab[--itab],"}");	/* Transform */

  return OK;
error:
  return ERROR;
}

static void write_appearence(RGBColor *rgb, float op, int lines, int itab)
{
  fprintf(fp,"%s%s\n",tab[itab++],"appearance Appearance {");
  fprintf(fp,"%s%s\n",tab[itab++],"material Material {");
  if (rgb) 
    fprintf(fp,"%s%s %f %f %f\n",tab[itab],"diffuseColor",rgb->r,rgb->g,rgb->b);
  if (lines == 1) {	/* lines connections */
    if (rgb) fprintf(fp,"%s%s %f %f %f\n",tab[itab],"emissiveColor",rgb->r,rgb->g,rgb->b);
    else fprintf(fp,"%s%s ",tab[itab],"emissiveColor 1 1 1");
  }
  if (op >=0.0) fprintf(fp,"%s%s %f\n",tab[itab],"transparency",op);
  fprintf(fp,"%s}\n",tab[--itab]);	/* Material */
  fprintf(fp,"%s}\n",tab[--itab]);	/* Appearance */
}

/* find the initial viewpoint based on bounding box of object */
/* and a 45 degree field of view */
Error SetViewPoint(Object dxobject)
{
  Point center;
  Point box[8];
  double d,distance;

  if (DXBoundingBox(dxobject,box)) {
    d = DXLength(DXSub(box[3],box[4]));
    distance = 1.12 * .5*d/tan(.5*.785398);
    center.x = (box[4].x +box[3].x)/2;
    center.y = (box[4].y +box[3].y)/2;
    center.z = (box[4].z +box[3].z)/2;
    fprintf(fp,"%s\n","Viewpoint {");
    fprintf(fp,"%s %g %g %g\n"," position",center.x,center.y,center.z+distance);
    fprintf(fp,"%s\n","}");
     
  }
    
  return OK;
}

/* write out transformation matrix */
static int write_transform(Matrix mat,int *trans_two)
{
  float angle,angle2;
  float sinhrot;
  Vector rotvec;
  Matrix mat_zx = {0,0,1,1,0,0,0,1,0};
  Matrix mat_zy = {0,1,0,0,0,1,1,0,0};

  /* what kind of transformation is it */
  if (mat.A[0][1]==0 && mat.A[0][2]==0 && mat.A[1][0]==0 && mat.A[1][2]==0 &&
	mat.A[2][0]==0 && mat.A[2][1]==0){ 
    if (mat.b[0]==0 && mat.b[1]==0 && mat.b[2]==0){ 
      if (mat.A[0][0]!=1 || mat.A[1][1]!=1 || mat.A[2][2]!=1){	/* scale */
        fprintf(fp,"%s\n","Transform {");
        fprintf(fp,"%s %f %f %f\n","scale",mat.A[0][0],mat.A[1][1],mat.A[2][2]);
      }
      else {	/* identity */
	*trans_two = -1;
	return 1;
      }
    }
    else {	/* translation */
      fprintf(fp,"%s\n","Transform {");
      fprintf(fp,"%s %f %f %f\n","translation",mat.b[0],mat.b[1],mat.b[2]);
    }
  }
  else{ 		/* rotation */
    DGetRotationOfMatrix(mat, rotvec, &sinhrot);
    fprintf(fp,"%s %g %g %g %g\n","# rot angle ",rotvec.x,rotvec.y,rotvec.z,    	2.*asin((double)sinhrot));
    if (mat.A[0][0]==1)	{	/* about the x-axis */
      /* angle = acos(mat.A[1][1]); */
      angle = asin(mat.A[1][2]);
      if (mat.A[1][1] >= 0)
	angle = angle;
      else {
	angle = 3.14159 - angle;
        fprintf(fp,"%s %g\n","# cos sin ",angle);
      }
      fprintf(fp,"%s\n","Transform {");
      fprintf(fp,"%s %f\n","rotation 1 0 0",angle);
    }
    else if (mat.A[1][1]==1) {	/* about the y-axis */
      /* angle = acos(mat.A[0][0]); */
      angle = -asin(mat.A[0][2]);
      if (mat.A[0][0] >= 0)
	angle = angle;
      else {
	angle = 3.14159 - angle;
        fprintf(fp,"%s %g\n","# cos sin ",angle);
      }
      fprintf(fp,"%s\n","Transform {");
      fprintf(fp,"%s %f\n","rotation 0 1 0",angle);
    }
    else if (mat.A[2][2]==1) {	/* about the z-axis */
      /* angle = acos(mat.A[0][0]); */
      angle = asin(mat.A[0][1]);
      if (mat.A[0][0] >= 0)
	angle = angle;
      else {
	angle = 3.14159 - angle;
        fprintf(fp,"%s %g\n","# cos sin ",angle);
      }
      fprintf(fp,"%s\n","Transform {");
      fprintf(fp,"%s %f\n","rotation 0 0 1",angle);
    }
    else {
      /* special case check for permutations (AutoAxes uses these) */
      if(mat.A[0][1] == 1){
	if (mat.A[1][2]==1 && mat.A[2][0]==1 && mat.A[0][0]==0 && 
	    mat.A[0][2]==0 && mat.A[1][0]==0 && mat.A[1][1]==0 && 
	    mat.A[2][1]==0 && mat.A[2][2]==0){
          fprintf(fp,"%s\n","Transform {");
          fprintf(fp,"%s\n","rotation 0 1 0 1.57");
	  fprintf(fp,"%s\n"," children[");
	  fprintf(fp,"%s\n","Transform {");
          fprintf(fp,"%s\n","rotation 0 0 1 1.57");
	  *trans_two=1;
	}
	else return 0;
      }
      else if (mat.A[0][2]==1){
	if (mat.A[1][0]==1 && mat.A[2][1]==1 && mat.A[0][0]==0 && 
	    mat.A[0][1]==0 && mat.A[1][1]==0 && mat.A[1][2]==0 && 
	    mat.A[2][0]==0 && mat.A[2][2]==0){
          fprintf(fp,"%s\n","Transform {");
          fprintf(fp,"%s\n","rotation 1 0 0 -1.57");
	  fprintf(fp,"%s\n"," children[");
	  fprintf(fp,"%s\n","Transform {");
          fprintf(fp,"%s\n","rotation 0 0 1 -1.57");
	  *trans_two=1;
	}
	else return 0;
      }
      else
        return 0;		/* not a simple translation,rotation,scale */
    }
  }
  fprintf(fp,"%s\n"," children [");
  
  /* check for translation */
  /*
  if (mat.b[0]==0 && mat.b[1]==0 && mat.b[2]==0){ 
      fprintf(fp,"%s\n","Transform {");
      fprintf(fp,"%s %f %f %f\n","translation",mat.b[0],mat.b[1],mat.b[2]);
      fprintf(fp,"%s\n"," children [");
      *trans_two = 1;
  }
  */
  return 1;
}

/* Use the Text Node and ProximitySensor to create a caption that */
/* is fixed in the screen */
static Error write_caption(Object dxobject,Point *box)
{
  int i=0,itab=1;
  Point center;
  float *pos;
  char *string;
  Object o,o1;
  float *rgb;	/* constant color */
  Array a;

  rgb = NULL;
  /* is there a colors component (if so is always a constant array) */
  if (!DXGetScreenInfo((Screen)dxobject,&o,NULL,NULL))
    goto error;
  if (!DXGetXformInfo((Xform)o,&o1,NULL))
    goto error;
  if ((o=DXGetEnumeratedMember((Group)o1,0,NULL))==NULL)
    goto error;
  if (!DXGetScreenInfo((Screen)o,&o1,NULL,NULL))
    goto error;
  if (!DXGetXformInfo((Xform)o1,&o,NULL))
    goto error;
  if ((a = (Array)DXGetComponentValue((Field)o,"colors")))
    rgb = (float *)DXGetConstantArrayData(a);

  if (DXGetIntegerAttribute((Object)dxobject,"Caption flag",&i) && i != 0){
    DXWarning("VRML export can only have captions that are viewport relative");
    pos[0] = 0.5;
    pos[1] = 0.05;
  }
  o = DXGetAttribute((Object)dxobject,"Caption position");
  pos = (float *)DXGetArrayData((Array)o);
  o = DXGetAttribute((Object)dxobject,"Caption string");

  /* Calculate the Transformation needed to place the caption at position */
  /* I have fixed the translation in z so size of caption is done by  */
  /* font size parameter		*/
  center.x = pos[0] * 0.2 -0.1;
  center.y = pos[1] * 0.12 -0.06;
  center.z = -0.15;
  fprintf(fp,"%s%s\n",tab[itab++],"DEF HudGroup Collision {");
  fprintf(fp,"%s%s\n",tab[itab],"collide FALSE");
  fprintf(fp,"%s%s\n",tab[itab++],"children [");
  fprintf(fp,"%s%s\n",tab[itab++],"DEF HudProx ProximitySensor {");
  fprintf(fp,"%s%s\n",tab[itab],"center 0 20 0");
  fprintf(fp,"%s%s\n",tab[itab],"size 500 500 500");
  fprintf(fp,"%s%s\n",tab[--itab],"}");
  fprintf(fp,"%s%s\n",tab[itab++],"DEF HudXform Transform {");
  fprintf(fp,"%s%s\n",tab[itab++],"children [");
  fprintf(fp,"%s%s\n",tab[itab++],"Transform {");
  fprintf(fp,"%s%s %g %g %g\n",tab[itab],"translation",center.x,center.y,center.z);
  fprintf(fp,"%s%s\n",tab[itab++],"children [");

  fprintf(fp,"%s%s\n",tab[itab++],"Shape {");
  fprintf(fp,"%s%s\n",tab[itab++],"geometry Text {");
  fprintf(fp,"%s%s", tab[itab],"string [");
  while (DXExtractNthString(o,i++,&string))
    fprintf(fp,"\"%s\"",string);
  fprintf(fp,"%s\n","]");
  if (DXGetStringAttribute((Object)dxobject,"VRML FontStyle",&string))
    fprintf(fp,"%s%s%s%s\n",tab[itab],"fontStyle FontStyle {",string," }");
  else {
    fprintf(fp,"%s%s\n",tab[itab],"fontStyle FontStyle {size .01");
    fprintf(fp,"%s%s\n",tab[itab], "justify [\"MIDDLE\" \"MIDDLE\"] }");
  }
  fprintf(fp,"%s%s\n",tab[--itab],"}");		/* Text */
  fprintf(fp,"%s%s\n",tab[itab++],"appearance Appearance {");
  fprintf(fp,"%s%s\n",tab[itab],"material Material {");
  if (rgb){
    fprintf(fp,"%s%s %g %g %g\n",tab[itab],"diffuseColor",rgb[0],rgb[1],rgb[2]);
    fprintf(fp,"%s%s %g %g %g\n",tab[itab],"emissiveColor",rgb[0],rgb[1],rgb[2]);
  }
  else
    fprintf(fp,"%s%s\n",tab[itab],"emissiveColor 1 1 1");
  fprintf(fp,"%s%s\n",tab[itab],"}");		/* Material */
  fprintf(fp,"%s%s\n",tab[--itab],"}");		/* Appearance */
  fprintf(fp,"%s%s\n",tab[--itab],"}");		/* Shape */

  fprintf(fp,"%s%s\n",tab[itab],"]");		/* children */
  fprintf(fp,"%s%s\n",tab[--itab],"}");		/* Transform */
  fprintf(fp,"%s%s\n",tab[--itab],"]");		/* children */
  fprintf(fp,"%s%s\n",tab[--itab],"}");		/* Transform */
  fprintf(fp,"%s%s\n",tab[--itab],"]");		/* children */
  fprintf(fp,"%s%s\n",tab[itab],"ROUTE HudProx.orientation_changed TO HudXform.rotation");
  fprintf(fp,"%s%s\n",tab[itab],"ROUTE HudProx.position_changed TO HudXform.translation");
  
  fprintf(fp,"%s%s\n",tab[--itab],"}");		/* collision */
  
  return OK;

error:
  return ERROR;
}
   
static Error
write_text(Object dxobject)
{
  char *string;
  float *rgb;
  Array a;
  int itab = 1;
  Point box[8];
  Point center;

  /* Use bounding box to get position of Text */
  /* 
  DXBoundingBox(dxobject,box);
  center.x = (box[4].x +box[3].x)/2;
  center.y = (box[4].y +box[3].y)/2;
  center.z = (box[4].z +box[3].z)/2;
  fprintf(fp,"%s%s\n",tab[itab++],"Transform {");
  fprintf(fp,"%s%s %g %g %g\n",tab[itab],"translation",center.x,center.y,center.z);
  fprintf(fp,"%s%s\n",tab[itab++],"children [");
  */
  fprintf(fp,"%s%s\n",tab[itab++],"Shape {");
  fprintf(fp,"%s%s\n",tab[itab++],"geometry Text {");
  fprintf(fp,"%s%s", tab[itab],"string [");
  DXGetStringAttribute((Object)dxobject,"Text string",&string);
  fprintf(fp,"\"%s\"",string);
  fprintf(fp,"%s\n","]");
  if (DXGetStringAttribute((Object)dxobject,"VRML FontStyle",&string))
    fprintf(fp,"%s%s%s%s\n",tab[itab],"fontStyle FontStyle {",string," }");
  else if (&fontstyle != NULL)
    fprintf(fp,"%s%s%s%s\n",tab[itab],"fontStyle FontStyle {",fontstyle," }");
  else
    fprintf(fp,"%s%s\n",tab[itab],"fontStyle FontStyle {size 1.25}");
  fprintf(fp,"%s%s\n",tab[--itab],"}");		/* Text */
  fprintf(fp,"%s%s\n",tab[itab++],"appearance Appearance {");
  fprintf(fp,"%s%s\n",tab[itab],"material Material {");
  if ((a = (Array)DXGetComponentValue((Field)dxobject,"colors"))){
    rgb = (float *)DXGetArrayData(a);
    fprintf(fp,"%s%s %g %g %g\n",tab[itab],"diffuseColor",rgb[0],rgb[1],rgb[2]);
    fprintf(fp,"%s%s %g %g %g\n",tab[itab],"emissiveColor",rgb[0],rgb[1],rgb[2]);
  }
  else
    fprintf(fp,"%s%s\n",tab[itab],"emissiveColor 1 1 1");
  fprintf(fp,"%s%s\n",tab[itab],"}");		/* Material */
  fprintf(fp,"%s%s\n",tab[--itab],"}");		/* Appearance */
  fprintf(fp,"%s%s\n",tab[--itab],"}");		/* Shape */
  /*
  fprintf(fp,"%s%s\n",tab[--itab],"]");
  fprintf(fp,"%s%s\n",tab[--itab],"}");
  */
  return OK;
}

void DGetRotationOfMatrix(Matrix mat, Vector rotvec, float *sinhrot)
{
    /* Double precision version of GetRotationOfMatrix.			*/
    /* Input:	pointer to mat = rotation/transl (no scaling) matrix	*/
    /* Output:	pointer to rotvec = rotation unit vector		*/
    /* 		pointer to sinhrot = sine of rotation half-angle	*/

    int i, j, imax1, imax2;
    float rows[3][3],rowmag[3];
    Vector tanvec, cenvec, sensvec, maxaxis;
    float maxmag, minmag;
    Vector row_max,row_med,mat_max;

    /* Find the row of (mat - identity) whose magnitude is maximum, and	*/
    /* similarly for minimum.  The latter is to get the median row by	*/
    /* the process of elimination.					*/

    for(i = 0; i < 3; i++) {
	float amag = (float)sqrt(mat.A[i][0]*mat.A[i][0] + mat.A[i][1]*mat.A[i][1]
						+ mat.A[i][2]*mat.A[i][2]);
	rowmag[i] = 0.;
	for(j = 0; j < 3; j++) {
	    rows[i][j] = mat.A[i][j]/amag - (float)(i == j);
	    rowmag[i] += rows[i][j]*rows[i][j];
	}
	rowmag[i] = (float)sqrt(rowmag[i]);
	if(!i || rowmag[i] > maxmag) {
	    maxmag = rowmag[i];
	    imax1 = i;
	}
	if(!i || rowmag[i] < minmag) {
	    minmag = rowmag[i];
	    imax2 = i;
	}
    }
    /* If all changes are the same, complement one of the indices.	*/
    if(imax1 == imax2) imax2 = 2 - imax1;
    if(maxmag < 1.e-14) {
	/* There is no rotation.  Return a vector along z with zero	*/
	/* rotation (sine = 0).						*/
	rotvec.x = rotvec.y = *sinhrot = 0.;
	rotvec.z = 1.;
	return;
    }
    /* With min and max rows in hand, median row is only one left.	*/
    /* Find it from the fact that the sum of indices = 3.		*/
    imax2 = 3 - imax1 - imax2;
    row_max.x = rows[imax1][0];
    row_max.y = rows[imax1][1];
    row_max.z = rows[imax1][2];
    row_med.x = rows[imax2][0];
    row_med.y = rows[imax2][1];
    row_med.z = rows[imax2][2];
    mat_max.x = mat.A[imax1][0];
    mat_max.y = mat.A[imax1][1];
    mat_max.z = mat.A[imax1][2];
    /* Cross the max row with the second max row and unitize to get the	*/
    /* rotation unit vector.						*/
    rotvec = DXNormalize(DXCross(row_max,row_med));
    /* Determine which direction it should point (backward or not)	*/
    /* by forcing it to lie in the same hemisphere as the cross product	*/
    /* of the axis vector corresponding to max row and its change.	*/
    /* That product establishes the right-handedness of the rotation.	*/
    sensvec = DXCross(mat_max, row_max);
    if(DXDot(rotvec, sensvec) < 0.)
      rotvec = DXNeg(rotvec);
    /* Cross rotation vector into axis vector corresponding to max	*/
    /* row, and unitize to get direction of axis tip motion at 		*/
    /* beginning of rotation (i. e. initial tangent).			*/
    maxaxis.x=maxaxis.y=maxaxis.z=0.;
    if (imax1 == 0) maxaxis.x=1.;
    else if (imax1 == 1) maxaxis.y=1.;
    else maxaxis.z=1.;
    tanvec = DXNormalize(DXCross(rotvec, maxaxis));
    /* Cross rotation unit vector into that initial tangent vector	*/
    /* to get corresponding centripetal vector.				*/
    cenvec = DXCross(rotvec, tanvec);
    /* The centripetal component (dot product with centripetal unit	*/
    /* of unitized max row is sine of half-rotation angle.		*/
    row_max = DXNormalize(row_max);
    *sinhrot = DXDot(row_max, cenvec);
}
