/* sweptsph.c,v 1.1.1.1 1995/02/27 07:38:36 explorer Exp */

/*
 *	Copyright (C) 1992, 1993 by 
 *			    Lawrence K. Coffin, Craig Colb, Mark Podlipec
 *	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".
 *
 */

/* History
 *  1.6  09Sept93 Larry Coffin
 *       Added checking for end spheres to discard any intersections with
 *       the half of the sphere that is inside the swept tube.  The swept
 *       tube now does not appear to match up with the half sphere that
 *       results.  I'm pretty sure it's not the new test resulting in less
 *       than half the sphere, but rather something wrong with the actual
 *       swept tube intersection portion of the code.
 *  1.5  20Jul93 Mark Podlipec
 *	 Added UV funtion. v vector is length along parametric curve u.
 *       u vector is along girth. 
 *  1.4  02Jul93 Mark Podlipec
 *       shadow rays still a problem. just for the heck of it, eliminate
 *       t = f1'/f0' routine and for each u root found do a sphere/ray
 *       intersection test. WOW! no errors. 
 *  1.3  02Jul93 Mark Podlipec
 *       Problem is now with shadow rays fired from surface of sweptsph.
 *       1e-04 isn't good enough. one failing case found a root 3.29e-04.
 *       from the surface. I'm going to try 7.5e-04.
 *  1.2  25Jun93 Mark Podlipec
 *       do the same for when t = f0' is < EPSILON.
 *  1.1  25Jun93 Mark Podlipec
 *       roots of f1' are causing div to be zero. Now when div is < EPSILON
 *       find ray intersection with sphere centered at u = root on parametric
 *       curve. Also radius at u.
 */

/*
 *	This primative is from "Ray Tracing Objects Defined by Sweeping a
 * Sphere" by J.J. van Wijk in Eurographics '84, pg 73.  It is defined by
 * a sphere of variying radius that is swept or extruded through space along a
 * parametricly defined curve.  The equations for the x,y,z components of the
 * curve and the radius of the sphere are defined by third order equations.
 * This allows for the curve to be defined as the actual equation, as a bezier
 * curve, and perhaps several other curves such as splines. 
 *
 *  The equation for the primitive is the parametric equation:
 *
 *  f(xyz,u) = |xyz - c(u)|^2 - r(u)^2 -- where xyz = {x,y,z} and
 *	c(u) = {x(u),y(u),z(u)}  for umin <= u <= umax.
 *	
 *
 *  Substituting the parametric form of a ray (xyz = Vt + P) where P is the
 *  ray position (ray->pos), and V is the ray direction (ray->dir) we get an
 *  equation
 *
 *  g(u,t) = f2 * t^2 + f1 * t^1 + f0
 *
 *  Where:
 *	f2 = |V|^2 = dotp<ray->dir,ray->dir> = 1 since the ray direction is 
 *		a unit vector.
 *
 *	f1 = 2*dotp<(P - c(u)),V)>
 *
 *	f0 = |P - c(u)|^2 - r(u)^2
 *
 *  Differentiating g(u,t) = 0 with respect to u gives us:
 *
 *	2*f2*t*t' + f1'*t + f1*t' + f0' = 0
 *
 *  Setting t' = 0 gives us:
 *
 *	f1'*t + f0' = 0
 *
 *  Which gives us t = -f1'/f0'
 *
 *  Substituting this back into g(u,t) gives us
 *
 *  g(u) = f2*f0'*f0' - f1*f1'*f0' + f1'*f1'*f0
 *
 *  working this out we get an equation in u of 2*(2*n -1) degree when starting
 *  with center or radius equations of nth degree (10th degree for 3rd degree
 *  center and radius equations).
 *
 *  After solving for u = 0, we can find t from t = -f1'/f0' and keep
 *  the shortest t as our distance of closest intersection.  We must also check
 *  the case where u = umin and u = umax to see if we intersect the sphere at
 *  either end.
 *
 */


#include "libcommon/common.h"
#include "geom.h"
#include "sweptsph.h"

#define SWEPT_EPS 1.0e-4
#define BIGNUM 9.9e6  /* used as the min and max values for the root finding
			intervals */
#define SMALLNUM .1e-6 /* a number close enough to zero */
#define	DBL_EPS	2.2204460492503131e-10 /* larger than the accuracy of
							the machine's Floats */ 
#define COUNT 8	/* maximum number of iterations for the Newton's method
				root finder */ /* 8 best for linear start */
#define COUNT2 200

#define sign(x) ((x) < 0 ? (-1) : (1))

/* flags for which end Inside() is checking */
#define T_ZERO 0
#define T_ONE 1

static int Inside _PROTO((SweptSph *, Ray *, Float, int));

static Methods *iSweptSphMethods = NULL;
static char sweptsphName[] = "sweptsph";

unsigned long SweptSphTests, SweptSphHits, maxcount;

/*
 * This holds the value for u for the most recent hit
 * This is neccecary inorder to calculate the normal
 */
Float SweptSphRoot;

