/* Copyright (c) 1986 Regents of the University of California */

#ifndef lint
static char SCCSid[] = "@(#)sphere.c 2.1 11/12/91 LBL";
#endif

/*
 *  sphere.c - routines for creating octrees for spheres.
 *
 *     7/28/85
 */

#include  "standard.h"

#include  "octree.h"

#include  "object.h"

#include  "otypes.h"

#define  ROOT3		1.732050808

/*
 *	Regrettably, the algorithm for determining a cube's location
 *  with respect to a sphere is not simple.  First, a quick test is 
 *  made to determine if the sphere and the bounding sphere of the cube 
 *  are disjoint.  This of course means no intersection.  Failing this,
 *  we determine if the cube lies inside the sphere.  The cube is
 *  entirely inside if the bounding sphere on the cube is
 *  contained within our sphere.  This means no intersection.  Otherwise,
 *  if the cube radius is smaller than the sphere's and the cube center is
 *  inside the sphere, we assume intersection.  If these tests fail,
 *  we proceed as follows.
 *	The sphere center is located in relation to the 6 cube faces,
 *  and one of four things is done depending on the number of
 *  planes the center lies between:
 *
 *	0:  The sphere is closest to a cube corner, find the
 *		distance to that corner.
 *
 *	1:  The sphere is closest to a cube edge, find this
 *		distance.
 *
 *	2:  The sphere is closest to a cube face, find the distance.
 *
 *	3:  The sphere has its center inside the cube.
 *
 *	In cases 0-2, if the closest part of the cube is within
 *  the radius distance from the sphere center, we have intersection.
 *  If it is not, the cube must be outside the sphere.
 *	In case 3, there must be intersection, and no further
 *  tests are necessary.
 */


o_sphere(o, cu)			/* determine if sphere intersects cube */
OBJREC  *o;
register CUBE  *cu;
{
	FVECT  v1;
	double  d1, d2;
	register FLOAT  *fa;
	register int  i;
#define  cent		fa
#define  rad		fa[3]
					/* get arguments */
	if (o->oargs.nfargs != 4)
		objerror(o, USER, "bad # arguments");
	fa = o->oargs.farg;
	if (rad < -FTINY) {
		objerror(o, WARNING, "negative radius");
		o->otype = o->otype == OBJ_SPHERE ?
				OBJ_BUBBLE : OBJ_SPHERE;
		rad = -rad;
	} else if (rad <= FTINY)
		objerror(o, USER, "zero radius");

	d1 = ROOT3/2.0 * cu->cusize;	/* bounding radius for cube */

	d2 = cu->cusize * 0.5;		/* get distance between centers */
	for (i = 0; i < 3; i++)
		v1[i] = cu->cuorg[i] + d2 - cent[i];
	d2 = DOT(v1,v1);

	if (d2 > (rad+d1+FTINY)*(rad+d1+FTINY))	/* quick test */
		return(O_MISS);			/* cube outside */
	
					/* check sphere interior */
	if (d1 < rad) {
		if (d2 < (rad-d1-FTINY)*(rad-d1-FTINY))
			return(O_MISS);		/* cube inside sphere */
		if (d2 < (rad+FTINY)*(rad+FTINY))
			return(O_HIT);		/* cube center inside */
	}
					/* find closest distance */
	for (i = 0; i < 3; i++)
		if (cent[i] < cu->cuorg[i])
			v1[i] = cu->cuorg[i] - cent[i];
		else if (cent[i] > cu->cuorg[i] + cu->cusize)
			v1[i] = cent[i] - (cu->cuorg[i] + cu->cusize);
		else
			v1[i] = 0;
					/* final intersection check */
	if (DOT(v1,v1) <= (rad+FTINY)*(rad+FTINY))
		return(O_HIT);
	else
		return(O_MISS);
}
