/*********************************************************************
 *
 *         EZWGL, the EZ Widget and Graphics Library
 *
 *             Copyright (C) 1996, 1997  Maorong Zou
 *  
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 **********************************************************************/
/*
 *  June 1996.  Beta Release.
 *  Sept 1996.  Release Version 1.0
 *  Dec 1996.  Release Version 1.1 Beta
 *  April 1997.  Release Version 1.2
 *  November 1997.  Release Version 1.3
 */
/***********************************************************************
 *
 *               Tesselate a sphere
 *
 ***********************************************************************/
#define _EZ_SPHERE_C_
#include "EZ_Widget.h"

/***********************************************************************
 *
 *   Functions implemented in this file
 */
void EZ_Sphere MY_ANSIARGS((int type, int level, float x, float y, float z, float radius));
void EZ_FreeSpheres MY_ANSIARGS((void));

    
/*************************************************************************
 * 
 * The code below is taken from the attic. It works but it is rather ugly.
 */
typedef struct triangle_ {
  int v1[2],v2[2],v3[2];
  EZ_Vector3 **f;
  struct triangle_ *next;
  struct triangle_ *down;
} EZ_Triangle;

#define New_triangle(t)  t=(EZ_Triangle *)my_malloc( sizeof(struct triangle_), _SPHERE_TRIANGLE_);\
                t->next = (EZ_Triangle *)0; t->down = (EZ_Triangle *)0

/***********************************************************************
 *
 *   Local functions
 */
static void  destroy_triangle MY_ANSIARGS((EZ_Triangle *tra));
static void  subdivide_triangle MY_ANSIARGS((EZ_Triangle *tra));
static void  gen_sphere_triangle_data MY_ANSIARGS((int n));
static void  gen_sphere_cube_data MY_ANSIARGS((int n));
static void  gen_sphere_parametric_data MY_ANSIARGS((int n));

/***********************************************************************
 *
 *   Local data
 */

typedef struct sphere_data {
  int nv;
  EZ_Vector3 **f[6];
} EZ_SphereData;

static int sphere_cube = 0;
static int sphere_triangle = 0;
static int sphere_parametric = 0;
static EZ_SphereData *sphere_cube_data[8], *sphere_triangle_data[8];
static EZ_SphereData *sphere_parametric_data[8];

/***********************************************************************
 *
 *  Display a Sphere.
 *
 */
extern void    EZ_TriangularMeshForSphere();
extern void    EZ_QuadMeshForSphere();

void EZ_Sphere(type, level, x,y,z, radius)
     int    type, level;
     float  x,y,z,radius;
{
  if(EZ_InCompilingMode != 0)
    {
      EZ_Token *t = AllocateNTokens(7);
      t->i = TOKEN_SPHERE;
      t[1].i = type; t[2].i = level; 
      t[3].f = x; t[4].f = y; t[5].f = z; t[6].f = radius;
    }
  if(EZ_InExecutionMode != 0)
    {  
      int           i,n,nv;
      EZ_SphereData *sphere;
      EZ_Vector3    **vtx;

      n = (level < 1 ? 4: (level > 8 ? 8: level));
      switch(type)
	{
	case EZ_SPHERE_TRIANGLE:
	  if( !(sphere_triangle & (1 <<(n-1))))
	    gen_sphere_triangle_data(n);
	  sphere = sphere_triangle_data[n-1];
	  nv = sphere->nv;

	  EZ_PushMatrix();
	  EZ_Translate(x,y,z);
	  EZ_Scale(radius,radius,radius);
	  for(i = 0; i < 4; i++)
	    {
	      vtx = (sphere->f)[i];
	      EZ_TriangularMeshForSphere(vtx,nv,nv);
	    }
	  EZ_PopMatrix();
	  
	  break;
	case EZ_SPHERE_QUAD:
	  if( !(sphere_cube & (1 <<(n-1))))
	    gen_sphere_cube_data(n);
	  sphere = sphere_cube_data[n-1];
	  nv = sphere->nv;

	  EZ_PushMatrix();
	  EZ_Translate(x,y,z);
	  EZ_Scale(radius,radius,radius);
	  for(i = 0; i < 6; i++)
	    {
	      vtx = (sphere->f)[i];
	      EZ_QuadMeshForSphere(vtx,nv,nv);
	    }
	  EZ_PopMatrix();
	  break;
	case EZ_SPHERE_PARAMETRIC:
	  if( !(sphere_parametric & (1 <<(n-1))))
	    gen_sphere_parametric_data(n);
	  sphere = sphere_parametric_data[n-1];
	  nv = sphere->nv;
	  vtx = (sphere->f)[0];
	  EZ_PushMatrix();
	  EZ_Translate(x,y,z);
	  EZ_Scale(radius,radius,radius);
	  EZ_QuadMeshForSphere(vtx,nv,nv);
	  EZ_PopMatrix();
	  break;
	default:
	  break;
	}
    }
}	