/*
 * after a normal is called, SweptSphRoot1 is passed
 * to here POD
 */
Float SweptSphRoot1;

int steps[20];
unsigned long int bichecks, bitests, newtons, newchecks;

/* error file for debugging */
FILE *sweptspherror;

/* vector for debugging -- {1/sqrt(3), 1/sqrt(3), 1/sqrt(3)} */
Vector one = {
  0.57735026918962576451,
  0.57735026918962576451,
  0.57735026918962576451
};

/*
 *  SweptSphCreate expects two prameters: the equations for the center and the
 *  equation for the radius.  The center equations are passed as an array of
 *  four vectors.  The center is defined by parametric equations such that
 *
 *  x(u) = cent[0].x + cent[1].x * u + cent[2].x * u^2 + cent[3].x * u^3
 *  y(u) = cent[0].y + cent[1].y * u + cent[2].y * u^2 + cent[3].y * u^3
 *  z(u) = cent[0].z + cent[1].z * u + cent[2].z * u^2 + cent[3].z * u^3
 *
 *  Likewise the radius is defined as:
 *
 *  r(u) = rad[0] + rad[1] * u + rad[2] * u^2 + rad[3] * u^3
 *
 */

GeomRef
SweptSphCreate(cent, rad)
     Vector cent[4];
     Float rad[4];
{
  SweptSph *sweptsph;
  int i;
  Float len;
  
  sweptsph = (SweptSph *)share_malloc(sizeof(SweptSph));
  
  for (i = 0; i < 4; i++){
    if (fabs(cent[i].x) < DBL_EPS) cent[i].x = 0.0;
    if (fabs(cent[i].y) < DBL_EPS) cent[i].y = 0.0;
    if (fabs(cent[i].z) < DBL_EPS) cent[i].z = 0.0;
    if (fabs(rad[i]) < DBL_EPS) rad[i] = 0.0;
  }
  
  sweptsph->a0 = cent[0];
  sweptsph->a1 = cent[1];
  sweptsph->a2 = cent[2];
  sweptsph->a3 = cent[3];
  
  sweptsph->rad[0] = rad[0];
  sweptsph->rad[1] = rad[1];
  sweptsph->rad[2] = rad[2];
  sweptsph->rad[3] = rad[3];
  
  /* d1 = cent_prime at t = 0 -- it is the vector that points
   *  to the "inside" of the swept tube at the first end
   */
  sweptsph->d1.x = cent[1].x;
  sweptsph->d1.y = cent[1].y;
  sweptsph->d1.z = cent[1].z;
  
  len = VecNormalize(&(sweptsph->d1));
  
  /* if cent_prime at t = 0 is zero, then determine d1 using
   *  (cent at t = 1e-2) - (cent at t = 0)
   */
  if (len == 0.0){
    sweptsph->d1.x = .01*cent[1].x + 1e-4*cent[2].x + 1e-6*cent[3].x;
    sweptsph->d1.y = .01*cent[1].y + 1e-4*cent[2].y + 1e-6*cent[3].y;
    sweptsph->d1.z = .01*cent[1].z + 1e-4*cent[2].z + 1e-6*cent[3].z;
  }
  
  len = VecNormalize(&(sweptsph->d1));
  
  /* d2 = -(cent_prime at t = 1) -- it is the vector that points
   *  to the "inside" of the swept tube at the second end
   */
  sweptsph->d2.x = -(cent[1].x + 2*cent[2].x + 3*cent[3].x);
  sweptsph->d2.y = -(cent[1].y + 2*cent[2].y + 3*cent[3].y);
  sweptsph->d2.z = -(cent[1].z + 2*cent[2].z + 3*cent[3].z);
  
  len = VecNormalize(&(sweptsph->d2));
  
  /* if cent_prime at t = 1 is zero, then determine d2 using
   *  (cent at t = (1 - 1e-2)) - (cent at t = 1)
   */
  if (len == 0.0){
    sweptsph->d2.x = .01*cent[1].x + .0199*cent[2].x + .029701*cent[3].x;
    sweptsph->d2.y = .01*cent[1].y + .0199*cent[2].y + .029701*cent[3].y;
    sweptsph->d2.z = .01*cent[1].z + .0199*cent[2].z + .029701*cent[3].z;
  }
  
  len = VecNormalize(&(sweptsph->d2));
  
  /*
     fprintf(stderr,"\nd1 = {%.4f, %.4f %.4f}, d2 = {%.4f, %.4f, %.4f}\n",
     sweptsph->d1.x,sweptsph->d1.y,sweptsph->d1.z,
     sweptsph->d2.x,sweptsph->d2.y,sweptsph->d2.z);
     fprintf(stderr, "dotp({1, 1, 1}/sqrt(3), d1): %f\n",
     dotp(&one, &sweptsph->d1));
     fprintf(stderr, "dotp({1, 1, 1}/sqrt(3), d2): %f\n",
     dotp(&one, &sweptsph->d2));
     */
  sweptsph->pb[0] = dotp(&(sweptsph->a0),&(sweptsph->a0)) - rad[0]*rad[0];
  sweptsph->pb[1] = 2*dotp(&(sweptsph->a0),&(sweptsph->a1)) - 2*rad[0]*rad[1];
  sweptsph->pb[2] = 2*dotp(&(sweptsph->a0),&(sweptsph->a2)) 
    + dotp(&(sweptsph->a1),&(sweptsph->a1)) - 2*rad[0]*rad[2] - rad[1]*rad[1];
  sweptsph->pb[3] = 2*dotp(&(sweptsph->a0),&(sweptsph->a3))
    + 2*dotp(&(sweptsph->a1),&(sweptsph->a2)) - 2*rad[0]*rad[3]
      - 2*rad[1]*rad[2];
  sweptsph->pb[4] = 2*dotp(&(sweptsph->a1),&(sweptsph->a3))
    + dotp(&(sweptsph->a2),&(sweptsph->a2)) - 2*rad[1]*rad[3] - rad[2]*rad[2];
  sweptsph->pb[5] = 2*dotp(&(sweptsph->a2),&(sweptsph->a3)) - 2*rad[2]*rad[3];
  sweptsph->pb[6] = dotp(&(sweptsph->a3),&(sweptsph->a3)) - rad[3]*rad[3];
  
  sweptsph->pd[0] = 16*(sweptsph->pb[4])*(sweptsph->pb[4]);
  sweptsph->pd[1] = 40*(sweptsph->pb[4])*(sweptsph->pb[5]);
  sweptsph->pd[2] = 48*(sweptsph->pb[4])*(sweptsph->pb[6])
    + 25*(sweptsph->pb[5])*(sweptsph->pb[5]);
  sweptsph->pd[3] = 60*(sweptsph->pb[5])*(sweptsph->pb[6]);
  sweptsph->pd[4] = 36*(sweptsph->pb[6])*(sweptsph->pb[6]);
  
  return (GeomRef)sweptsph;
}

