#include "bounds3d.h"
#include "rc_globals.h"
#include "ray.h"
#include "../log.h"

#ifdef OPENGL
#include <GL/gl.h>
#endif

#include "boundslog.h"

ostream& operator<<(ostream &co, const Bounds3d &B) {

  co << B[0] << " - " << B[1] << endl;

	return co;
}

void Bounds3d::CalcPointOnSurface(int major_plane, real a, real b, Vec4 &V,
								  int back) const {

	switch (major_plane) {

		case MP_XY:
			V.set_x(_p[0].x() * (1.0f - a) + _p[1].x() * a);
			V.set_y(_p[0].y() * (1.0f - b) + _p[1].y() * b);
			V.set_z(back ? _p[0].z() : _p[1].z()); // XY reversed!
			break;

		case MP_XZ:
			V.set_x(_p[0].x() * (1.0f - a) + _p[1].x() * a);
			V.set_y(back ? _p[1].y() : _p[0].y());
			V.set_z(_p[0].z() * (1.0f - b) + _p[1].z() * b);
			break;

		case MP_YZ:
			V.set_x(back ? _p[1].x() : _p[0].x());
			V.set_y(_p[0].y() * (1.0f - b) + _p[1].y() * b);
			V.set_z(_p[0].z() * (1.0f - a) + _p[1].z() * a);
			break;

		default:
			cerr << "Bounds3d::CalcPointOnSurface(): undefined major plane"
				 << endl;
	}
}


//returns face == -1 if the ray direction is 0,0,0

//else
//tleft = t of where the ray leaves the bounding box
//face and direction of where the ray leaves
//direction == 1 if leaving +face, == 0 if leaving -face
void Bounds3d::whereLeft (const Ray &ray, float &tleft, int &face, int &direction, Vec4 *leftpt) const
{
	float t_far = MAXFLOAT;
	float t1, t2;

	float dir;

	face = -1;

	BOUNDSLOG (BoundsWhereLeft_Start, new BoundsWhereLeft_Start_Entry (*this, ray.R(), ray.D()));

        //t1 = distance along ray to hit the low plane
        //t2 = distance along ray to hit the high plane

	dir = ray.D().x();
	if (dir) {

		t1 = (_p[0].x() - ray.R().x()) / dir;
		t2 = (_p[1].x() - ray.R().x()) / dir;

		if (dir > 0.0)
		  direction = 1;
		else
		  direction = 0;

		if (t1 < t2) {
		  t_far = t2;
		  face = 0;
		  
		}
		else {
		  t_far = t1;
		  face = 0;
		}

		BOUNDSLOG (BoundsWhereLeft_Face_Project, new BoundsWhereLeft_Face_Project_Entry (0, direction, t_far, 0, direction, t_far));
	}

	dir = ray.D().y();
	if (dir) {

		t1 = (_p[0].y() - ray.R().y()) / dir;
		t2 = (_p[1].y() - ray.R().y()) / dir;

		if (t1 < t2) {
		  if (t2 < t_far) 
		    {
		      t_far = t2;
		      face = 1;
		      if (dir > 0.0)
			direction = 1;
		      else
			direction = 0;

		    }
		}
		else {
		  if (t1 < t_far) 
		    {
		      t_far = t1;
		      face = 1;
		      if (dir > 0.0)
			direction = 1;
		      else
			direction = 0;
		    }
		}
		BOUNDSLOG (BoundsWhereLeft_Face_Project, new BoundsWhereLeft_Face_Project_Entry (1, (dir > 0.0), (t1 < t2) ? t2 : t1, face, direction, t_far));
	}

	dir = ray.D().z();
	if (dir) {
		t1 = (_p[0].z() - ray.R().z()) / dir;
		t2 = (_p[1].z() - ray.R().z()) / dir;

		if (t1 < t2) {
			if (t2 < t_far) 
			  {
			    t_far = t2;
			    face = 2;
			    if (dir > 0.0)
			      direction = 1;
			    else
			      direction = 0;
			  }
		}
		else {
			if (t1 < t_far) 
			  {
			    t_far = t1;
			    face = 2;
			    if (dir > 0.0)
			      direction = 1;
			    else
			      direction = 0;
			  }
		}
		BOUNDSLOG (BoundsWhereLeft_Face_Project, new BoundsWhereLeft_Face_Project_Entry (2, (dir > 0.0), (t1 < t2) ? t2 : t1, face, direction, t_far));
	}

	tleft = t_far;

	//compute the point the usual way...
	if (leftpt)
	  {
	    *leftpt = ray.R() + tleft * ray.D();

	    //...but we can set one of its members *exactly*.
	    switch (face)
	      {
	      case 0:
		leftpt->set_x ((direction == 1) ? _p[1].x() : _p[0].x());
		break;
	      case 1:
		leftpt->set_y ((direction == 1) ? _p[1].y() : _p[0].y());
		break;	
	      case 2:
		leftpt->set_z ((direction == 1) ? _p[1].z() : _p[0].z());
		break;
	      default:
		cerr << "whereLeft given ray " << ray << ", never left bbox " << (*this) << ", apparently." << endl;
		assert (1==0);
		break;
	      }
	  } //if leftpt

	BOUNDSLOG (BoundsWhereLeft_End, new BoundsWhereLeft_End_Entry (face, direction, leftpt));
}