void EZ_FreeSpheres()
{
  EZ_SphereData *t;
  int i, j, k, nv;

  for(i = 0; i < 8; i++)
    {
      if(sphere_triangle & (1 << i))
	{
	  t = sphere_triangle_data[i];
	  nv = t->nv;
	  for(j = 0; j < 4; j++)
	    {
	      for(k = 0; k < nv; k++)
		(void)my_free( (char *)(t->f)[j][k]);
	      (void)my_free((char *)(t->f)[j]);
	    }
	  (void)my_free( (char *)t);
	}
    }
  sphere_triangle = 0;

  for(i = 0; i < 8; i++)
    {
      if(sphere_cube & (1 << i))
	{
	  t = sphere_cube_data[i];
	  nv = t->nv;
	  for(j = 0; j < 6; j++)
	    {
	      for(k = 0; k < nv; k++)
		(void)my_free( (char *)(t->f)[j][k]);
	      (void)my_free((char *)(t->f)[j]);
	    }
	  (void)my_free( (char *)t);
	}
    }
  sphere_cube = 0;
  for(i = 0; i < 8; i++)
    {
      if(sphere_parametric & (1 << i))
	{
	  t = sphere_parametric_data[i];
	  nv = t->nv;
	  for(k = 0; k < nv; k++)
	    (void)my_free( (char *)(t->f)[0][k]);
	  (void)my_free((char *)(t->f)[0]);
	  (void)my_free( (char *)t);
	}
    }
  sphere_parametric = 0;
}

  
/*************************************************************************
 * 
 *  Generate a parametric tesselation of the unit sphere.
 */
static void  gen_sphere_parametric_data(n)
     int n;
{
  EZ_SphereData *t;
  int i,j,k;
  
  n = (n < 1 ? 4: (n > 8 ? 8: n));
  if( sphere_parametric & ( 1<< (n-1)) ) return;
  sphere_parametric |= ( 1<< (n-1));
  
  {  /* allocate memeory */
    t = (EZ_SphereData *)my_malloc( sizeof(EZ_SphereData), _SPHERE_P_);
    if(!t) EZ_OutOfMemory("gen_sphere_parametric_data");
    t->nv = k = (1<< n);
    sphere_parametric_data[n-1] = t;  /* save this data set */

    (t->f)[0] = (EZ_Vector3 **)my_malloc(k*sizeof(EZ_Vector3 *), _SPHERE_V3_);
    if(!(t->f[0])) EZ_OutOfMemory("gen_sphere_parametric_data");
    for(j=0; j< k; j++)
      {
	(t->f)[0][j] = (EZ_Vector3 *)my_malloc(k*sizeof(EZ_Vector3), _SPHERE_V3_); 
	if(!((t->f)[0][j])) EZ_OutOfMemory("gen_sphere_parametric_data");
      }
  }
  
  {
    float dtheta,dphi, theta, phi;
    float sinphi;

    dtheta =  6.2831853071795862/(float)(k-1);
    dphi = 0.5 * dtheta;
    
    phi = 0.0;
    for( i = 0; i < k; i++)
      {
	theta = 0.0;
	sinphi = sin(phi);
	for(j = 0; j < k; j++)
	  {
	    (t->f)[0][i][j][0] = sinphi * cos(theta);
	    (t->f)[0][i][j][1] = sinphi * sin(theta);
	    (t->f)[0][i][j][2] = cos(phi);
	    theta -= dtheta;
	  }
	phi += dphi;
      }
  }
}

