/* Copyright (c) 1992 Regents of the University of California */

#ifndef lint
static char SCCSid[] = "@(#)sphere.c 2.2 10/24/92 LBL";
#endif

/*
 *  sphere.c - compute ray intersection with spheres.
 *
 *     8/19/85
 */

#include  "ray.h"

#include  "otypes.h"


o_sphere(so, r)			/* compute intersection with sphere */
OBJREC  *so;
register RAY  *r;
{
	double  a, b, c;	/* coefficients for quadratic equation */
	double  root[2];	/* quadratic roots */
	int  nroots;
	double  t;
	register FLOAT  *ap;
	register int  i;

	if (so->oargs.nfargs != 4)
		objerror(so, USER, "bad # arguments");
	ap = so->oargs.farg;
	if (ap[3] < -FTINY) {
		objerror(so, WARNING, "negative radius");
		so->otype = so->otype == OBJ_SPHERE ?
				OBJ_BUBBLE : OBJ_SPHERE;
		ap[3] = -ap[3];
	} else if (ap[3] <= FTINY)
		objerror(so, USER, "zero radius");

	/*
	 *	We compute the intersection by substituting into
	 *  the surface equation for the sphere.  The resulting
	 *  quadratic equation in t is then solved for the
	 *  smallest positive root, which is our point of
	 *  intersection.
	 *	Since the ray is normalized, a should always be
	 *  one.  We compute it here to prevent instability in the
	 *  intersection calculation.
	 */
				/* compute quadratic coefficients */
	a = b = c = 0.0;
	for (i = 0; i < 3; i++) {
		a += r->rdir[i]*r->rdir[i];
		t = r->rorg[i] - ap[i];
		b += 2.0*r->rdir[i]*t;
		c += t*t;
	}
	c -= ap[3] * ap[3];

	nroots = quadratic(root, a, b, c);	/* solve quadratic */
	
	for (i = 0; i < nroots; i++)		/* get smallest positive */
		if ((t = root[i]) > FTINY)
			break;
	if (i >= nroots)
		return(0);			/* no positive root */

	if (t >= r->rot)
		return(0);			/* other is closer */

	r->ro = so;
	r->rot = t;
					/* compute normal */
	a = ap[3];
	if (so->otype == OBJ_BUBBLE)
		a = -a;			/* reverse */
	for (i = 0; i < 3; i++) {
		r->rop[i] = r->rorg[i] + r->rdir[i]*t;
		r->ron[i] = (r->rop[i] - ap[i]) / a;
	}
	r->rod = -DOT(r->rdir, r->ron);
	r->rox = NULL;

	return(1);			/* hit */
}