int Bounds3d::IncludesBox (const Bounds3d &box) const
{
  if (_p[0].x() > box._p[0].x()) return 0;
  if (_p[1].x() < box._p[1].x()) return 0;
  if (_p[0].y() > box._p[0].y()) return 0;
  if (_p[1].y() < box._p[1].y()) return 0;
  if (_p[0].z() > box._p[0].z()) return 0;
  if (_p[1].z() < box._p[1].z()) return 0;
  return 1;
}

int Bounds3d::IncludesPoint (const Vec4 &pt) const
{
  BOUNDSLOG (BoundsIncludesPt_Start, new BoundsIncludesPt_Start_Entry (*this, pt));

  if (pt.x() < _p[0].x())
    {
      BOUNDSLOG (BoundsIncludesPt_Face_Check, new BoundsIncludesPt_Face_Check_Entry (*this, pt, 0, 0, 0));
      return 0;
    }
  else {
      BOUNDSLOG (BoundsIncludesPt_Face_Check, new BoundsIncludesPt_Face_Check_Entry (*this, pt, 0, 0, 1));
    }
  
  if (pt.x() > _p[1].x()) {
      BOUNDSLOG (BoundsIncludesPt_Face_Check, new BoundsIncludesPt_Face_Check_Entry (*this, pt, 0, 1, 0));
      return 0;
  }
  else {
      BOUNDSLOG (BoundsIncludesPt_Face_Check, new BoundsIncludesPt_Face_Check_Entry (*this, pt, 0, 1, 1));
    }

  if (pt.y() < _p[0].y()) {
      BOUNDSLOG (BoundsIncludesPt_Face_Check, new BoundsIncludesPt_Face_Check_Entry (*this, pt, 1, 0, 0));
      return 0;
  } 
  else {
      BOUNDSLOG (BoundsIncludesPt_Face_Check, new BoundsIncludesPt_Face_Check_Entry (*this, pt, 1, 0, 1));
  }

  if (pt.y() > _p[1].y()) {
      BOUNDSLOG (BoundsIncludesPt_Face_Check, new BoundsIncludesPt_Face_Check_Entry (*this, pt, 1, 1, 0));
      return 0;
  } 
  else {
      BOUNDSLOG (BoundsIncludesPt_Face_Check, new BoundsIncludesPt_Face_Check_Entry (*this, pt, 1, 1, 1));
  }

  if (pt.z() < _p[0].z()) {
      BOUNDSLOG (BoundsIncludesPt_Face_Check, new BoundsIncludesPt_Face_Check_Entry (*this, pt, 2, 0, 0));
      return 0;
  } 
  else {
      BOUNDSLOG (BoundsIncludesPt_Face_Check, new BoundsIncludesPt_Face_Check_Entry (*this, pt, 2, 0, 1));
  }
  
  if (pt.z() > _p[1].z()) {
      BOUNDSLOG (BoundsIncludesPt_Face_Check, new BoundsIncludesPt_Face_Check_Entry (*this, pt, 2, 1, 0));
      return 0;
  }
  else
    {
      BOUNDSLOG (BoundsIncludesPt_Face_Check, new BoundsIncludesPt_Face_Check_Entry (*this, pt, 2, 1, 1));
    }
  return 1;
}