/***********************************************************************
 *
 *   Tesselate the unit sphere by triangles.
 */
static void  gen_sphere_triangle_data(n)
     int n;
{
  EZ_SphereData *t;
  EZ_Triangle *T = NULL;
  EZ_Vector3 **f;
  int i,j,k;
  
  n = (n < 1 ? 4: (n > 8 ? 8: n));
  if( sphere_triangle & ( 1<< (n-1)) ) return;
  sphere_triangle |= ( 1<< (n-1));
  
  {  /* allocate memeory */
    t = (EZ_SphereData *)my_malloc( sizeof(EZ_SphereData), _SPHERE_DATA_);
    if(!t) EZ_OutOfMemory("gen_sphere_triangle_data");
    t->nv = k = (1<< (n-1))+1;
    sphere_triangle_data[n-1] = t;  /* save this data set */

    for(i=0; i < 4; i++)
      {
	(t->f)[i] = (EZ_Vector3 **)my_malloc(k*sizeof(EZ_Vector3 *), _SPHERE_TRIANGLE_);
	if(!(t->f)[i]) EZ_OutOfMemory("gen_sphere_triangle_data");
	for(j=0; j< k; j++)
	  {
	    (t->f)[i][j] = (EZ_Vector3 *)my_malloc(k*sizeof(EZ_Vector3), _SPHERE_TRIANGLE_); 
	    if(!((t->f)[i][j])) EZ_OutOfMemory("gen_sphere_triangle_data");
	  }
      }
  }
  { /* initialize data */
    k -= 1;
    f = (t->f)[0];
    f[0][0][0] = 1.0; f[0][0][1] = 0.0; f[0][0][2] = 0.0;
    f[0][k][0] = 0.0; f[0][k][1] = 1.0; f[0][k][2] = 0.0;
    f[k][0][0] = 0.0; f[k][0][1] = 0.0; f[k][0][2] = 1.0;
    f[k][k][0] = -1.0; f[k][k][1] = 0.0; f[k][k][2] = 0.0;
    f = (t->f)[1];
    f[0][0][0] = -1.0; f[0][0][1] = 0.0; f[0][0][2] = 0.0;
    f[0][k][0] = 0.0; f[0][k][1] = -1.0; f[0][k][2] = 0.0;
    f[k][0][0] = 0.0; f[k][0][1] = 0.0; f[k][0][2] = 1.0;
    f[k][k][0] = 1.0; f[k][k][1] = 0.0; f[k][k][2] = 0.0;
    f = (t->f)[2];
    f[0][0][0] = 1.0; f[0][0][1] = 0.0; f[0][0][2] = 0.0;
    f[0][k][0] = 0.0; f[0][k][1] = 0.0; f[0][k][2] = -1.0;
    f[k][0][0] = 0.0; f[k][0][1] = 1.0; f[k][0][2] = 0.0;
    f[k][k][0] = -1.0; f[k][k][1] = 0.0; f[k][k][2] = 0.0;
    f = (t->f)[3];
    f[0][0][0] = -1.0; f[0][0][1] = 0.0; f[0][0][2] = 0.0;
    f[0][k][0] = 0.0; f[0][k][1] =  0.0; f[0][k][2] = -1.0;
    f[k][0][0] = 0.0; f[k][0][1] = -1.0; f[k][0][2] = 0.0;
    f[k][k][0] = 1.0; f[k][k][1] = 0.0; f[k][k][2] = 0.0;
  }
  { /* initialize triangle */
    EZ_Triangle *tmp,*c = NULL;
    for(i=0; i<4; i++)
      {
	New_triangle(tmp);
	if(!tmp) EZ_OutOfMemory("gen_sphere_triangle_data");
	if(i == 0) {T = tmp;} else { c->next = tmp;}
	c = tmp;
	c->f = (t->f)[i];
	(c->v1)[0] = 0; (c->v1)[1] = 0;
	(c->v2)[0] = k; (c->v2)[1] = 0;
	(c->v3)[0] = 0; (c->v3)[1] = k;
	New_triangle(tmp);
	if(!tmp) EZ_OutOfMemory("gen_sphere_triangle_data");
	c->next = tmp; c=tmp;
	c->f = (t->f)[i];
	(c->v1)[0] = 0; (c->v1)[1] = k;	
	(c->v2)[0] = k; (c->v2)[1] = 0;
	(c->v3)[0] = k; (c->v3)[1] = k;
      }
  }
  { /* do the subdivision */
    for(i = 1; i < n; i++)
      subdivide_triangle(T);
    destroy_triangle(T); 
  }
}
static void destroy_triangle(t)
     EZ_Triangle *t;
{
  EZ_Triangle *next, *down;
  
  if(t == (EZ_Triangle *)0 ) return;
  next = t->next;
  down = t->down;
  (void)my_free( (char *) t);
  destroy_triangle(down);
  destroy_triangle(next);
}

