/* patch.c,v 1.1.1.1 1995/02/27 07:38:34 explorer Exp */

/*
 * Copyright (C) 1994, Tolulope Okusanya.
 * All rights reserved.
 *
 * 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 "geom.h"
#include "patch.h"

MATRIX beziermatrix = {
  { -1,  3, -3, 1 },
  {  3, -6,  3, 0 },
  { -3,  3,  0, 0 },
  {  1,  0,  0, 0 }
};

MATRIX cardinalmatrix = {
  { -0.5,  1.5, -1.5,  0.5 },
  {  1.0, -2.5,  2.0, -0.5 },
  { -0.5,  0.0,  0.5,  0.0 },
  {  0.0,  1.0,  0.0,  0.0 }
};

MATRIX bsplinematrix = {
  { -1.0/6.0,  3.0/6.0, -3.0/6.0, 1.0/6.0 },
  {  3.0/6.0, -6.0/6.0,  3.0/6.0,     0.0 },
  { -3.0/6.0,      0.0,  3.0/6.0,     0.0 },
  {  1.0/6.0,  4.0/6.0,  1.0/6.0,     0.0 }
 };

MATRIX hermitematrix = {
  {  2,  -2,  1,  1 },
  { -3,   3, -2, -1 },
  {  0,   0,  1,  0 },
  {  1,   0,  0,  0 }
};

static Vector samples[MAXDIV +1][MAXDIV +1];

static Methods *iPatchMethods = NULL;
static char patchName[] = "patch";

unsigned long PatchTests, PatchHits;

/*****************************************************************************/
/*****************************************************************************/

GeomRef
PatchCreate(type, geomx, geomy, geomz, div)
     int type, div;
     MATRIX geomx, geomy, geomz;
{
  Patch *spatch;
  
  spatch = NewPatch();
  
  switch(type) {
    case BEZIER:
      DefPatch(spatch, beziermatrix, geomx, geomy, geomz, div);
      break;
    case CARDINAL:
      DefPatch(spatch, cardinalmatrix, geomx, geomy, geomz, div);
      break;
    case BSPLINE:
      DefPatch(spatch, bsplinematrix, geomx, geomy, geomz, div);
      break;
    case HERMITE:
      DefPatch(spatch, hermitematrix, geomx, geomy, geomz, div);
      break;
    default:
      RLerror(RL_ABORT,"Selected basis matrix unknown\n");
    }

  return (GeomRef)spatch;
}

Methods *
PatchMethods()
{
  if(iPatchMethods == (Methods *)NULL) {
    iPatchMethods = MethodsCreate();
    iPatchMethods->name = PatchName;
#if 0
    iPatchMethods->create = (GeomCreateFunc *)PatchCreate;
#endif
    iPatchMethods->methods = PatchMethods;
    iPatchMethods->intersect = PatchIntersect;
    iPatchMethods->normal = PatchNormal;
    iPatchMethods->uv = PatchUV;
    iPatchMethods->bounds = PatchBounds;
    iPatchMethods->stats = PatchStats;
    iPatchMethods->checkbounds = TRUE;
    iPatchMethods->closed = FALSE;
  }
  return iPatchMethods;
}

int
PatchIntersect(ref, ray, hl, mindist, maxdist)
     GeomRef ref;
     Ray *ray;
     HitList *hl;  /* unused here */
     Float mindist, *maxdist;
{
  Patch *spatch = (Patch *)ref;
  Float dist;
  Isectrec irec;
  
  PatchTests++;
  
  irec = IsectPatch(spatch,ray);
  dist = irec.t;

  if(dist != -1.0) {
    if((dist < *maxdist) && (dist > mindist)) {
      *maxdist = dist;
      PatchHits++;
      
      return TRUE;
    }
  }
  
  return FALSE;
}

int
PatchNormal(ref, pos, nrm, gnrm)
     GeomRef ref;
     Vector *pos, *nrm, *gnrm;
{
  Patch *spatch = (Patch *)ref;
  Float u, v;
  Vec2d uv;
  
  PatchUV((GeomRef)spatch, pos, (Vector *)NULL, &uv, (Vector *)NULL,
	  (Vector *)NULL);
  
  u = uv.u;
  v = uv.v;
  
  *gnrm = NormalPatch(spatch, u, v);
  
  *nrm = *gnrm;
  return FALSE;
}

