/* fl.c,v 1.1.1.1 1995/02/27 07:38:31 explorer Exp */

/*
 *	fracland: a fractal landscape primitive for rayshade.
 *
 * 	Copyright (C) 1992, Lawrence K. Coffin.
 *	Parts Copyright (C) 1989, 1991, Craig E. Kolb.
 *	All rights reserved.
 * 
 *	Parts of this program are from "Numerical Recipes in C" by William H.
 *	Press, Brian P. Flannery, Saul A. Teukolsky, and William T. Vetterling.
 *
 *	This software may be freely copied, modified, and redistributed
 *	provided that this copyright notice is preserved on all copies.
 *
 *	You may not distribute this software, in whole or in part, as part of
 *	any commercial product without the express consent of the authors.
 * 
 *	There is no warranty or other guarantee of fitness of this software
 *	for any purpose.  It is provided solely "as is".
 *
 */

#include "libcommon/common.h"
#include "geom.h"
#include "hf.h"
#include "fl.h"
#include <math.h>
#if defined(AMIGA) && defined(__GNUC__)
#define pow mypow
extern double mypow _PROTO((const double x, const double y));
#endif
#include <stdlib.h>

static Methods *iFlMethods = NULL;

static float rv _PROTO((void));

extern unsigned long HFTests, HFHits;