static void subdivide_triangle(t)
     EZ_Triangle *t;
{
  EZ_Triangle *next, *down, *tmp, *c;
  int v12[2],v13[2],v23[2];
  
  if(t == (EZ_Triangle *)0 ) return;
  next = t->next;
  down = t->down;
  v12[0] = (t->v1[0] + t->v2[0])*0.5;
  v12[1] = (t->v1[1] + t->v2[1])*0.5;
  v13[0] = (t->v1[0] + t->v3[0])*0.5;
  v13[1] = (t->v1[1] + t->v3[1])*0.5;
  v23[0] = (t->v2[0] + t->v3[0])*0.5;
  v23[1] = (t->v2[1] + t->v3[1])*0.5;
  {
    float x[3],norm;
    EZ_Vector3 **f;
    int i; 

    f = (t->f);
    for(i = 0; i < 3; i++)
      x[i] = f[t->v1[0]][t->v1[1]][i] + f[t->v2[0]][t->v2[1]][i];
    norm = (float)sqrt((double)(x[0]*x[0]+x[1]*x[1]+x[2]*x[2])); 
    for(i=0; i < 3; i++)   f[v12[0]][v12[1]][i] = x[i]/norm;

    for(i = 0; i < 3; i++)
      x[i] = f[t->v1[0]][t->v1[1]][i] + f[t->v3[0]][t->v3[1]][i];
    norm = (float)sqrt((double)(x[0]*x[0]+x[1]*x[1]+x[2]*x[2]));
    for(i=0; i < 3; i++)   f[v13[0]][v13[1]][i] = x[i]/norm;

    for(i = 0; i < 3; i++)
      x[i] = f[t->v3[0]][t->v3[1]][i] + f[t->v2[0]][t->v2[1]][i];
    norm = (float)sqrt((double)(x[0]*x[0]+x[1]*x[1]+x[2]*x[2]));
    for(i=0; i < 3; i++)   f[v23[0]][v23[1]][i] = x[i]/norm;
  }
  {
    New_triangle(c);
    c->down = down; t->down = c; c->f = t->f;
    (c->v1)[0] = v13[0]; (c->v1)[1] = v13[1];
    (c->v2)[0] = v12[0]; (c->v2)[1] = v12[1];
    (c->v3)[0] = v23[0]; (c->v3)[1] = v23[1];    
    New_triangle(tmp);
    c->next = tmp; c = tmp;  c->f = t->f;
    (c->v1)[0] = v12[0]; (c->v1)[1] = v12[1];
    (c->v2)[0] = (t->v2)[0]; (c->v2)[1] = (t->v2)[1];
    (c->v3)[0] = v23[0]; (c->v3)[1] = v23[1];    
    New_triangle(tmp);
    c->next = tmp; c = tmp; c->f = t->f;
    (c->v1)[0] = v13[0]; (c->v1)[1] = v13[1];
    (c->v2)[0] = v23[0]; (c->v2)[1] = v23[1];
    (c->v3)[0] = (t->v3)[0]; (c->v3)[1] = (t->v3)[1];    

    (t->v2)[0] = v12[0];  (t->v2)[1] = v12[1]; 
    (t->v3)[0] = v13[0];  (t->v3)[1] = v13[1]; 
  }
  subdivide_triangle(down);
  subdivide_triangle(next);
}
/************************************************************************/