void
PatchUV(ref, pos, norm, uv, dpdu, dpdv)
     GeomRef ref;
     Vector *pos, *norm, *dpdu, *dpdv;
     Vec2d *uv;
{
  Patch *spatch = (Patch *)ref;
  Ray r;
  Vector delta;
  Isectrec irec;
  Float u,v;

  seednrand(123); 

  /* Easy way to do this is to set up problem as intersection between 
   * ray at pos - deltaP in direction deltaP and surface :-) 
   */
  

  delta.x = nrand()*10;
  delta.y = nrand()*10;
  delta.z = nrand()*10;
  
  r.pos.x = pos->x - delta.x;
  r.pos.y = pos->y - delta.y;
  r.pos.z = pos->z - delta.z;

  r.dir.x = delta.x;
  r.dir.y = delta.y;
  r.dir.z = delta.z;
  
  normalize(&(r.dir));

  irec = IsectPatch(spatch,&r);
  
  if(irec.t == -1.0) RLerror(RL_ABORT,"FATAL ERROR IN PatchUV\n");
  
  u = uv->u = irec.u;
  v = uv->v = irec.v;

  if(dpdu) {
    dpdu->x = eval_patch(u, spatch->Md, spatch->geomx, spatch->MT, v);
    dpdu->y = eval_patch(u, spatch->Md, spatch->geomy, spatch->MT, v);
    dpdu->z = eval_patch(u, spatch->Md, spatch->geomz, spatch->MT, v);
    (void)VecNormalize(dpdu);
  }
  
  if(dpdv) {
    dpdv->x = eval_patch(u, spatch->M, spatch->geomx, spatch->MdT, v);
    dpdv->y = eval_patch(u, spatch->M, spatch->geomy, spatch->MdT, v);
    dpdv->z = eval_patch(u, spatch->M, spatch->geomz, spatch->MdT, v);
    (void)VecNormalize(dpdv);
  }
}

void
PatchBounds(ref, bounds)
     GeomRef ref;
     Float bounds[2][3];
{
  Patch *spatch = (Patch *)ref;
  Vector Min, Max;
  Float xdiff, ydiff, zdiff, factor;
  
  Min = (spatch->tree)->box_min;
  Max = (spatch->tree)->box_max;
  
  xdiff = Max.x - Min.x;
  ydiff = Max.y - Min.y;
  zdiff = Max.z - Min.z;
  
  factor = 0.1;
  
  bounds[LOW][X]  = Min.x - factor*xdiff;
  bounds[HIGH][X] = Max.x + factor*xdiff;
  bounds[LOW][Y]  = Min.y - factor*ydiff;
  bounds[HIGH][Y] = Max.y + factor*ydiff;
  bounds[LOW][Z]  = Min.z - factor*zdiff;
  bounds[HIGH][Z] = Max.z + factor*zdiff;
}

char *
PatchName()
{
  return patchName;
}

void
PatchStats(tests, hits)
     unsigned long *tests, *hits;
{
  *tests = PatchTests;
  *hits = PatchHits;
}

void
PatchMethodRegister(meth)
     UserMethodType meth;
{
  if (iPatchMethods)
    iPatchMethods->user = meth;
}


/*****************************************************************************/
/*************************** NECESSARY PRECURSORS ****************************/

void
mmult4x4_4x4(a, b, c)
     MATRIX a, b, c;
{
  int i, j, k;
  MATRIX temp;
  
  for(i = 0; i < 4; i++)
    for (j = 0; j < 4; j++) {
      temp[i][j] = 0;
      for (k = 0; k < 4; k++) 
	temp[i][j] += (b[i][k] * c[k][j]);
    }
  
  for(i = 0; i < 4; i++)
    for (j = 0; j < 4; j++)
      a[i][j] = temp[i][j];
}

void
mmult1x4_4x1(a, b, c)
     MATRIX a;
     Float b[1][4], c[4][1];
{
  int k;
  Float temp;
  
  temp = 0;
  for (k = 0; k < 4; k++) 
    temp += (b[0][k] * c[k][0]);
  
  a[0][0] = temp;
}

void
mmult4x4_4x1(a, b, c)
     Float a[4][1], b[4][4], c[4][1];
{
  int i, k;
  Float temp[4][1];
  
  for (i = 0; i < 4; i++) {
    temp[i][0] = 0;
    for (k = 0; k < 4; k++) 
      temp[i][0] += (b[i][k] * c[k][0]);
  }
  
  for (i = 0; i < 4; i++) 
    a[i][0] = temp[i][0];
}