Methods *
SweptSphMethods()
{
  if (iSweptSphMethods == (Methods *)NULL) {
    iSweptSphMethods = MethodsCreate();
#if 0
    iSweptSphMethods->create = (GeomCreateFunc *)SweptSphCreate;
#endif
    iSweptSphMethods->methods = SweptSphMethods;
    iSweptSphMethods->name = SweptSphName;
    iSweptSphMethods->intersect = SweptSphIntersect;
    iSweptSphMethods->normal = SweptSphNormal;
    iSweptSphMethods->uv = SweptSphUV;
    iSweptSphMethods->bounds = SweptSphBounds;
    iSweptSphMethods->stats = SweptSphStats;
    iSweptSphMethods->checkbounds = TRUE;
    iSweptSphMethods->closed = TRUE;
  }
  return iSweptSphMethods;
}

int
SweptSphIntersect(ref, ray, hl, mindist, maxdist)
     GeomRef ref;
     Ray *ray;
     HitList *hl;  /* unused here */
     Float mindist, *maxdist;
{
  SweptSph *sweptsph = (SweptSph *)ref;
  Vector a0, a1, a2, a3, dir, pos;
  Float b[7],c[4], d[11], e[6], h[5], i[11], j[11], g[11], roots[10];
  Float rad[4], radic, root;
  int num, hit, x, k;
  
  SweptSphTests++;
  
  dir = ray->dir;
  pos = ray->pos;
  
  rad[0] = sweptsph->rad[0];
  rad[1] = sweptsph->rad[1];
  rad[2] = sweptsph->rad[2];
  rad[3] = sweptsph->rad[3];
  
  a0 = sweptsph->a0;
  a1 = sweptsph->a1;
  a2 = sweptsph->a2;
  a3 = sweptsph->a3;
  
  /* b[] = f0 */
  b[0] =    dotp(&pos,&pos) - 2*dotp(&pos,&a0) + sweptsph->pb[0];
  b[1] = -2*dotp(&pos,&a1 ) + sweptsph->pb[1];
  b[2] = -2*dotp(&pos,&a2 ) + sweptsph->pb[2];
  b[3] = -2*dotp(&pos,&a3 ) + sweptsph->pb[3];
  b[4] = sweptsph->pb[4];
  b[5] = sweptsph->pb[5];
  b[6] = sweptsph->pb[6];
  
  /* c[] = f1 */
  c[0] =  2*dotp(&pos,&dir) - 2*dotp(&a0,&dir);
  c[1] = -2*dotp(&a1 ,&dir);
  c[2] = -2*dotp(&a2 ,&dir);
  c[3] = -2*dotp(&a3 ,&dir);
  
  /* d[] = f0'*f0' which also equals f2*f0'*f0' since our ray->dir is
     a unit vector */
  d[0] =     b[1]*b[1];
  d[1] =   4*b[1]*b[2];
  d[2] =   6*b[1]*b[3] +  4*b[2]*b[2];
  d[3] =   8*b[1]*b[4] + 12*b[2]*b[3];
  d[4] =  10*b[1]*b[5] + 16*b[2]*b[4] +  9*b[3]*b[3];
  d[5] =  12*b[1]*b[6] + 20*b[2]*b[5] + 24*b[3]*b[4];
  d[6] =  24*b[2]*b[6] + 30*b[3]*b[5] + sweptsph->pd[0];
  d[7] =  36*b[3]*b[6] + sweptsph->pd[1];
  d[8] =  sweptsph->pd[2];
  d[9] =  sweptsph->pd[3];
  d[10] = sweptsph->pd[4];
  
  /* e = f1*f1' */
  e[0] =   c[0]*c[1];
  e[1] = 2*c[0]*c[2] +   c[1]*c[1];
  e[2] = 3*c[0]*c[3] + 3*c[1]*c[2];
  e[3] = 4*c[1]*c[3] + 2*c[2]*c[2];
  e[4] = 5*c[2]*c[3];
  e[5] = 3*c[3]*c[3];
  
  /* h[] = f1'*f1' */
  h[0] =    c[1]*c[1];
  h[1] =  4*c[1]*c[2];
  h[2] =  6*c[1]*c[3] + 4*c[2]*c[2];
  h[3] = 12*c[2]*c[3];
  h[4] =  9*c[3]*c[3];
  
  /* i[] = f1*f1'*f0' */
  i[0] =    e[0]*b[1];
  i[1] =  2*e[0]*b[2] +   e[1]*b[1];
  i[2] =  3*e[0]*b[3] + 2*e[1]*b[2] +   e[2]*b[1];
  i[3] =  4*e[0]*b[4] + 3*e[1]*b[3] + 2*e[2]*b[2] +   e[3]*b[1];
  i[4] =  5*e[0]*b[5] + 4*e[1]*b[4] + 3*e[2]*b[3] + 2*e[3]*b[2] +   e[4]*b[1];
  i[5] =  6*e[0]*b[6] + 5*e[1]*b[5] + 4*e[2]*b[4] + 3*e[3]*b[3] 
    + 2*e[4]*b[2] + e[5]*b[1];
  i[6] =  6*e[1]*b[6] + 5*e[2]*b[5] + 4*e[3]*b[4] + 3*e[4]*b[3] + 2*e[5]*b[2];
  i[7] =  6*e[2]*b[6] + 5*e[3]*b[5] + 4*e[4]*b[4] + 3*e[5]*b[3];
  i[8] =  6*e[3]*b[6] + 5*e[4]*b[5] + 4*e[5]*b[4];
  i[9] =  6*e[4]*b[6] + 5*e[5]*b[5];
  i[10] = 6*e[5]*b[6];
  
  /* j[] = f1'*f1'*f0 */
  j[0] =  h[0]*b[0];
  j[1] =  h[0]*b[1] + h[1]*b[0];
  j[2] =  h[0]*b[2] + h[1]*b[1] + h[2]*b[0];
  j[3] =  h[0]*b[3] + h[1]*b[2] + h[2]*b[1] + h[3]*b[0];
  j[4] =  h[0]*b[4] + h[1]*b[3] + h[2]*b[2] + h[3]*b[1] + h[4]*b[0];
  j[5] =  h[0]*b[5] + h[1]*b[4] + h[2]*b[3] + h[3]*b[2] + h[4]*b[1];
  j[6] =  h[0]*b[6] + h[1]*b[5] + h[2]*b[4] + h[3]*b[3] + h[4]*b[2];
  j[7] =  h[1]*b[6] + h[2]*b[5] + h[3]*b[4] + h[4]*b[3];
  j[8] =  h[2]*b[6] + h[3]*b[5] + h[4]*b[4];
  j[9] =  h[3]*b[6] + h[4]*b[5];
  j[10] = h[4]*b[6];
  
  /* g[] = f2*f0'*f0' - f1*f1'*f0' + f1'*f1'*f0 */
  for (x = 0; x <= 10; x++){
    g[x] = d[x] - i[x] + j[x];
  }
  
  /* find the roots */
  num = FindRoot(g,10,0.0,1.0,roots,-2);
  
  hit = FALSE;
  
  /* check each root */
  for (k = 0; k < num; k++) {
    register Float kx,ky,kz,kr,ku,kb,kt;
    
    ku = roots[k];
    kr = ((rad[3]*ku + rad[2])*ku + rad[1])*ku + rad[0];
    kx = ((  a3.x*ku +   a2.x)*ku +   a1.x)*ku + a0.x - pos.x;
    ky = ((  a3.y*ku +   a2.y)*ku +   a1.y)*ku + a0.y - pos.y;
    kz = ((  a3.z*ku +   a2.z)*ku +   a1.z)*ku + a0.z - pos.z;
    kb = kx*dir.x + ky*dir.y + kz*dir.z;
    kt = kb*kb - kx*kx - ky*ky - kz*kz + kr*kr;
    if (kt > 0) {
      Float ks;
      kt = (Float)sqrt(kt);
      ks = kb - kt;
      if (ks < *maxdist) {
	if (ks > (mindist + SWEPT_EPS)) {
	  SweptSphRoot = roots[k];
	  *maxdist = ks;
	  hit = TRUE;
	} else {
	  ks = kb + kt;
	  if (ks > (mindist + SWEPT_EPS)) {
	    if (ks < *maxdist) {
	      SweptSphRoot = roots[k];
	      *maxdist = ks;
	      hit = TRUE;
	    }
	  } /* if large root > mindist */ 
	} /* if closest root is not a hit */
      } /* if both roots < maxdist */
    } /* kt > 0 */
  }
  
  /* check intersection with the end spheres */
  g[2] = 1;
  g[1] = c[0];
  g[0] = b[0];
  
  radic = g[1]*g[1] - 4*g[0];
  
  if (radic > 0){
    radic = sqrt(radic);
    root = (-g[1] - radic)/2.0;
    
    if (root > (mindist + SWEPT_EPS) && root < *maxdist){
      if (!Inside(sweptsph, ray, root, T_ZERO)){
	SweptSphRoot = 0.0;
	*maxdist = root;
	hit = TRUE;
      }
    }
    
    root = (-g[1] + radic)/2.0;
    
    if (root > (mindist + SWEPT_EPS) && root < *maxdist){
      if (!Inside(sweptsph, ray, root, T_ZERO)){
	SweptSphRoot = 0.0;
	*maxdist = root;
	hit = TRUE;
      }
    }
  }
  else if (radic == 0){
    root = -g[1]/2.0;
    if (root > (mindist + SWEPT_EPS) && root < *maxdist){
      if (!Inside(sweptsph, ray, root, T_ZERO)){
	SweptSphRoot = 0.0;
	*maxdist = root;
	hit = TRUE;
      }
    }
  }		
  
  
  g[2] = 1;
  g[1] = c[0]+c[1]+c[2]+c[3];
  g[0] = b[0]+b[1]+b[2]+b[3]+b[4]+b[5]+b[6];
  
  radic = g[1]*g[1] - 4*g[0];
  
  if (radic > 0){
    radic = sqrt(radic);
    root = (-g[1] - radic)/2.0;
    
    if (root > (mindist + SWEPT_EPS) && root < *maxdist){
      if (!Inside(sweptsph, ray, root, T_ONE)){
	SweptSphRoot = 1.0;
	*maxdist = root;
	hit = TRUE;
      }
    }
    
    root = (-g[1] + radic)/2.0;
    
    if (root > (mindist + SWEPT_EPS) && root < *maxdist){
      if (!Inside(sweptsph, ray, root, T_ONE)){
	SweptSphRoot = 1.0;
	*maxdist = root;
	hit = TRUE;
      }
    }
  }
  else if (radic == 0){
    root = -g[1]/2.0;
    if (root > (mindist + SWEPT_EPS) && root < *maxdist){
      if (!Inside(sweptsph, ray, root, T_ONE)){
	SweptSphRoot = 1.0;
	*maxdist = root;
	hit = TRUE;
      }
    }
  }		
  
  if (hit == TRUE){
    SweptSphHits++;
  }
  return hit;
}