static void gen_sphere_cube_data(n)
     int n;
{
  register int i,j, vcount;
  register float tnorm, tx, ty, tz, nx, ny, nz, dt;
  EZ_Vector3 ***fptr;
  EZ_SphereData *t;

  n = (n < 1 ? 4: (n > 8 ? 8: n));
  if( (sphere_cube & (1<<(n-1))) ) return;
  sphere_cube |= (1<<(n-1));
  
  /*
   * allocate space
   */
  {
    vcount = (1<< (n-1)) +1;
    t = (EZ_SphereData *)my_malloc( sizeof(EZ_SphereData),_SPHERE_CUBE_);
    if(!t) EZ_OutOfMemory("gen_sphere_cube_data");
    t->nv = vcount;
    sphere_cube_data[n-1] = t;
    fptr = t->f;
    for(i = 0; i < 6; i++)
      {
	fptr[i] = (EZ_Vector3 **)my_malloc( vcount * sizeof(EZ_Vector3 *),_SPHERE_TRIANGLE_);
	if(!fptr[i]) EZ_OutOfMemory("gen_sphere_cube_data");
	for(j=0; j < vcount; j++)
	  {
	    fptr[i][j] = (EZ_Vector3 *)my_malloc( vcount * sizeof(EZ_Vector3),_SPHERE_TRIANGLE_);
	    if(!(fptr[i][j])) EZ_OutOfMemory("gen_sphere_cube_data");
	  }
      }
  }
  /*
   * generate data. 
   */
  dt = 2.0 / ( (float)( vcount - 1));

  for(i = 0; i < vcount; i++)
    for(j = 0; j < vcount; j++)
      {
	tx = 1.0;
	ty = -1.0 + dt * (float)j;
	tz = -1.0 + dt * (float)i;  
	tnorm = (float)sqrt((double)(tx * tx + ty * ty +tz * tz));
	nx = tx/tnorm; ny = ty/tnorm; nz = tz/tnorm; 
	fptr[0][i][j][0] = nx;	fptr[0][i][j][1] = ny; fptr[0][i][j][2] = nz;
	fptr[1][i][j][0] = -nx; fptr[1][i][j][1] = -ny; fptr[1][i][j][2] = nz;
	fptr[2][i][j][0] = -ny;  fptr[2][i][j][1] = nx; fptr[2][i][j][2] = nz; 
	fptr[3][i][j][0] = ny; 	fptr[3][i][j][1] = -nx; fptr[3][i][j][2] = nz;
	fptr[4][i][j][0] = -nz; fptr[4][i][j][1] = ny;  fptr[4][i][j][2] = nx;
	fptr[5][i][j][0] = nz; fptr[5][i][j][1] = ny; fptr[5][i][j][2] = -nx;
      }
}
/****************************************************************************/
#undef _EZ_SPHERE_C_