#ifdef OPENGL
void Bounds3d::drawFilled (void) const
{
  glBegin (GL_QUADS);
  //-z face
  glTexCoord2f (0,0);
   glVertex3f (x1(), y1(), z1());
  glTexCoord2f (0,1);
   glVertex3f (x1(), y2(), z1());
  glTexCoord2f (1,1);
   glVertex3f (x2(), y2(), z1());
  glTexCoord2f (1,0);
   glVertex3f (x2(), y1(), z1());

   //+z face
  glTexCoord2f (1,1);
   glVertex3f (x1(), y1(), z2());
  glTexCoord2f (0,1);
   glVertex3f (x2(), y1(), z2());
  glTexCoord2f (0,0);
   glVertex3f (x2(), y2(), z2());
   glTexCoord2f (1, 0);
   glVertex3f (x1(), y2(), z2());

   //-y face
   glTexCoord2f (0, 0);
   glVertex3f (x1(), y1(), z1());
   glTexCoord2f (1, 0);
   glVertex3f (x2(), y1(), z1());
   glTexCoord2f (1, 1);
   glVertex3f (x2(), y1(), z2());
   glTexCoord2f (0, 1);
   glVertex3f (x1(), y1(), z2());

   //+y face
   glTexCoord2f (0, 0);
   glVertex3f (x1(), y2(), z1());
   glTexCoord2f (0, 1);
   glVertex3f (x1(), y2(), z2());
   glTexCoord2f (1, 1);
   glVertex3f (x2(), y2(), z2());
   glTexCoord2f (1, 0);
   glVertex3f (x2(), y2(), z1());

   //-x face
   glTexCoord2f (0, 0);
   glVertex3f (x1(), y1(), z1());
   glTexCoord2f (0, 1);
   glVertex3f (x1(), y1(), z2());
   glTexCoord2f (1, 1);
   glVertex3f (x1(), y2(), z2());
   glTexCoord2f (1, 0);
   glVertex3f (x1(), y2(), z1());

   //+x face
   glTexCoord2f (0, 0);
   glVertex3f (x2(), y1(), z1());
   glTexCoord2f (1, 0);
   glVertex3f (x2(), y2(), z1());
   glTexCoord2f (1, 1);
   glVertex3f (x2(), y2(), z2());
   glTexCoord2f (0, 1);
   glVertex3f (x2(), y1(), z2());

   glEnd();
}

void Bounds3d::drawWireFrame ( void ) const
{
   // Draw twelve edges of axial box
   float xfudge = (x2() - x1()) * .05;
   float yfudge = (y2() - y1()) * .05;
   float zfudge = (z2() - z1()) * .05;

   glBegin (GL_LINES);
   //lines on the xy plane
   glVertex3f (x1() + xfudge, y1(), z1());
   glVertex3f (x2() - xfudge, y1(), z1());

   glVertex3f (x1() + xfudge, y2(), z1());
   glVertex3f (x2() - xfudge, y2(), z1());

   glVertex3f (x1() + xfudge, y1(), z2());
   glVertex3f (x2() - xfudge, y1(), z2());

   glVertex3f (x1() + xfudge, y2(), z2());
   glVertex3f (x2() - xfudge, y2(), z2());

   //lines on the yz plane
   glVertex3f (x1(), y1(), z1() + zfudge);
   glVertex3f (x1(), y1(), z2() - zfudge);

   glVertex3f (x1(), y2(), z1() + zfudge);
   glVertex3f (x1(), y2(), z2() - zfudge);

   glVertex3f (x2(), y1(), z1() + zfudge);
   glVertex3f (x2(), y1(), z2() - zfudge);

   glVertex3f (x2(), y2(), z1() + zfudge);
   glVertex3f (x2(), y2(), z2() - zfudge);

   //lines on the xz plane
   glVertex3f (x1(), y1() + yfudge, z1());
   glVertex3f (x1(), y2() - yfudge, z1());

   glVertex3f (x1(), y1() + yfudge, z2());
   glVertex3f (x1(), y2() - yfudge, z2());

   glVertex3f (x2(), y1() + yfudge, z1());
   glVertex3f (x2(), y2() - yfudge, z1());

   glVertex3f (x2(), y1() + yfudge, z2());
   glVertex3f (x2(), y2() - yfudge, z2());

   glEnd();
}