/*
 * This routine checks to see if a certain root intersects the
 *	inside half of the end sphere.  The end checked is
 *	specified by "end".
 */
static int
Inside(sweptsph, ray, root, end)
     SweptSph *sweptsph;
     Ray *ray;
     Float root;
     int end;
{
  Vector cent, pnt, *end_vec;
  Vector a0, a1, a2, a3;
  
  a0 = sweptsph->a0;
  a1 = sweptsph->a1;
  a2 = sweptsph->a2;
  a3 = sweptsph->a3;
  
  /* pnt is the point where the ray intersects the end sphere */
  pnt.x = ray->pos.x + root * ray->dir.x;
  pnt.y = ray->pos.y + root * ray->dir.y;
  pnt.z = ray->pos.z + root * ray->dir.z;
  
  
  switch (end){
  case T_ZERO:
    /*
     * cent is the vector from the center of the sphere to pnt
     */
    cent.x = pnt.x - a0.x;
    cent.y = pnt.y - a0.y;
    cent.z = pnt.z - a0.z;
    
    end_vec = &sweptsph->d1;
    break;
  case T_ONE:
    /*
     * cent is the vector from the center of the sphere to pnt
     */ 
    cent.x = pnt.x - a0.x - a1.x - a2.x - a3.x;
    cent.y = pnt.y - a0.y - a1.y - a2.y - a3.y;
    cent.z = pnt.z - a0.z - a1.z - a2.z - a3.z;
    
    end_vec = &sweptsph->d2;
    break;
  }
  
  VecNormalize(&cent);
  return (dotp(&cent,end_vec) > 0);
  
}