void
mmult1x4_4x4(a, b, c)
     Float a[1][4], b[1][4], c[4][4];
{
  int j, k;
  Float temp[1][4];
  
  for (j = 0; j < 4; j++) {
    temp[0][j] = 0;
    for (k = 0; k < 4; k++) 
      temp[0][j] += (b[0][k] * c[k][j]);
  }
  
  for (j = 0; j < 4; j++)
    a[0][j] = temp[0][j];
}

void
transpose(a,b)
     MATRIX a, b;
{
  int i, j;
  
  for (i = 0; i < 4 ; i++)
    for (j = 0; j < 4; j++)
      b[j][i] = a[i][j];
}
 
int
fcmp(a,b)
     Float a,b;
{
  if (fabs(a - b) < TOL)
    return 1; 
  return 0;
}

Float
length(v1, v2)
     Vector v1, v2;
{
  Float a, b, c;
  
  a = v1.x - v2.x;
  b = v1.y - v2.y;
  c = v1.z - v2.z;
  
  return sqrt(a*a + b*b + c*c);
}

void
normalize(vec)
     Vector *vec;
{
  Float mag;
  
  mag = sqrt(vec->x*vec->x + vec->y*vec->y + vec->z*vec->z);
  vec->x /= mag;
  vec->y /= mag;
  vec->z /= mag;
}

void
cross_product(c, a, b)    /* c = a X b */
     Vector a, b, *c;
{
  c->x = a.y*b.z - a.z*b.y;
  c->y = a.z*b.x - a.x*b.z;
  c->z = a.x*b.y - a.y*b.x;
}

Float
dot_product(a, b)
     Vector a, b;
{
  return a.x*b.x + a.y*b.y + a.z*b.z;
}

Float
vec_mag(a)
     Vector a;
{
  return(sqrt(dot_product(a,a)));
}

PatchPtr
NewPatch()
{
  return((PatchPtr) Malloc(sizeof(Patch)));
}

void
FreePatch(p)
     PatchPtr p;
{
  free(p);
}

void
DefPatch(p, M, geomx, geomy, geomz, div)
     PatchPtr p;                      /* Pointer to patch struct */
     MATRIX M, geomx, geomy, geomz;   /* Spline basis, geometry matrices */
     int div;                         /* Number of divisions for tree */
{
  int i, j;
  
  /* Copy basic information into patch struct */
  
  for (i = 0; i < 4; i++)
    for (j = 0; j < 4; j++) {
      p->M[i][j] = M[i][j];
      p->geomx[i][j] = geomx[i][j];
      p->geomy[i][j] = geomy[i][j];
      p->geomz[i][j] = geomz[i][j];
    }
  
  /* Compute derivative of basis matrix */
  
  p->Md[0][0] = p->Md[0][1] = p->Md[0][2] = p->Md[0][3] = 0.0;
  
  for (i = 1; i < 4; i++) 
    for (j = 0; j < 4; j++) 
      p->Md[i][j] = p->M[i-1][j] * (4-i); 
  
  /* Fill in transpose fields of patch struct */
  
  transpose(p->M , p->MT );
  transpose(p->Md, p->MdT);
  
  /* Compute intermediate blending matrices */
  
  mmult4x4_4x4(p->Mx, p->M, p->geomx);     /* blending matrix for x,y,z */
  mmult4x4_4x4(p->My, p->M, p->geomy);
  mmult4x4_4x4(p->Mz, p->M, p->geomz);
  mmult4x4_4x4(p->Mx, p->Mx, p->MT);
  mmult4x4_4x4(p->My, p->My, p->MT);
  mmult4x4_4x4(p->Mz, p->Mz, p->MT);
  
  mmult4x4_4x4(p->Mxdu, p->Md, p->geomx);  /* d/du matrix for x,y,z */
  mmult4x4_4x4(p->Mydu, p->Md, p->geomy);
  mmult4x4_4x4(p->Mzdu, p->Md, p->geomz);
  mmult4x4_4x4(p->Mxdu, p->Mxdu, p->MT);
  mmult4x4_4x4(p->Mydu, p->Mydu, p->MT);
  mmult4x4_4x4(p->Mzdu, p->Mzdu, p->MT);
  
  mmult4x4_4x4(p->Mxdv, p->M, p->geomx);   /* d/dv matrix for x,y,z */  
  mmult4x4_4x4(p->Mydv, p->M, p->geomy);
  mmult4x4_4x4(p->Mzdv, p->M, p->geomz);
  mmult4x4_4x4(p->Mxdv, p->Mxdv, p->MdT);
  mmult4x4_4x4(p->Mydv, p->Mydv, p->MdT);
  mmult4x4_4x4(p->Mzdv, p->Mzdv, p->MdT);
  
  samplepatch(p, div);
  p->tree = buildtree(0, div, 0, div, div);
  
}