void Bounds3d::DrawXOnFace (int face, int direction) const
{
  switch (face)
    {
    case 0:
      glBegin (GL_LINES);
      if (direction == 0) //-X
	{
	  glVertex3f (x1(), y1(), z1());
	  glVertex3f (x1(), y2(), z2());

	  glVertex3f (x1(), y1(), z2());
	  glVertex3f (x1(), y2(), z1());
	}
      else //+X
	{
	  glVertex3f (x2(), y1(), z1());
	  glVertex3f (x2(), y2(), z2());

	  glVertex3f (x2(), y1(), z2());
	  glVertex3f (x2(), y2(), z1());
	}
      glEnd();
      break;
      
    case 1:
      glBegin (GL_LINES);
      if (direction == 0) //-Y
	{
	  glVertex3f (x1(), y1(), z1());
	  glVertex3f (x2(), y1(), z2());

	  glVertex3f (x1(), y1(), z2());
	  glVertex3f (x2(), y1(), z1());
	}
      else //+Y
	{
	  glVertex3f (x1(), y2(), z1());
	  glVertex3f (x2(), y2(), z2());

	  glVertex3f (x1(), y2(), z2());
	  glVertex3f (x2(), y2(), z1());
	}
      glEnd();
      break;

    case 2:
      glBegin (GL_LINES);
      if (direction == 0) //-Z
	{
	  glVertex3f (x1(), y1(), z1());
	  glVertex3f (x2(), y2(), z1());

	  glVertex3f (x1(), y2(), z1());
	  glVertex3f (x2(), y1(), z1());
	}
      else //+Z
	{
	  glVertex3f (x1(), y1(), z2());
	  glVertex3f (x2(), y2(), z2());

	  glVertex3f (x1(), y2(), z2());
	  glVertex3f (x2(), y1(), z2());
	}
      glEnd();
      break;
    default:
      cerr << "unknown face: " << face << endl;
      break;
    }
}

#endif