int
SweptSphNormal(ref, pos, nrm, gnrm)
     GeomRef ref;
     Vector *pos, *nrm, *gnrm;
{
  SweptSph *sweptsph = (SweptSph *)ref;
  Vector a0, a1, a2, a3;
  
  a0 = sweptsph->a0;
  a1 = sweptsph->a1;
  a2 = sweptsph->a2;
  a3 = sweptsph->a3;
  
  /* normal is simply the normal to the sphere at c(u): u = SweptSphRoot */
  nrm->x = pos->x
    - a0.x - (a1.x + (a2.x + a3.x*SweptSphRoot)*SweptSphRoot)*SweptSphRoot;
  nrm->y = pos->y
    - a0.y - (a1.y + (a2.y + a3.y*SweptSphRoot)*SweptSphRoot)*SweptSphRoot;
  nrm->z = pos->z
    - a0.z - (a1.z + (a2.z + a3.z*SweptSphRoot)*SweptSphRoot)*SweptSphRoot;
  
  VecNormalize(nrm);
  *gnrm = *nrm;
  SweptSphRoot1 = SweptSphRoot; /*POD temp*/
  return FALSE;
}

void
SweptSphUV(ref, pos, norm, uv, dpdu, dpdv)
     GeomRef ref;
     Vector *pos, *norm, *dpdu, *dpdv;
     Vec2d *uv;
{
  SweptSph *sweptsph = (SweptSph *)ref;
  Float root,costheta,sintheta;
  Vector down,xdir,ydir,zdir,tmp;
  Vector a1, a2, a3;
  static int invalid = 0;
  
  a1 = sweptsph->a1;
  a2 = sweptsph->a2;
  a3 = sweptsph->a3;
  
  /* 
   * question of the day - 
   * is SweptSphRoot still valid or have shadow rays been cast?
   * use SweptSphRoot1 for now.
   */
  /*
   * Cheap answer: if you never, ever see this, then....
   */
  if (!invalid && SweptSphRoot != SweptSphRoot1) {
    fprintf(stderr,
	    "SweptSphRoot has been modifed since SweptSphNormal()!!!\n");
    invalid = 1;
  }
  
  root = uv->v = SweptSphRoot1;
  if (uv->v < 0.0) uv->v = 0.0;
  if (uv->v > 1.0) uv->v = 1.0;
  
  zdir.x = -a1.x - (2.0*a2.x + 3.0*a3.x*root)*root;
  zdir.y = -a1.y - (2.0*a2.y + 3.0*a3.y*root)*root;
  zdir.z = -a1.z - (2.0*a2.z + 3.0*a3.z*root)*root;
  if ( VecNormalize(&zdir) == 0.0 )
    { /*POD temp */
      fprintf(stderr,"Swept Test: zdir==0\n");
    }
  down.x = 0.0; down.y = 0.0; down.z = -1.0;
  if (VecNormCross(&zdir,&down,&tmp) == 0.0)
    {
      down.x = 0.0; down.y = 1.0; down.z = 0.0;
      VecNormCross(&zdir,&down,&tmp);
    }
  VecNormCross(&tmp,&zdir,&xdir);
  VecNormCross(&zdir,&xdir,&ydir);
  
  costheta = dotp(norm,&xdir);
  sintheta = dotp(norm,&ydir);
  if (costheta > 1.)      /* roundoff */
    uv->u = 0.;
  else if (costheta < -1.)
    uv->u = 0.5;
  else
    uv->u = acos(costheta) / TWOPI;
  if (sintheta < 0.)
    uv->u = 1. - uv->u;
  
  if (dpdu) {
    /* dpdu will be cross of normal with dpdv */
    VecNormCross(&zdir,norm,dpdu);
    
    /* dpdv will be tangent to center curve u */
    dpdv->x = zdir.x;
    dpdv->y = zdir.y;
    dpdv->z = zdir.z;
  }
}