void
samplepatch(p, div)
     PatchPtr p;
     int div;
{
  Float U[1][4], V[4][1];         /* u, v vectors */
  MATRIX C;                       /* sol matrix (only [0][0] used) */
  MATRIX M2x, M2y, M2z;           /* Intermediate results */
  Float u, v;                     /* Parameters */
  int i,j;
  
  for (i = 0; i <= div; i++) {
      u = (Float) i / div;
      U[0][3] = 1;
      U[0][2] = u;
      U[0][1] = u * u;
      U[0][0] = U[0][1] * u;
      
      mmult1x4_4x4 (M2x, U, p->Mx);
      mmult1x4_4x4 (M2y, U, p->My);
      mmult1x4_4x4 (M2z, U, p->Mz);
      
      for (j = 0; j <= div; j++) 
	{
	  v = (Float) j / div;
	  V[3][0] = 1;
	  V[2][0] = v;
	  V[1][0] = v * v;
	  V[0][0] = V[1][0] * v;
	  mmult1x4_4x1 (C, M2x, V);
	  samples[i][j].x = C[0][0];
	  mmult1x4_4x1 (C, M2y, V);
	  samples[i][j].y = C[0][0];
	  mmult1x4_4x1 (C, M2z, V);
	  samples[i][j].z = C[0][0];
	}
    }
}