//returns 0 if no intersect and sets hit to be < 0 if we did a quick reject
//1 if we hit the box coming in
//2 if we hit the box going out (meaning we started inside of it)
//face = 0, 1, 2 for X, Y, Z
//direction = 0, 1 for -/+
int Bounds3d::Intersect(const Ray &ray, real &hit, int &face, int &direction) const {

#ifndef DISABLE_QUICK_REJECT
  //quick reject if the point is on the +X side of the max plane
  //and the ray is going in the +X direction (and similarly for the
  //other axes)
  hit = -1;

  Vec4 origin = ray.R();
  if (ray.Direction(0) >= 1)
    {
      if (origin.x() > _p[1].x()) return 0;
    }
  else if (ray.Direction(0) <= -1)
    {
      if (origin.x() < _p[0].x()) return 0;
    }


  if (ray.Direction(1) >= 1)
    {
      if (origin.y() > _p[1].y()) return 0;
    }
  else if (ray.Direction(1) <= -1)
    {
      if (origin.y() < _p[0].y()) return 0;
    }

  if (ray.Direction(2) >= 1)
    {
      if (origin.z() > _p[1].z()) return 0;
    }
  else if (ray.Direction(2) <= -1)
    {
      if (origin.z() < _p[0].z()) return 0;
    }

#endif
  hit = 1;

	float t_near = -MAXFLOAT;
	float t_far = MAXFLOAT;
	float t1, t2;

        //t1 = distance along ray to hit the low plane
        //t2 = distance along ray to hit the high plane

	float dir, pt;

	int nearface, farface;
	int neardir, fardir;
	face = -1; //start out invalid

	dir = ray.D().x();
	if (dir) {
                nearface = farface = 0;

		if (dir > 0.)
		  neardir = fardir = 1;
		else
		  neardir = fardir = 0;

	        pt = ray.R().x();

		t1 = (_p[0].x() - pt) / dir;
		t2 = (_p[1].x() - pt) / dir;

		if (t1 < t2) {
			t_near = t1;
			t_far = t2;
		}
		else {
			t_near = t2;
			t_far = t1;
		}
	}

	dir = ray.D().y();
	if (dir) {

	  pt = ray.R().y();

		t1 = (_p[0].y() - pt) / dir;
		t2 = (_p[1].y() - pt) / dir;

		if (t1 < t2) {
			if (t1 > t_near) 
			  {
			    t_near = t1;
			    if (dir > 0.)
			      neardir = 1;
			    else
			      neardir = 0;
			    nearface = 1;
			  }
			if (t2 < t_far) 
			  {
			    t_far = t2;
			    if (dir > 0.)
			      fardir = 1;
			    else
			      fardir = 0;
			    farface = 1;
			  }
		}
		else {
			if (t2 > t_near) 
			  {
			    t_near = t2;
			    if (dir > 0.)
			      neardir = 1;
			    else
			      neardir = 0;
			    nearface = 1;
			  }
			if (t1 < t_far) 
			  {
			    t_far = t1;
			    if (dir > 0.)
			      fardir = 1;
			    else
			      fardir = 0;
			    farface = 1;
			  }
		}

		if (t_near > t_far)
			return 0;
	}

	dir = ray.D().z();
	if (dir) {

	  pt = ray.R().z();

		t1 = (_p[0].z() - pt) / dir;
		t2 = (_p[1].z() - pt) / dir;

		if (t1 < t2) {
			if (t1 > t_near)
			  {
			    t_near = t1;
			    if (dir > 0.)
			      neardir = 1;
			    else
			      neardir = 0;
			    nearface = 2;
			  }
			if (t2 < t_far) 
			  {
			    t_far = t2;
			    if (dir > 0.)
			      fardir = 1;
			    else
			      fardir = 0;
			    farface = 2;
			  }
		}
		else {
			if (t2 > t_near) 
			  {
			    t_near = t2;
			    if (dir > 0.)
			      neardir = 1;
			    else
			      neardir = 0;
			    nearface = 2;
			  }
			if (t1 < t_far) 
			  {
			    t_far = t1;
			    if (dir > 0.)
			      fardir = 1;
			    else
			      fardir = 0;
			    farface = 2;

			  }
		}

		if (t_near > t_far)
			return 0;
	}

	if (t_near >= 0.0) {
		hit = t_near;
		face = nearface;
		direction = neardir;

		//keep us in sync with whereEntered
#ifdef TEST_INTERSECT
		float ttest;
		int facetest, dirtest;
		Vec4 pttest;
		whereEntered (ray, ttest, facetest, dirtest, pttest);
		assert (ttest == hit);
#endif
		return 1;
	}

	if (t_far >= 0.0) {
		hit = t_far;
		face = farface;
		direction = fardir;

		//keep us in sync with whereLeft
#ifdef TEST_INTERSECT
		float ttest;
		int facetest, dirtest;
		Vec4 pttest;
		whereLeft (ray, ttest, facetest, dirtest, pttest);
		assert (ttest == hit);
#endif
		return 2;
	}

	return 0;
}



//returns 0 if it misses the box altogether