void
SweptSphBounds(ref, bounds)
     GeomRef ref;
     Float bounds[2][3];
{
  SweptSph *sweptsph = (SweptSph *)ref;
  Float a[4], aprime[3],root, val, rad, radimax;
  
  /* for the bounding boxes, calculate the maximum radius of the swept
     sphere then set the bounding box to the max and min of the
     center curve plus or minus the maximum radius of the sphere
     */
  
  /* find the maximum radius */
  
  radimax = 0;
  
  a[0] = sweptsph->rad[0];
  a[1] = sweptsph->rad[1];
  a[2] = sweptsph->rad[2];
  a[3] = sweptsph->rad[3];
  
  aprime[0] = a[1];
  aprime[1] = 2.0*a[2];
  aprime[2] = 3.0*a[3];
  
  if (aprime[2] == 0){
    if (aprime[1] != 0){
      root = -aprime[0]/aprime[1];
      if (root > 0.0 && root < 1.0){
	val = a[0] + (a[1] + (a[2] + a[3]*root)*root)*root;
	
	if (val > radimax){
	  radimax = val;
	}
      }
    }
  }
  else {
    rad = aprime[1]*aprime[1] - 4.0 * aprime[2] *  aprime[0];
    if (rad == 0) {
      root = -aprime[1]/(2.0*aprime[2]);
      if (root > 0.0 && root < 1.0){
	val = a[0] + (a[1] + (a[2] + a[3]*root)*root)*root;
	
	if (val > radimax){
	  radimax = val;
	}
      }
    }
    else if (rad > 0){
      rad = sqrt(rad);
      
      root = (-aprime[1] - rad)/(2.0*aprime[2]);
      
      if (root > 0.0 && root < 1.0){
	val = a[0] + (a[1] + (a[2] + a[3]*root)*root)*root;
	
	if (val > radimax){
	  radimax = val;
	}
      }
      
      root = (-aprime[1] + rad)/(2.0*aprime[2]);
      
      if (root > 0.0 && root < 1.0){
	val = a[0] + (a[1] + (a[2] + a[3]*root)*root)*root;
	
	if (val > radimax){
	  radimax = val;
	}
      }
    }
  }
  
  val = a[0];
  if (val > radimax){
    radimax = val;
  }
  
  val = a[0] + a[1] + a[2] + a[3];
  if (val > radimax){
    radimax = val;
  }
  
  
  /* find the x bounds of the curve */
  a[0] = sweptsph->a0.x;
  a[1] = sweptsph->a1.x;
  a[2] = sweptsph->a2.x;
  a[3] = sweptsph->a3.x;
  
  aprime[0] = a[1];
  aprime[1] = 2.0*a[2];
  aprime[2] = 3.0*a[3];
  
  
  if (aprime[2] == 0){
    if (aprime[1] != 0){
      root = -aprime[0]/aprime[1];
      if (root > 0.0 && root < 1.0){
	val = a[0] + (a[1] + (a[2] + a[3]*root)*root)*root;
	
	if (val - radimax < bounds[LOW][X]){
	  bounds[LOW][X] = val - radimax;
	}
	if (val + radimax > bounds[HIGH][X]){
	  bounds[HIGH][X] = val + radimax;
	}
      }
    }
  } else {
    rad = aprime[1]*aprime[1] - 4.0 * aprime[2] *  aprime[0];
    
    if (rad == 0) {
      root = -aprime[1]/(2.0*aprime[2]);
      if (root > 0.0 && root < 1.0){
	val = a[0] + (a[1] + (a[2] + a[3]*root)*root)*root;
	
	if (val - radimax < bounds[LOW][X]){
	  bounds[LOW][X] = val - radimax;
	}
	if (val + radimax > bounds[HIGH][X]){
	  bounds[HIGH][X] = val + radimax;
	}
      }
    }
    else if (rad > 0){
      rad = sqrt(rad);
      
      root = (-aprime[1] - rad)/(2.0*aprime[2]);
      
      if (root > 0.0 && root < 1.0){
	val = a[0] + (a[1] + (a[2] + a[3]*root)*root)*root;
	
	if (val - radimax < bounds[LOW][X]){
	  bounds[LOW][X] = val - radimax;
	}
	if (val + radimax > bounds[HIGH][X]){
	  bounds[HIGH][X] = val + radimax;
	}
      }
      
      root = (-aprime[1] + rad)/(2.0*aprime[2]);
      
      if (root > 0.0 && root < 1.0){
	val = a[0] + (a[1] + (a[2] + a[3]*root)*root)*root;
	
	if (val - radimax < bounds[LOW][X]){
	  bounds[LOW][X] = val - radimax;
	}
	if (val + radimax > bounds[HIGH][X]){
	  bounds[HIGH][X] = val + radimax;
	}
      }
    }
  }
  
  val = a[0];
  if (val - radimax < bounds[LOW][X]){
    bounds[LOW][X] = val - radimax;
  }
  if (val + radimax > bounds[HIGH][X]){
    bounds[HIGH][X] = val + radimax;
  }
  val = a[0] + a[1] + a[2] + a[3];
  if (val - radimax < bounds[LOW][X]){
    bounds[LOW][X] = val - radimax;
  }
  if (val + radimax > bounds[HIGH][X]){
    bounds[HIGH][X] = val + radimax;
  }
  
  
  /* find the y bounds of the curve */
  a[0] = sweptsph->a0.y;
  a[1] = sweptsph->a1.y;
  a[2] = sweptsph->a2.y;
  a[3] = sweptsph->a3.y;
  
  aprime[0] = a[1];
  aprime[1] = 2.0*a[2];
  aprime[2] = 3.0*a[3];
  
  if (aprime[2] == 0){
    if (aprime[1] != 0){
      root = -aprime[0]/aprime[1];
      if (root > 0.0 && root < 1.0){
	val = a[0] + (a[1] + (a[2] + a[3]*root)*root)*root;
	
	if (val - radimax < bounds[LOW][Y]){
	  bounds[LOW][Y] = val - radimax;
	}
	if (val + radimax > bounds[HIGH][Y]){
	  bounds[HIGH][Y] = val + radimax;
	}
      }
    }
  } else {
    rad = aprime[1]*aprime[1] - 4.0 * aprime[2] *  aprime[0];
    if (rad == 0) {
      root = -aprime[1]/(2.0*aprime[2]);
      if (root > 0.0 && root < 1.0){
	val = a[0] + (a[1] + (a[2] + a[3]*root)*root)*root;
	
	if (val - radimax < bounds[LOW][Y]){
	  bounds[LOW][Y] = val - radimax;
	}
	if (val + radimax > bounds[HIGH][Y]){
	  bounds[HIGH][Y] = val + radimax;
	}
      }
    } else if (rad > 0){
      rad = sqrt(rad);
      root = (-aprime[1] - rad)/(2.0*aprime[2]);
      if (root > 0.0 && root < 1.0){
	val = a[0] + (a[1] + (a[2] + a[3]*root)*root)*root;
	
	if (val - radimax < bounds[LOW][Y]){
	  bounds[LOW][Y] = val - radimax;
	}
	if (val + radimax > bounds[HIGH][Y]){
	  bounds[HIGH][Y] = val + radimax;
	}
      }
      root = (-aprime[1] + rad)/(2.0*aprime[2]);
      if (root > 0.0 && root < 1.0){
	val = a[0] + (a[1] + (a[2] + a[3]*root)*root)*root;	
	
	if (val - radimax < bounds[LOW][Y]){
	  bounds[LOW][Y] = val - radimax;
	}
	if (val + radimax > bounds[HIGH][Y]){
	  bounds[HIGH][Y] = val + radimax;
	}
      }
    }
  }
  
  val = a[0];
  if (val - radimax < bounds[LOW][Y]){
    bounds[LOW][Y] = val - radimax;
  }
  if (val + radimax > bounds[HIGH][Y]){
    bounds[HIGH][Y] = val + radimax;
  }
  val = a[0] + a[1] + a[2] + a[3];
  if (val - radimax < bounds[LOW][Y]){
    bounds[LOW][Y] = val - radimax;
  }
  if (val + radimax > bounds[HIGH][Y]){
    bounds[HIGH][Y] = val + radimax;
  }
  
  
  /* find the z bounds of the curve */
  a[0] = sweptsph->a0.z;
  a[1] = sweptsph->a1.z;
  a[2] = sweptsph->a2.z;
  a[3] = sweptsph->a3.z;
  
  aprime[0] = a[1];
  aprime[1] = 2.0*a[2];
  aprime[2] = 3.0*a[3];
  
  if (aprime[2] == 0){
    if (aprime[1] != 0){
      root = -aprime[0]/aprime[1];
      if (root > 0.0 && root < 1.0){
	val = a[0] + (a[1] + (a[2] + a[3]*root)*root)*root;
	
	if (val - radimax < bounds[LOW][Z]){
	  bounds[LOW][Z] = val - radimax;
	}
	if (val + radimax > bounds[HIGH][Z]){
	  bounds[HIGH][Z] = val + radimax;
	}
      }
    }
  } else {
    rad = aprime[1]*aprime[1] - 4.0 * aprime[2] *  aprime[0];
    
    if (rad == 0) {
      root = -aprime[1]/(2.0*aprime[2]);
      if (root > 0.0 && root < 1.0){
	val = a[0] + (a[1] + (a[2] + a[3]*root)*root)*root;
	
	if (val - radimax < bounds[LOW][Z]){
	  bounds[LOW][Z] = val - radimax;
	}
	if (val + radimax > bounds[HIGH][Z]){
	  bounds[HIGH][Z] = val + radimax;
	}
      }
    }
    else if (rad > 0){
      rad = sqrt(rad);
      root = (-aprime[1] - rad)/(2.0*aprime[2]);
      if (root > 0.0 && root < 1.0){
	val = a[0] + (a[1] + (a[2] + a[3]*root)*root)*root;
	
	if (val - radimax < bounds[LOW][Z]){
	  bounds[LOW][Z] = val - radimax;
	}
	if (val + radimax > bounds[HIGH][Z]){
	  bounds[HIGH][Z] = val + radimax;
	}
      }
      root = (-aprime[1] + rad)/(2.0*aprime[2]);
      if (root > 0.0 && root < 1.0){
	val = a[0] + (a[1] + (a[2] + a[3]*root)*root)*root;
	
	if (val - radimax < bounds[LOW][Z]){
	  bounds[LOW][Z] = val - radimax;
	}
	if (val + radimax > bounds[HIGH][Z]){
	  bounds[HIGH][Z] = val + radimax;
	}
      }
    }
  }
  
  val = a[0];
  if (val - radimax < bounds[LOW][Z]){
    bounds[LOW][Z] = val - radimax;
  }
  if (val + radimax > bounds[HIGH][Z]){
    bounds[HIGH][Z] = val + radimax;
  }
  val = a[0] + a[1] + a[2] + a[3];
  if (val - radimax < bounds[LOW][Z]){
    bounds[LOW][Z] = val - radimax;
  }
  if (val + radimax > bounds[HIGH][Z]){
    bounds[HIGH][Z] = val + radimax;
  }
}

void
SweptSphStats(tests, hits)
     unsigned long *tests, *hits;
{
  *tests = SweptSphTests;
  *hits = SweptSphHits;
}

char *
SweptSphName()
{
  return sweptsphName;
}