TreePtr
buildtree(u_low, u_hi, v_low, v_hi, div)
     int u_low, u_hi, v_low, v_hi, div;
{
  int u_mid, v_mid, j;
  TreePtr curr_node;
  
  curr_node = (TreePtr) Malloc(sizeof(TreeNode));

  if ((u_low+1 == u_hi) || (v_low+1 == v_hi)) /* Found leaf */
    {         
      /* Compute midpoint */
      curr_node->u_mid = ((((Float) (u_hi - u_low)) / 2.0) + u_low) / div ; 
      curr_node->v_mid = ((((Float) (v_hi - v_low)) / 2.0) + v_low) / div ;
 
      /* Compute box */
      if (length(samples[u_low][v_low], samples[u_hi][v_hi]) >
          length(samples[u_hi][v_low], samples[u_low][v_hi]))
	{
	  curr_node->box_min.x = samples[u_low][v_low].x;
	  curr_node->box_max.x = samples[u_hi ][v_hi ].x;
	  curr_node->box_min.y = samples[u_low][v_low].y;
	  curr_node->box_max.y = samples[u_hi ][v_hi ].y;
	  curr_node->box_min.z = samples[u_low][v_low].z;
	  curr_node->box_max.z = samples[u_hi ][v_hi ].z;
	} 
      else
	{
	  curr_node->box_min.x = samples[u_hi ][v_low].x;
	  curr_node->box_max.x = samples[u_low][v_hi ].x;
	  curr_node->box_min.y = samples[u_hi ][v_low].y;
	  curr_node->box_max.y = samples[u_low][v_hi ].y;
	  curr_node->box_min.z = samples[u_hi ][v_low].z;
	  curr_node->box_max.z = samples[u_low][v_hi ].z;
	}

      for (j = 0; j < 4; j++) curr_node->child[j] = (TreePtr)NULL;
      
    } 
  else 
    {                                           /* Not leaf node */ 
      u_mid = (u_hi - u_low) / 2 + u_low; 
      v_mid = (v_hi - v_low) / 2 + v_low;
      
      curr_node->child[0] = buildtree(u_low, u_mid, v_low, v_mid, div); 
      curr_node->child[1] = buildtree(u_mid, u_hi, v_low, v_mid, div); 
      curr_node->child[2] = buildtree(u_low, u_mid, v_mid, v_hi, div);
      curr_node->child[3] = buildtree(u_mid, u_hi, v_mid, v_hi, div);

      /* Compute box */
      curr_node->box_min.x = 1e99;
      curr_node->box_max.x = -1e99;
      for (j = 0; j < 4; j++) 
	{
	  if (curr_node->child[j]->box_min.x < curr_node->box_min.x)
	    curr_node->box_min.x = curr_node->child[j]->box_min.x;
	  if (curr_node->child[j]->box_min.x > curr_node->box_max.x)
            curr_node->box_max.x = curr_node->child[j]->box_min.x;
	  if (curr_node->child[j]->box_max.x < curr_node->box_min.x)
            curr_node->box_min.x = curr_node->child[j]->box_max.x;
	  if (curr_node->child[j]->box_max.x > curr_node->box_max.x)
            curr_node->box_max.x = curr_node->child[j]->box_max.x;
	}

      curr_node->box_min.y = 1e99;
      curr_node->box_max.y = -1e99;
      for (j = 0; j < 4; j++) 
	{
	  if (curr_node->child[j]->box_min.y < curr_node->box_min.y)
            curr_node->box_min.y = curr_node->child[j]->box_min.y;
	  if (curr_node->child[j]->box_min.y > curr_node->box_max.y)
            curr_node->box_max.y = curr_node->child[j]->box_min.y;
	  if (curr_node->child[j]->box_max.y < curr_node->box_min.y)
            curr_node->box_min.y = curr_node->child[j]->box_max.y;
	  if (curr_node->child[j]->box_max.y > curr_node->box_max.y)
            curr_node->box_max.y = curr_node->child[j]->box_max.y;
	}

      curr_node->box_min.z = 1e99;
      curr_node->box_max.z = -1e99;
      for (j = 0; j < 4; j++) 
	{
	  if (curr_node->child[j]->box_min.z < curr_node->box_min.z)
            curr_node->box_min.z = curr_node->child[j]->box_min.z;
	  if (curr_node->child[j]->box_min.z > curr_node->box_max.z)
            curr_node->box_max.z = curr_node->child[j]->box_min.z;
	  if (curr_node->child[j]->box_max.z < curr_node->box_min.z)
            curr_node->box_min.z = curr_node->child[j]->box_max.z;
	  if (curr_node->child[j]->box_max.z > curr_node->box_max.z)
            curr_node->box_max.z = curr_node->child[j]->box_max.z;
	}
    }

   return curr_node;
}

Float
PatchBoxIntersect(r, b1, b2) 
     Ray r;
     Vector b1, b2;
{
  Float t1, t2, tswap, tnear, tfar, dinv;
  
  /*  Check X slabs */
  if (fcmp (r.dir.x, 0.0)) /* Ray is parallel to X planes of box */
    {  
      if (!(((b1.x <= r.pos.x ) && (r.pos.x <= b2.x)) ||
            ((b2.x <= r.pos.x ) && (r.pos.x <= b1.x)))) /* But not between */
	return -1.0;
      tfar = INF;
      tnear = -INF;
    } 
  else 
    {
      dinv = 1.0/r.dir.x;         /* Calculate inverse for speed */
      t1 = (b1.x - r.pos.x) * dinv;
      t2 = (b2.x - r.pos.x) * dinv;
      
      if (t1 > t2) { tfar = t1; tnear = t2; } 
      else         { tfar = t2; tnear = t1; } 
      if (tfar < 0.0)              /* Box is behind ray */
	return -1.0;
    }
  
  /* Check Y slabs */
  
  if (fcmp (r.dir.y, 0.0)) /* Ray is parallel to Y planes of box */
    {  
      if (!(((b1.y <= r.pos.y ) && (r.pos.y <= b2.y)) ||
	    ((b2.y <= r.pos.y ) && (r.pos.y <= b1.y))))  /* But not between */
	return -1.0;
    } 
  else
    {
      dinv = 1.0/r.dir.y;         /* Calculate inverse for speed */
      t1 = (b1.y - r.pos.y) * dinv;
      t2 = (b2.y - r.pos.y) * dinv;
      
      if (t1 > t2) { tswap = t1; t1 = t2; t2 = tswap; }
      
      if (t1 > tnear) tnear = t1;
      if (t2 < tfar)  tfar = t2;
      
      if (tnear > tfar)            /* Box is missed */
	return -1.0;
      
      if (tfar < 0.0)              /* Box is behind ray */
	return -1.0;     
    }
  
  /* Check Z slabs */
  
  if (fcmp (r.dir.z, 0.0))    /* Ray is parallel to Z planes of box */
    {
      if (!(((b1.z <= r.pos.z ) && (r.pos.z <= b2.z)) ||
	    ((b2.z <= r.pos.z ) && (r.pos.z <= b1.z))))  /* But not between */
	return -1.0;
    }
  else
    {
      dinv = 1.0/r.dir.z;         /* Calculate inverse for speed */
      t1 = (b1.z - r.pos.z) * dinv;
      t2 = (b2.z - r.pos.z) * dinv;
      
      if (t1 > t2) { tswap = t1; t1 = t2; t2 = tswap; }
      
      if (t1 > tnear) tnear = t1;
      if (t2 < tfar)  tfar = t2;
      
      if (tnear > tfar)            /* Box is missed */
	return -1.0;
      
      if (tfar < 0.0)              /* Box is behind ray */
	return -1.0;     
    } 
  return tnear;
} 