//else
//tleft = t of where the ray enters the bounding box
//face and direction of where the ray enters
//direction == 1 if entering +face, == 0 if entering -face
int Bounds3d::whereEntered (const Ray &ray, float &tentered, int &face, int &direction, Vec4 &enteredpt) const
{
	float t_near = -MAXFLOAT;
	float t_far = MAXFLOAT;
	float t1, t2;

	float dir, pt;

	face = -1;

        //t1 = distance along ray to hit the low plane
        //t2 = distance along ray to hit the high plane

	BOUNDSLOG (BoundsWhereEntered_Start, new BoundsWhereEntered_Start_Entry (*this, ray.R(), ray.D()));

	dir = ray.D().x();
	if (dir) {

	  pt = ray.R().x();
		t1 = (_p[0].x() - pt) / dir;
		t2 = (_p[1].x() - pt) / dir;

		if (dir < 0.0)
		  direction = 1;
		else
		  direction = 0;

		if (t1 < t2) {
		  t_near = t1;
		  t_far = t2;
		  face = 0;
		  
		}
		else {
		  t_near = t2;
		  t_far = t1;
		  face = 0;
		}

		BOUNDSLOG (BoundsWhereEntered_Face_Project, new BoundsWhereEntered_Face_Project_Entry (0, (dir < 0.0), (t1 < t2) ? t1 : t2, (t1 < t2) ? t2 : t1, face, direction, t_near, t_far));
	}

	dir = ray.D().y();
	if (dir) {

	  pt = ray.R().y();
		t1 = (_p[0].y() - pt) / dir;
		t2 = (_p[1].y() - pt) / dir;

		if (t1 < t2) {
		  if (t1 > t_near) 
		    {
		      t_near = t1;
		      face = 1;
		      if (dir < 0.0)
			direction = 1;
		      else
			direction = 0;
		    }
		  if (t2 < t_far) t_far = t2;
		}
		else {
		  if (t2 > t_near)
		    {
		      t_near = t2;
		      face = 1;
		      if (dir < 0.0)
			direction = 1;
		      else
			direction = 0;

		    }
		  if (t1 < t_far) t_far = t1;
		}

		BOUNDSLOG (BoundsWhereEntered_Face_Project, new BoundsWhereEntered_Face_Project_Entry (1, (dir < 0.0), (t1 < t2) ? t1 : t2, (t1 < t2) ? t2 : t1, face, direction, t_near, t_far));

		if (t_near > t_far)
		  {
		    BOUNDSLOG (BoundsWhereEntered_End, new BoundsWhereEntered_End_Entry (Missed, 1, (dir > 0.0), NULL));
		    return 0;
		  }
	}

	dir = ray.D().z();
	if (dir) {
		t1 = (_p[0].z() - ray.R().z()) / dir;
		t2 = (_p[1].z() - ray.R().z()) / dir;

		if (t1 < t2) {
			if (t1 > t_near)
			  {
			    t_near = t1;
			    face = 2;
			    if (dir < 0.0)
			      direction = 1;
			    else
			      direction = 0;
			  }
			if (t2 < t_far) t_far = t2;
		}
		else {
			if (t2 > t_near)
			  {
			    t_near = t2;
			    face = 2;
			    if (dir < 0.0)
			      direction = 1;
			    else
			      direction = 0;
			  }
			if (t1 < t_far) t_far = t1;
		}

		BOUNDSLOG (BoundsWhereEntered_Face_Project, new BoundsWhereEntered_Face_Project_Entry (2, (dir < 0.0), (t1 < t2) ? t1 : t2, (t1 < t2) ? t2 : t1, face, direction, t_near, t_far));

		if (t_near > t_far)
		  {
		    BOUNDSLOG (BoundsWhereEntered_End, new BoundsWhereEntered_End_Entry (Missed, 2, (dir > 0.0), NULL));
		    return 0;
		  }
	}

       if (t_near < 0.0)
         {
         BOUNDSLOG (BoundsWhereEntered_End, new BoundsWhereEntered_End_Entry (Behind, -1, -1, NULL));
         return 0;
         }

	tentered = t_near;

	//compute the point the usual way...
	enteredpt = ray.R() + tentered * ray.D();

	//...but we can set one of its members *exactly*.
	//note: direction is not the ray direction but the face "direction"
	//+1 is the max face, 0 is the min face
	switch (face)
	  {
	  case 0:
	    enteredpt.set_x ((direction == 0) ? _p[0].x() : _p[1].x());
	    break;
	  case 1:
	    enteredpt.set_y ((direction == 0) ? _p[0].y() : _p[1].y());
	    break;	
	  case 2:
	    enteredpt.set_z ((direction == 0) ? _p[0].z() : _p[1].z());
	    break;
	  default:
	    assert (1==0);
	    break;
	  }

         BOUNDSLOG (BoundsWhereEntered_End, new BoundsWhereEntered_End_Entry (FoundHit, face, direction, &enteredpt));
	return 1;
}