GeomRef
FlCreate(seed, subdiv)
     long seed;
     int subdiv;
{
  Hf *hf;
  float val, *maxptr, *minptr;
  int i, j, n;
  int in,i2,x,y;
  float rc, temp;
  
  seednrand(seed);
  
  hf = (Hf *)Malloc(sizeof(Hf));

  /*
   * Make the following an option someday.
   */
  hf->BestSize = BESTSIZE;

  /*
   * Store the inverse for faster computation.
   */
  hf->iBestSize = 1. / (float)hf->BestSize;

  /*
   * Get HF size.
   */
  n = pow((double)2.0,(double)subdiv);
  hf->size = n+1;
  
  hf->data = (float **)share_malloc(hf->size * sizeof(float *));
  
  for(i = 0; i < hf->size; i++){
    hf->data[i] = (float *)share_malloc(hf->size * sizeof(float));
  }
  
  
  hf->data[0][0] = 0;
  hf->data[0][n] = 0;
  hf->data[n][0] = 0;
  hf->data[n][n] = 0;
  for (i=0; i<subdiv; i++){
    in = (double)n/pow((double)2.0,(double)i);
    i2 = in/2;
    rc = (double)1.0/pow((double)2.0,(double)i);
    for (x=0; x<n; x+=in){
      for (y=0; y<n; y+=in){
	hf->data[x+i2][y   ] =
	  (hf->data[x   ][y   ] + hf->data[x+in][y   ])/2 + rv()*rc;
	hf->data[x   ][y+i2] =
	  (hf->data[x   ][y   ] + hf->data[x   ][y+in])/2 + rv()*rc;
	hf->data[x+in][y+i2] =
	  (hf->data[x+in][y   ] + hf->data[x+in][y+in])/2 + rv()*rc;
	hf->data[x+i2][y+in] =
	  (hf->data[x   ][y+in] + hf->data[x+in][y+in])/2 + rv()*rc;
	hf->data[x+i2][y+i2] =
	  (hf->data[x][y] + hf->data[x+in][y] + hf->data[x][y+in] 
	   + hf->data[x+in][y+in])/4 + rv()*rc;
      }
    }
  }
  /*
   *  This flips the points around so the surfaces created here are the same
   *	as the surfaces created with the program "fracland".
   */
  for (x = 0; x < hf->size; x++){
    for (y = x; y < hf->size; y++){
      temp = hf->data[x][y];
      hf->data[x][y] = hf->data[y][x];
      hf->data[y][x] = temp;
    }
  }
  
  for (i = 0; i < hf->size; i++) {
    for (j = 0; j < hf->size; j++) {
      val = hf->data[i][j];
      if (val <= HF_UNSET) {
	hf->data[i][j] = HF_UNSET;
	/*
	 * Don't include the point in min/max
	 * calculations.
	 */
	continue;
      }
      if (val > hf->maxz)
	hf->maxz = val;
      if (val < hf->minz)
	hf->minz = val;
    }
  }
  
  /*
   * Allocate levels of grid.  hf->levels = log base BestSize of hf->size
   */
  for (i = hf->size, hf->levels = 0; i > hf->BestSize; i /= hf->BestSize,
       hf->levels++)
    ;
  hf->levels++;
  hf->qsize = CACHESIZE;
  hf->q = (hfTri **)Calloc((unsigned)hf->qsize, sizeof(hfTri *));
  hf->qtail = 0;
  
  hf->lsize = (int *)share_malloc(hf->levels * sizeof(int));
  hf->spacing = (float *)share_malloc(hf->levels * sizeof(float));
  hf->boundsmax = (float ***)share_malloc(hf->levels * sizeof(float **));
  hf->boundsmin = (float ***)share_malloc(hf->levels * sizeof(float **));
  
  hf->spacing[0] = hf->size -1;
  hf->lsize[0] = (int)hf->spacing[0];
  hf->boundsmax[0] = (float **)share_malloc(hf->lsize[0]*sizeof(float *));
  hf->boundsmin[0] = (float **)share_malloc(hf->lsize[0]*sizeof(float *));
  /*
   * Compute initial bounding boxes
   */
  for (i = 0; i < hf->lsize[0]; i++) {
    hf->boundsmax[0][i]=(float *)share_malloc(hf->lsize[0]*sizeof(float));
    hf->boundsmin[0][i]=(float *)share_malloc(hf->lsize[0]*sizeof(float));
    maxptr = hf->boundsmax[0][i];
    minptr = hf->boundsmin[0][i];
    for (j = 0; j < hf->lsize[0]; j++) {
      *maxptr++ = maxalt(i, j, hf->data) + EPSILON;
      *minptr++ = minalt(i, j, hf->data) - EPSILON;
    }
  }
  
  for (i = 1; i < hf->levels; i++) {
    hf->spacing[i] = hf->spacing[i-1] * hf->iBestSize;
    hf->lsize[i] = (int)hf->spacing[i];
    if ((Float)hf->lsize[i] != hf->spacing[i])
      hf->lsize[i]++;
    hf->boundsmax[i]=(float **)share_malloc(hf->lsize[i]*sizeof(float *));
    hf->boundsmin[i]=(float **)share_malloc(hf->lsize[i]*sizeof(float *));
    for (j = 0; j < hf->lsize[i]; j++) {
      hf->boundsmax[i][j] = (float *)share_malloc(hf->lsize[i] *
						  sizeof(float));
      hf->boundsmin[i][j] = (float *)share_malloc(hf->lsize[i] *
						  sizeof(float));
    }
    integrate_grid(hf, i);
  }
  
  hf->boundbox[LOW][X] = hf->boundbox[LOW][Y] = 0.0;
  hf->boundbox[HIGH][X] = hf->boundbox[HIGH][Y] = 1.0;
  hf->boundbox[LOW][Z] = hf->minz;
  hf->boundbox[HIGH][Z] = hf->maxz;
  
  return (GeomRef)hf;
}
/*
 * Generate a random value from -0.5 to 0.5
 */

static float
rv()
{
  float y;
  
  y = (float)nrand() - 0.5;
  return (y);
}

Methods *FlMethods()
{
  if (iFlMethods == (Methods *)NULL) {
    iFlMethods = MethodsCreate();
#if 0
    iFlMethods->create = (GeomCreateFunc *)FlCreate;
#endif
    iFlMethods->methods = FlMethods;
    iFlMethods->name = HfName;
    iFlMethods->intersect = HfIntersect;
    iFlMethods->normal = HfNormal;
    iFlMethods->uv = HfUV;
    iFlMethods->bounds = HfBounds;
    iFlMethods->stats = HfStats;
    iFlMethods->checkbounds = TRUE;
    iFlMethods->closed = FALSE;
  }
  
  return iFlMethods;
}