/***********************************************************************
* Create a intersection list node.  Put it in the list based on the
*  parameter t.  Return the new head of the list
*  Pass: list ptr, node to insert, t
************************************************************************/

ListPtr insert_node(list, node, t)
     ListPtr list;
     TreePtr node;
     Float t;
{
  ListPtr new, this, back;
  
  new = (ListPtr) Malloc(sizeof(ListNode));
  
  new->t = t;
  new->node = node;
  new->next = NULL;
  
  if (!list) 
    return new;
  
  back = NULL;
  this = list;
  while (this && (this->t < new->t)) 
    {
      back = this;
      this = this->next;
    }
  if (!back)
    {                       /* First item */
      new->next = list;
      return new;
    } 
  else
    {                             /* Second through last item */
      back->next = new;
      new->next  = this;
      return list;
    }
}   
   
void
free_list(list) 
     ListPtr list;
{
  ListPtr this;
  
  this = list;
  while (this) 
    {
      list = this->next;
      free(this);
      this = list;
    }
}
 
Float
eval_patch(u, M, G, MT, v)
     Float u, v;
     MATRIX M, G, MT;
{
  Float U[1][4], V[4][1];              /* u, v vectors */
  MATRIX M2;                           /* Intermediate results */
  
  U[0][3] = 1;
  U[0][2] = u;
  U[0][1] = u * u;
  U[0][0] = U[0][1] * u;
  V[3][0] = 1;
  V[2][0] = v;
  V[1][0] = v * v;
  V[0][0] = V[1][0] * v;
  
  mmult1x4_4x4 (M2, U, M);
  mmult1x4_4x4 (M2, M2, G);
  mmult1x4_4x4 (M2, M2, MT);
  mmult1x4_4x1 (M2, M2, V); 
  return M2[0][0];
}

int
IsParallel(a,b)
     Vector a,b;
{
  normalize(&a);
  normalize(&b);
  
  if(fcmp(a.x,b.x) && fcmp(a.y,b.y) && fcmp(a.z,b.z)) return 1;
  
  return 0;
}