void Bounds3d::ClipToBbox (const Bounds3d &insideme)
{
  //move in mins
  if (_p[0].x() < insideme._p[0].x())
    _p[0].set_x (insideme._p[0].x());
  if (_p[0].y() < insideme._p[0].y())
    _p[0].set_y (insideme._p[0].y());
  if (_p[0].z() < insideme._p[0].z())
    _p[0].set_z (insideme._p[0].z());

  //move in maxs
  if (_p[1].x() > insideme._p[1].x())
    _p[1].set_x (insideme._p[1].x());
  if (_p[1].y() > insideme._p[1].y())
    _p[1].set_y (insideme._p[1].y());
  if (_p[1].z() > insideme._p[1].z())
    _p[1].set_z (insideme._p[1].z());
}

void Bounds3d::ClipToFrontOfRay (const Vec4 &pt, const Vec4 &dir)
{
  real maxaxis = -MAXFLOAT;
  real current;
  int primary_axis;
  int direction;
  for (int i = 0; i < 3; i++)
    {
      current = dir[i];

      if (current > 0.)
	{
	  if (current > maxaxis)
	    {
	      maxaxis = current;
	      primary_axis = i;
	      direction = 1;
	    }
	}
      else if (current < 0.)
	{
	  if ((-current) > maxaxis)
	    {
	      maxaxis = -current;
	      primary_axis = i;
	      direction = -1;
	    }
	}
    }

  //if we're primarily going in the X direction, clip on X
  if (primary_axis == 0) 
    {
      if (direction == 1) 
	{
	if (_p[0].x() < pt.x())
	  _p[0].set_x (pt.x());
	if (_p[1].x() < pt.x())
	  _p[1].set_x (pt.x());
	}
      else
	{
	if (_p[0].x() > pt.x())
	  _p[0].set_x (pt.x());
	if (_p[1].x() > pt.x())
	  _p[1].set_x (pt.x());
	}
    }
  else if (primary_axis == 1)
    {
      if (direction == 1) 
	{
	  if (_p[0].y() < pt.y())
	    _p[0].set_y (pt.y());
	  if (_p[1].y() < pt.y())
	    _p[1].set_y (pt.y());
	}
      else
	{
	  if (_p[0].y() > pt.y())
	    _p[0].set_y (pt.y());
	  if (_p[1].y() > pt.y())
	    _p[1].set_y (pt.y());
	}
    }
  else
    {
      if (direction == 1) 
	{
	  if (_p[0].z() < pt.z())
	    _p[0].set_z (pt.z());
	  if (_p[1].z() < pt.z())
	    _p[1].set_z (pt.z());
	}
      else
	{
	  if (_p[0].z() > pt.z())
	    _p[0].set_z (pt.z());
	  if (_p[1].z() > pt.z())
	    _p[1].set_z (pt.z());
	}

  }
}

#define EPS 10E-4

// fill in 6 halfspaces whose intersection is
// identical to the specified axial bounding box
void Bounds3d::FillHalfspaces ( Plane halves[6] ) const
{
    /* extent xmin: x - xmin = 0 */
    halves[0].Set ( 1.0, 0.0, 0.0, -(xmin()-EPS) );

    /* extent xmax: xmax - x = 0 */
    halves[3].Set (-1.0, 0.0, 0.0, xmax()+EPS );

	/* extent ymin: y - ymin = 0 */
    halves[1].Set ( 0.0, 1.0, 0.0, -(ymin()-EPS) );

	/* extent ymax: ymax - x = 0 */
    halves[4].Set ( 0.0,-1.0, 0.0,  ymax()+EPS );

	/* extent zmin: z - zmin = 0 */
    halves[2].Set ( 0.0, 0.0, 1.0, -(zmin()-EPS) );

	/* extent zmax: zmax - z = 0 */
    halves[5].Set ( 0.0, 0.0,-1.0,  zmax()+EPS );
}