void
PatchNewton(surface, node, r, irec)
     PatchPtr surface;
     TreePtr node;
     Ray r;
     Isectrec *irec;
{
   int iterations, success, failure, i, j;
   Float u, v, error, last_error, e1, e2;
   Float plane1d, plane2d, E1, E2, det;
   Vector plane1abc, plane2abc, temp; 
   MATRIX H1, H2;
   Float dfdu, dfdv, dgdu, dgdv;

   /* get initial values for u and v from tree node */
   u = node->u_mid;
   v = node->v_mid;
 
   /* calculate plane1, plane2 */

   cross_product(&plane1abc, r.pos, r.dir);   
   normalize(&plane1abc);
   cross_product(&plane2abc, plane1abc, r.dir);
   normalize(&plane2abc);

   plane1d = dot_product(plane1abc, r.pos);
   plane2d = dot_product(plane2abc, r.pos); 

   /* calculate the H matrices */
   for (i=0; i<4; i++) 
     {
       for (j=0; j<4; j++) 
	 {
	   temp.x = surface->geomx[i][j];
	   temp.y = surface->geomy[i][j];
	   temp.z = surface->geomz[i][j];
	   H1[i][j] = dot_product(plane1abc, temp);
	   H2[i][j] = dot_product(plane2abc, temp); 
	 }
     }

   /* begin iteration */

   error = 0;
   iterations = 0;
   success = failure = FALSE;
   while (!success && !failure) 
     {
       E1 = eval_patch(u, surface->M, H1, surface->MT, v) - plane1d; 
       E2 = eval_patch(u, surface->M, H2, surface->MT, v) - plane2d; 
       last_error = error;
       e1 = ABS(E1);
       e2 = ABS(E2);
       error = e1 + e2;
       
       if (error < NEWTON_TOL) 
         success = TRUE;
       else if ((iterations >= MAX_ITER) && (error >= last_error)) 
         failure = TRUE;
       else                 /****** calc newton step ***  f = E1, g = E2 **/ 
	 {
	   dfdu = eval_patch(u, surface->Md, H1, surface->MT, v);
	   dfdv = eval_patch(u, surface->M, H1, surface->MdT, v);
	   dgdu = eval_patch(u, surface->Md, H2, surface->MT, v);
	   dgdv = eval_patch(u, surface->M, H2, surface->MdT, v);
	   
	   det = dfdu*dgdv-dfdv*dgdu; /* determinant */
	   
	   if(det == 0.0)
	     {
	       u += 0.00001;
	       v += 0.00001;
	     }
	   else
	     {
	       u += (E2*dfdv-E1*dgdv)/det;  /* Delta u */
	       v += (E2*dfdu-E1*dgdu)/-det;  /* Delta v */
	     }
	   
	   iterations++; 
	 }
     }
   
   /* Must lie in proper range */

   if ((u > 1.0) || (u < 0.0) || (v > 1.0) || (v < 0.0))
     success = FALSE;

   if(success)
     {
       irec->isect.x = eval_patch(u, surface->M, surface->geomx, surface->MT, v); 
       irec->isect.y = eval_patch(u, surface->M, surface->geomy, surface->MT, v);   
       irec->isect.z = eval_patch(u, surface->M, surface->geomz, surface->MT, v);  
       irec->t = length (r.pos, irec->isect);
       irec->u = u;
       irec->v = v;
     }
   else irec->t = -1.0;
}

Isectrec IsectPatch(surface, ray)
     PatchPtr surface;
     Ray *ray;
{
   int i;
   Float tmin, t;
   ListPtr this, head; 
   ListNode thisnode;
   Isectrec isect, isectmin;
   Ray r;

   r = *ray;
   
   head = insert_node(NULL, surface->tree, 0); 
   tmin = INF;
   
   isectmin.t = -1.0;              /* Use -1.0 to indicate no intersection */

   while (head) 
     {
       this = head;
       head = this->next;
       thisnode = *this;
       free(this);
       if(thisnode.node->child[0] == (TreePtr)NULL) /* node is a leaf node */
	 {     
	   PatchNewton(surface, thisnode.node, r, &isect); 

	   if ((isect.t != -1.0) && (isect.t < tmin))
	     {
	       tmin = isect.t;    
	       isectmin = isect;
	     }
	 } 
       else
	 {
	   for (i = 0; i < 4; i++) /* for each child of node */
	     {               
	       t = PatchBoxIntersect(r, thisnode.node->child[i]->box_min,
				thisnode.node->child[i]->box_max);
	       if ( t != -1.0)           /* if ray intersects box */
		 head = insert_node(head, thisnode.node->child[i], t);
	     }
	 }

       if ((head) && (tmin < head->t)) 
	 { 
	   free_list(head); 
	   head = NULL; 
	 } 
     }

   return isectmin;
}   
      
Vector NormalPatch(p, u, v)
     PatchPtr p;
     Float u, v;
{
  Vector ddu, ddv, norm;
  
  ddu.x = eval_patch(u, p->Md, p->geomx, p->MT, v);
  ddu.y = eval_patch(u, p->Md, p->geomy, p->MT, v);
  ddu.z = eval_patch(u, p->Md, p->geomz, p->MT, v);
  
  ddv.x = eval_patch(u, p->M, p->geomx, p->MdT, v);
  ddv.y = eval_patch(u, p->M, p->geomy, p->MdT, v);
  ddv.z = eval_patch(u, p->M, p->geomz, p->MdT, v);
  
  cross_product(&norm, ddu, ddv);
  normalize(&norm); 
  return norm;
}

