#include "stdafx.h"
#include "realbox.h"
#include "randomizer.h" //For cRandomizer in randomVector
#include "sprite.h" //For the cColorStyle::CN_ color names.
#include "graphics.h"
#include "spritepolygon.h"
#include "critter.h"
#include "game.h" //for cGame::BACKGROUNDOFFSET

IMPLEMENT_SERIAL(cRealBox2, CObject, 0);
IMPLEMENT_SERIAL(cRealBox3, CObject, 0);

//----TWO DIMENSIONAL -----
//Private field mutators================
void cRealBox2::_arrange()
{ //Helper function
	Real temp;
	if (_lox > _hix)
	{
		temp = _lox;
		_lox = _hix;
		_hix = temp;
	}
	if (_loy > _hiy)  //In Real land loy IS < hiy.
	{
		temp = _loy;
		_loy = _hiy;
		_hiy = temp;
	}
}

void cRealBox2::_initialize(Real px, Real py, Real qx, Real qy)
{
	if (!_pcolorstyle)
		_pcolorstyle = new cColorStyle();
	_lox = px;
	_loy = py;
	_hix = qx;
	_hiy = qy;
	_arrange();
}

void cRealBox2::_copy(const cRealBox2 &box)
{
	if (_pcolorstyle)
		delete _pcolorstyle;
	_pcolorstyle = new cColorStyle(box.pcolorstyle());
	_initialize(box.lox(), box.loy(), box.hix(), box.hiy());
}

void cRealBox2::_copy(const cRealBox3 &box)
{
	if (_pcolorstyle)
		delete _pcolorstyle;
	_pcolorstyle = new cColorStyle(box.pcolorstyle());
	_initialize(box.lox(), box.loy(), box.hix(), box.hiy());
}

//==============Constructors===============

cRealBox2::cRealBox2(Real xsize, Real ysize, Real zsize):_pcolorstyle(NULL)
{
	set(xsize, ysize, zsize);
}

cRealBox2::cRealBox2(const cVector2 &locorner, const cVector2 &hicorner):_pcolorstyle(NULL)
{
	set(locorner, hicorner);
}

cRealBox2::cRealBox2(const cVector2 &center, Real xsize,
	Real ysize, Real zsize):_pcolorstyle(NULL)
{
	set(center, xsize, ysize, zsize);
}

cRealBox2::cRealBox2(const cVector2 &center, Real edge):_pcolorstyle(NULL)
{
	set(center, edge, edge);
}

void cRealBox2::Serialize(CArchive &ar)
{
	if (ar.IsStoring()) //Writing data.
		ar << _lox << _hix << _loy << _hiy << _pcolorstyle;
	else //Reading data.
	{
 		delete _pcolorstyle;
 			//always delete a pointer before reading into it or you have a leak.
		ar >> _lox >> _hix >> _loy >> _hiy >> _pcolorstyle;
	}
}

CArchive& operator<<(CArchive& ar, cRealBox2 &realbox)
{
	realbox.Serialize(ar);
	return ar;
}

CArchive& operator>>(CArchive& ar, cRealBox2 &realbox)
{
	realbox.Serialize(ar);
	return ar;
}

//Field Mutators===================

void cRealBox2::set(const cVector2 &locorner, const cVector2 &hicorner)
{
	_initialize(locorner.x(), locorner.y(), hicorner.x(), hicorner.y());
}

void cRealBox2::set(const cVector2 &center, Real xsize, Real ysize, Real zsize)
{
	if (xsize < 0.0)
		xsize = -xsize;
	if (ysize < 0.0)
		ysize = -ysize;
	Real lox, loy, hix, hiy;
	lox = center.x() - xsize/2.0;
	loy = center.y() - ysize/2.0;
	hix = center.x() + xsize/2.0;
	hiy = center.y() + ysize/2.0;
	_initialize(lox, loy, hix, hiy);
}

void cRealBox2::set(Real xsize, Real ysize, Real zsize)
{
	set(cVector2::ZEROVECTOR, xsize, ysize, zsize);
}

//Mutators

void cRealBox2::matchAspect(int cx, int cy)
{
	Real oldaspect, newaspect, centery;

	if (!(_hiy - _loy) || !cy)
		return;
	oldaspect = (_hix - _lox)/(_hiy - _loy);
	if (!oldaspect)
		return;
	newaspect = (Real)cx/(Real)cy;
	if (!newaspect)
		return;
	centery = (_hiy - _loy)/2.0;
	//stretch or shrink the box vertically
	_hiy = centery + (oldaspect/newaspect)*(_hiy - centery);
	_loy = centery - (oldaspect/newaspect)*(centery - _loy);
/*  If we instead wanted to stretch or shrink the box horizontally:
	_hix = centerx + (newaspect/oldaspect)*(_hix - centerx);
	_lox = centerx - (newaspect/oldaspect)*(centerx - _lox);
*/
}

cRealBox2 cRealBox2::innerBox(Real radius)const
{ // Return a box offset inward by radius
	Real xoffset, yoffset;
	xoffset = yoffset = radius;
	CLAMP(xoffset, 0.0, xsize()/2.0); //Don't offset further than there's room for.
	CLAMP(yoffset, 0.0, ysize()/2.0); 
	cRealBox2 returnbox(*this);
	returnbox._initialize(_lox+xoffset, _loy+yoffset, _hix-xoffset, _hiy-yoffset);
	return returnbox;
}

cRealBox2 cRealBox2::outerBox(Real radius)const
{ // Return a box offset outward by radius
	cRealBox2 returnbox(*this);
	returnbox._initialize(_lox-radius, _loy-radius, _hix+radius, _hiy+radius);
	return returnbox;
}

cVector2 cRealBox2::randomVector()const
{
	return cVector2(cRandomizer::pinstance()->randomReal(_lox, _hix),
		cRandomizer::pinstance()->randomReal(_loy, _hiy));
}

BOOL cRealBox2::inside(const cVector2 &testpos)const
{
	return (_lox <= testpos.x() && testpos.x() <= _hix &&
		_loy <= testpos.y() && testpos.y() <= _hiy);
}

int cRealBox2::outcode(const cVector2 &testpos) const
{ /* This tells you which of the nine possible positions testpos has
relative to the cRealBox2 */
	int outcode = BOX_INSIDE; //This is 0.

	if (testpos.x() < _lox)
		outcode |= BOX_LOX;
	if (testpos.x() > _hix)
		outcode |= BOX_HIX;
	if (testpos.y() < _loy)
		outcode |= BOX_LOY;
	if (testpos.y() > _hiy)
		outcode |= BOX_HIY;
	return outcode;
}

Real cRealBox2::distanceTo(const cVector2 &testpos) const
{ /*This gives the distance from testpos to the closest point of the
cRealBox3.  If you are in a "side" zone your nearest distance is a point
on the side.  If you are in a "corner" zone your nearest distance is a
corner.  If you are inside, we call the distance 0.*/
	int posoutcode = outcode(testpos);
	Real dx, dy;
	dx=dy=0.0;
	if (posoutcode & BOX_LOX)
		dx = _lox - testpos.x();
	if (posoutcode & BOX_HIX)
		dx = testpos.x() - _hix;
	if (posoutcode & BOX_LOY)
		dy = _loy - testpos.y();
	if (posoutcode & BOX_HIY)
		dy = testpos.y() - _hiy;
	return sqrt(dx*dx + dy*dy);
}

Real cRealBox2::maxDistanceToCorner(const cVector2 &testpos) const
{ /*This gives the distance from testpos to the furthest corner of the
cRealBox2. */
	int posoutcode = outcode(testpos);
	Real dx, dy;
	if (posoutcode & BOX_LOX)
		dx = _hix - testpos.x();
	else if (posoutcode & BOX_HIX)
		dx = testpos.x() - _lox;
	else
		dx = __max(testpos.x() - _lox, _hix - testpos.x());
	if (posoutcode & BOX_LOY)
		dy = _hiy - testpos.y();
	else if (posoutcode & BOX_HIY)
		dy = testpos.y() - _loy;
	else
		dy = __max(testpos.y() - _loy, _hiy - testpos.y());
	return sqrt(dx*dx + dy*dy);
}

cVector2 cRealBox2::corner(int i)const
{
	ASSERT(0 <= i && i <= 3);
	switch(i)
	{
		case 0:
			return cVector2(_lox, _loy);
		case 1:
			return cVector2(_lox, _hiy);
		case 2:
			return cVector2(_hix, _loy);
		default: //case 3
			return cVector2(_hix, _hiy);
	}
}

int cRealBox2::wrap(cVector2 &position) const
{
	int outcode = BOX_INSIDE;
	if (position.x() < _lox)
	{
		outcode |= BOX_LOX;
		position.set(_hix - _lox + position.x(), position.y());
	}
	if (position.x() > _hix)
	{
		outcode |= BOX_HIX;
		position.set(_lox - _hix + position.x(), position.y());
	}
	if (position.y() < _loy)
	{
		outcode |= BOX_LOY;
		position.set(position.x(), _hiy - _loy + position.y());
	}
	if (position.y() > _hiy)
	{
		outcode |= BOX_HIY;
		position.set(position.x(), _loy - _hiy + position.y());
	}
	return outcode;
}

int cRealBox2::wrap(cVector2 &position, cVector2 &wrapposition1,
	 cVector2 &wrapposition2, cVector2 &wrapposition3, Real radius) const
{
	wrapposition3 = wrapposition2 = wrapposition1 = position;
	int outcode = wrap(position);
	cRealBox2 smallbox = this->innerBox(radius);
//First wrap the x
	if (position.x() < smallbox._lox)
	{
		outcode |= BOX_LOX;
		wrapposition1.set(_hix - _lox + position.x(),position.y());
	}
	if (position.x() > smallbox._hix)
	{
		outcode |= BOX_HIX;
		wrapposition1.set(_lox - _hix + position.x(), position.y());
	}
//If there's no x wrap, wrap the y for wrapposition1
	if (wrapposition1 == position)
	{
		if (position.y() < smallbox._loy)
		{
			outcode |= BOX_LOY;
			wrapposition1.set(position.x(), _hiy - _loy + position.y());
		}
		if (position.y() > smallbox._hiy)
		{
			outcode |= BOX_HIY;
			wrapposition1.set(position.x(), _loy - _hiy + position.y());
		}
	}
//If there's x wrap in wrapposition1, do the y wrap of position and wrapposition1
	else //wrapposition1 not equal to position
	{
		if (position.y() < smallbox._loy)
		{
			outcode |= BOX_LOY;
			wrapposition2.set(position.x(), _hiy - _loy + position.y());
			wrapposition3.set(wrapposition1.x(), _hiy - _loy + position.y());
		}
		if (position.y() > smallbox._hiy)
		{
			outcode |= BOX_HIY;
			wrapposition2.set(position.x(), _loy - _hiy + position.y());
			wrapposition3.set(wrapposition1.x(), _loy - _hiy + position.y());
		}
	}
	//Don't bother doing this for z.
	return outcode;
}

int cRealBox2::clamp(cVector2 &position) const
{
	int outcode = BOX_INSIDE;
	if (position.x() < _lox)
	{
		outcode |= BOX_LOX;
		position.set(_lox, position.y());
	}
	if (position.x() > _hix)
	{
		outcode |= BOX_HIX;
		position.set(_hix, position.y());
	}
	if (position.y() < _loy)
	{
		outcode |= BOX_LOY;
		position.set(position.x(), _loy);
	}
	if (position.y() > _hiy)
	{
		outcode |= BOX_HIY;
		position.set(position.x(), _hiy);
	}
	return outcode;
}

int cRealBox2::clamp(cVector2 &position, cVector2 &velocity) const
{
	int outcode = clamp(position);
	if (outcode & BOX_LOX) //If you hit a LO X wall, zero out any neg X velocity.
		velocity.set(__max(0.0, velocity.x()), velocity.y());
	if (outcode & BOX_HIX) //If you hit an HI X wall, zero out any pos X velocity.
		velocity.set(__min(0.0, velocity.x()), velocity.y());
	if (outcode & BOX_LOY) //Same deal for Y.
		velocity.set( velocity.x(), __max(0.0, velocity.y()) );
	if (outcode & BOX_HIY) 
		velocity.set( velocity.x(), __min(0.0, velocity.y()) );
	return outcode;
}

int cRealBox2::addBounce(cVector2 &position, cVector2 &velocity, Real bounciness)const
{
	int outcode = BOX_INSIDE;

	position += velocity;
	/* The basic idea is to reverse appropriate velocity component if you've passed an edge,
	and reflect the motion past the edge into motion away from the edge.  Reflection means
	at the right newx would be _hix - (position.x()-_hix), or 2*_hix - position.x()
	and at the newx would be _lox + (_lox - position.x()), or 2*_lox - position.x(). 
		Now add in the notion of a bounciness between 0.0 and 1.0.  First of all the
	reflected newvel is attentuated to -bounciness*oldvel.  Second, the reflected
	position should take into account the fact that the object is moving slower after
	the reflection, so we get, on the right, 
	newx  = _hix - bounciness*(position.x()-_hix) and on the left 
	newx = _lox + bounciness*(_lox - position.x())	*/
#ifdef USEBOUNCINESS
	if (position.x() > _hix)
	{
		velocity.set(-bounciness*velocity.x(), velocity.y());
		position.set(_hix - bounciness*(position.x()-_hix), position.y());
		outcode |= BOX_HIX;
	}
	if (position.x() < _lox)
	{
     	velocity.set(-bounciness*velocity.x(), velocity.y());
		position.set(_lox - bounciness*(position.x()-_lox), position.y());
		outcode |= BOX_LOX;
	}
	if (position.y() > _hiy)
	{
     	velocity.set(velocity.x(), -bounciness*velocity.y());
		position.set(position.x(), _hiy - bounciness*(position.y()-_hiy));
		outcode |= BOX_HIY;
	}
	if (position.y() < _loy)
	{
     	velocity.set(velocity.x(), -bounciness*velocity.y());
		position.set(position.x(), _loy - bounciness*(position.y()-_loy));
		outcode |= BOX_LOY;
	}
	if (outcode != BOX_INSIDE)
		clamp(position); /* Just so some screwy high velocity bounce can't 
			leave you outside the box */
    return outcode;
#else //old way, not USEBOUNCINESS
	if (position.x() > _hix)
	{
		velocity.set(-velocity.x(), velocity.y());
		position.set(2*_hix - position.x(), position.y());
		outcode |= BOX_HIX;
	}
	if (position.x() < _lox)
	{
     	velocity.set(-velocity.x(), velocity.y());
		position.set(2*_lox - position.x(), position.y());
		outcode |= BOX_LOX;
	}
	if (position.y() > _hiy)
	{
     	velocity.set(velocity.x(), -velocity.y());
		position.set(position.x(), 2*_hiy - position.y());
		outcode |= BOX_HIY;
	}
	if (position.y() < _loy )
	{
     	velocity.set(velocity.x(), -velocity.y());
		position.set(position.x(), 2*_loy - position.y());
		outcode |= BOX_LOY;
	}
	if (outcode != BOX_INSIDE)
		clamp(position); /* Just so some screwy high velocity bounce can't 
			leave you outside the box */
    return outcode;
#endif //USEBOUNCINESS
}

int cRealBox2::addBounce(cVector2 &position, cVector2 &velocity, Real bounciness, Real dt)const
{
	if (fabs(dt)<SMALL_REAL)
		return BOX_INSIDE; //You're not moving.
	cVector2 dtvelocity = dt*velocity;
	int outcode = addBounce(position, dtvelocity, bounciness);
	velocity = dtvelocity/dt;
	return outcode;
}

/*CRect cRealBox2::realToPixel(const cRealPixelConverter &crealpixelconverter)const
{
	int left, top, right, bottom;	
	crealpixelconverter.realToPixel(_lox, _loy, &left, &bottom);
	crealpixelconverter.realToPixel(_hix, _hiy, &right, &top);
	return CRect(left, top, right, bottom);
}
*/

void cRealBox2::draw(cGraphics *pgraphics, int drawflags)
{ 
//#define BOXASPOLY
#ifdef BOXASPOLY
	cVector pverts[4] = {cVector(_lox, _loy), cVector(_hix, _loy), 
		cVector(_hix, _hiy), cVector(_lox, _hiy)}; /* We don't specify cVector2 or cVector3 here
			because the cPolygon constructor uses whichever type is currently active. */
	cPolygon rectpoly(4, pverts, _pcolorstyle);
	rectpoly.setPrismDz(0.001); //Give it a slight thickness so you draw front and back faces.
	rectpoly.draw(pgraphics, drawflags); /* This is too slow in large images.  Instead
		switch to a clear method. */
 /* This seems to sometimes be a speed bottleneck 
			when I have a large pixelsize of the view and a lot of bits per pixel.  I've
			seen problems for instance at 1000 by 700 pixels with 24 bits per pixel. */
#else
	pgraphics->drawrectangle(locorner(), hicorner(), _pcolorstyle);
#endif //BOXASPOLY
}

cRealBox2 operator*(Real scale, const cRealBox2 &box)
{
	return cRealBox2(box.center(), scale*box.xsize(),
		scale*box.ysize());
}

BOOL operator==(const cRealBox2 arect, const cRealBox2 brect)
{
	return (arect._lox==brect._lox && arect._loy==brect._loy && 
		arect._hix==brect._hix && arect._hiy==brect._hiy);
}

BOOL operator!=(const cRealBox2 arect, const cRealBox2 brect)
{ return !(arect==brect);}

//3D =============================================================

//Private field mutators================
void cRealBox3::_arrange()
{ //Helper function
	Real temp;
	if (_lox > _hix)
	{
		temp = _lox;
		_lox = _hix;
		_hix = temp;
	}
	if (_loy > _hiy)  
	{
		temp = _loy;
		_loy = _hiy;
		_hiy = temp;
	}
	if (_loz > _hiz)  
	{
		temp = _loz;
		_loz = _hiz;
		_hiz = temp;
	}
}


void cRealBox3::_initialize(Real px, Real py, Real pz, Real qx, Real qy, Real qz)
{
	if (!_pcolorstyle)
		_pcolorstyle = new cColorStyle();
	_lox = px;
	_loy = py;
	_loz = pz;
	_hix = qx;
	_hiy = qy;
	_hiz = qz;
	_arrange();
}

void cRealBox3::_copy(const cRealBox2 &box)
{
	if (_pcolorstyle)
		delete _pcolorstyle;
	_pcolorstyle = new cColorStyle(box.pcolorstyle());
	_initialize(box.lox(), box.loy(), 0.0, box.hix(), box.hiy(), 0.0);
}

void cRealBox3::_copy(const cRealBox3 &box)
{
	if (_pcolorstyle)
		delete _pcolorstyle;
	_pcolorstyle = new cColorStyle(box.pcolorstyle());
	_initialize(box.lox(), box.loy(), box.loz(), box.hix(), box.hiy(), box.hiz());
}

//==============Constructors===============

cRealBox3::cRealBox3(Real xsize, Real ysize, Real zsize):_pcolorstyle(NULL)
{
	set(xsize, ysize, zsize);
}

cRealBox3::cRealBox3(const cVector3 &locorner, const cVector3 &hicorner):_pcolorstyle(NULL)
{
	set(locorner, hicorner);
}

cRealBox3::cRealBox3(const cVector3 &center, Real xsize,
	Real ysize, Real zsize):_pcolorstyle(NULL)
{
	set(center, xsize, ysize, zsize);
}

cRealBox3::cRealBox3(const cVector3 &center, Real edge):_pcolorstyle(NULL)
{
	set(center, edge, edge, edge);
}

void cRealBox3::Serialize(CArchive &ar)
{
	if (ar.IsStoring()) //Writing data.
		ar << _lox << _hix << _loy << _hiy << _loz << _hiz << _pcolorstyle;
	else //Reading data.
	{
 		delete _pcolorstyle;
		_pcolorstyle = NULL;
 			//always delete a pointer before reading into it or you have a leak.
		ar >> _lox >> _hix >> _loy >> _hiy >> _loz >> _hiz >> _pcolorstyle;
	}
}

CArchive& operator<<(CArchive& ar, cRealBox3 &realbox)
{
	realbox.Serialize(ar);
	return ar;
}

CArchive& operator>>(CArchive& ar, cRealBox3 &realbox)
{
	realbox.Serialize(ar);
	return ar;
}

//Field Mutators===================

void cRealBox3::set(const cVector3 &locorner, const cVector3 &hicorner)
{
	_initialize(locorner.x(), locorner.y(), locorner.z(), hicorner.x(), hicorner.y(), hicorner.z());
}

void cRealBox3::set(const cVector3 &center, Real xsize, Real ysize, Real zsize)
{
	if (xsize < 0.0)
		xsize = -xsize;
	if (ysize < 0.0)
		ysize = -ysize;
	if (zsize < 0.0)
		zsize = -zsize;
	Real lox, loy, hix, hiy, loz, hiz;
	lox = center.x() - xsize/2.0;
	loy = center.y() - ysize/2.0;
	loz = center.z() - zsize/2.0;
	hix = center.x() + xsize/2.0;
	hiy = center.y() + ysize/2.0;
	hiz = center.z() + zsize/2.0;
	_initialize(lox, loy, loz, hix, hiy, hiz);
}

void cRealBox3::set(Real xsize, Real ysize, Real zsize)
{
	set(cVector3::ZEROVECTOR, xsize, ysize, zsize);
}

//Other mutators  

void cRealBox3::matchAspect(int cx, int cy)
{
	Real oldaspect, newaspect, centery;

	if (!(_hiy - _loy) || !cy)
		return;
	oldaspect = (_hix - _lox)/(_hiy - _loy);
	if (!oldaspect)
		return;
	newaspect = (Real)cx/(Real)cy;
	if (!newaspect)
		return;
	centery = (_hiy - _loy)/2.0;
	//stretch or shrink the box vertically
	_hiy = centery + (oldaspect/newaspect)*(_hiy - centery);
	_loy = centery - (oldaspect/newaspect)*(centery - _loy);
/*  If we instead wanted to stretch or shrink the box horizontally:
	_hix = centerx + (newaspect/oldaspect)*(_hix - centerx);
	_lox = centerx - (newaspect/oldaspect)*(centerx - _lox);
*/
}

Real cRealBox3::minsize()const
{
	if (zsize())
		return __min(__min(xsize(), ysize()), zsize());
	else
		return __min(xsize(), ysize());
}

cRealBox3 cRealBox3::innerBox(Real radius)const
{ // Return a box offset inward by radius, but don't offset more than would fit.
	Real xoffset, yoffset, zoffset;
	xoffset = yoffset = zoffset = radius;
	CLAMP(xoffset, 0.0, xsize()/2.0); //Don't offset further than there's room for.
	CLAMP(yoffset, 0.0, ysize()/2.0); 
	CLAMP(zoffset, 0.0, zsize()/2.0); 
	cRealBox3 returnbox(*this);
	returnbox._initialize(_lox+xoffset, _loy+yoffset, _loz+zoffset,
		_hix-xoffset, _hiy-yoffset, _hiz-zoffset);
	return returnbox;
}

cRealBox3 cRealBox3::outerBox(Real radius)const
{ // Return a box offset outward by radius
	cRealBox3 returnbox(*this);
	returnbox._initialize(_lox-radius, _loy-radius, _loz-radius,
		 _hix+radius, _hiy+radius, _hiz+radius);
	return returnbox;
}

cVector3 cRealBox3::randomVector()const
{
	return cVector3(cRandomizer::pinstance()->randomReal(_lox, _hix),
	 cRandomizer::pinstance()->randomReal(_loy, _hiy),
	 cRandomizer::pinstance()->randomReal(_loz, _hiz));
}

BOOL cRealBox3::inside(const cVector3 &testpos)const
{
	return (_lox <= testpos.x() && testpos.x() <= _hix &&
		_loy <= testpos.y() && testpos.y() <= _hiy &&
		_loz <= testpos.z() && testpos.z() <= _hiz );
}

int cRealBox3::outcode(const cVector3 &testpos) const
{ /* This tells you which of the nine possible positions testpos has
relative to the cRealBox3 */
	int outcode = BOX_INSIDE; //This is 0.

	if (testpos.x() < _lox)
		outcode |= BOX_LOX;
	if (testpos.x() > _hix)
		outcode |= BOX_HIX;
	if (testpos.y() < _loy)
		outcode |= BOX_LOY;
	if (testpos.y() > _hiy)
		outcode |= BOX_HIY;
	if (testpos.z() < _loz)
		outcode |= BOX_LOZ;
	if (testpos.z() > _hiz)
		outcode |= BOX_HIZ;
	return outcode;
}

Real cRealBox3::distanceTo(const cVector3 &testpos) const
{ /*This gives the distance from testpos to the closest point of the
cRealBox3.  If you are in a "side" zone your nearest distance is a point
on the side.  If you are in a "corner" zone your nearest distance is a
corner.  If you are inside, we call the distance 0.*/
	int posoutcode = outcode(testpos);
	Real dx, dy, dz;
	dx=dy=dz=0.0;
	if (posoutcode & BOX_LOX)
		dx = _lox - testpos.x();
	if (posoutcode & BOX_HIX)
		dx = testpos.x() - _hix;
	if (posoutcode & BOX_LOY)
		dy = _loy - testpos.y();
	if (posoutcode & BOX_HIY)
		dy = testpos.y() - _hiy;
	if (posoutcode & BOX_LOZ)
		dz = _loz - testpos.z();
	if (posoutcode & BOX_HIZ)
		dz = testpos.z() - _hiz;
	return sqrt(dx*dx + dy*dy + dz*dz);
}

Real cRealBox3::maxDistanceToCorner(const cVector3 &testpos) const
{ /*This gives the distance from testpos to the furthest corenr of the
cRealBox3. */
	int posoutcode = outcode(testpos);
	Real dx, dy, dz;
	if (posoutcode & BOX_LOX)
		dx = _hix - testpos.x();
	else if (posoutcode & BOX_HIX)
		dx = testpos.x() - _lox;
	else
		dx = __max(testpos.x() - _lox, _hix - testpos.x());
	if (posoutcode & BOX_LOY)
		dy = _hiy - testpos.y();
	else if (posoutcode & BOX_HIY)
		dy = testpos.y() - _loy;
	else
		dy = __max(testpos.y() - _loy, _hiy - testpos.y());
	if (posoutcode & BOX_LOZ)
		dz = _hiz - testpos.z();
	else if (posoutcode & BOX_HIZ)
		dz = testpos.z() - _loz;
	else
		dz = __max(testpos.z() - _loz, _hiz - testpos.z());
	return sqrt(dx*dx + dy*dy + dz*dz);
}

cVector3 cRealBox3::corner(int i)const
{
	ASSERT(0 <= i && i <= 7);
	switch(i)
	{
		case 0:
			return cVector3(_lox, _loy ,_loz);
		case 1:
			return cVector3(_lox, _loy ,_hiz);
		case 2:
			return cVector3(_lox, _hiy ,_loz);
		case 3:
			return cVector3(_lox, _hiy ,_hiz);
		case 4:
			return cVector3(_hix, _loy ,_loz);
		case 5:
			return cVector3(_hix, _loy ,_hiz);
		case 6:
			return cVector3(_hix, _hiy ,_loz);
		default: //case 7
			return cVector3(_hix, _hiy ,_hiz);
	}
}

int cRealBox3::wrap(cVector3 &position) const
{
	/* There's a slight chance you might have a very thin world where a critter
has raced out so far past the edge that if you wrap it, then the wrapped position
is still past the edge. This would be particulary likely if you have a zero
zsize cRealBox3.  In this wrap code, we call our CLAMP macro after 
finding our wrapped positions. */
	int outcode = BOX_INSIDE;
	Real newx, newy, newz;
	newx = position.x(); newy = position.y(); newz = position.z();
	if (position.x() < _lox)
	{
		outcode |= BOX_LOX;
		newx = _hix - _lox + position.x();
		CLAMP(newx, _lox, _hix);
	}
	else if (position.x() > _hix)
	{
		outcode |= BOX_HIX;
		newx = _lox - _hix + position.x();
		CLAMP(newx, _lox, _hix);
	}
	if (position.y() < _loy)
	{
		outcode |= BOX_LOY;
		newy = _hiy - _loy + position.y();
		CLAMP(newy, _loy, _hiy);
	}
	else if (position.y() > _hiy)
	{
		outcode |= BOX_HIY;
		newy = _loy - _hiy + position.y();
		CLAMP(newy, _loy, _hiy);
	}
	if (position.z() < _loz)
	{
		outcode |= BOX_LOZ;
		newz = _hiz - _loz + position.z();
		CLAMP(newz, _loz, _hiz);
	}
	else if (position.z() > _hiz)
	{
 		outcode |= BOX_HIZ;
		newz =  _loz - _hiz + position.z();
		CLAMP(newz, _loz, _hiz);
	}
	position.set(newx, newy, newz);
	return outcode;
}

int cRealBox3::wrap(cVector3 &position, cVector3 &wrapposition1,
	 cVector3 &wrapposition2, cVector3 &wrapposition3, Real radius) const
{
	wrapposition3 = wrapposition2 = wrapposition1 = position;
	int outcode = wrap(position);
	cRealBox3 smallbox = this->innerBox(radius);
//First wrap the x
	if (position.x() < smallbox._lox)
	{
		outcode |= BOX_LOX;
		wrapposition1.set(_hix - _lox + position.x(),position.y(),position.z());
	}
	if (position.x() > smallbox._hix)
	{
		outcode |= BOX_HIX;
		wrapposition1.set(_lox - _hix + position.x(), position.y(),position.z());
	}
//If there's no x wrap, wrap the y for wrapposition1
	if (wrapposition1 == position)
	{
		if (position.y() < smallbox._loy)
		{
			outcode |= BOX_LOY;
			wrapposition1.set(position.x(), _hiy - _loy + position.y(),position.z());
		}
		if (position.y() > smallbox._hiy)
		{
			outcode |= BOX_HIY;
			wrapposition1.set(position.x(), _loy - _hiy + position.y(),position.z());
		}
	}
//If there's x wrap in wrapposition1, do the y wrap of position and wrapposition1
	else //wrapposition1 not equal to position
	{
		if (position.y() < smallbox._loy)
		{
			outcode |= BOX_LOY;
			wrapposition2.set(position.x(), _hiy - _loy + position.y(),position.z());
			wrapposition3.set(wrapposition1.x(), _hiy - _loy + position.y(),position.z());
		}
		if (position.y() > smallbox._hiy)
		{
			outcode |= BOX_HIY;
			wrapposition2.set(position.x(), _loy - _hiy + position.y(),position.z());
			wrapposition3.set(wrapposition1.x(), _loy - _hiy + position.y(),position.z());
		}
	}
	if (position.z() < _loz)
	{
		outcode |= BOX_LOZ;
		position.set(position.x(), position.y(), _hiz - _loz + position.z());
	}
	if (position.z() > _hiz)
	{
		outcode |= BOX_HIZ;
		position.set(position.x(), position.y(), _loz - _hiz + position.z());
	}
	return outcode;
}

int cRealBox3::clamp(cVector3 &position) const
{
	int outcode = BOX_INSIDE;
	if (position.x() < _lox)
	{
		outcode |= BOX_LOX;
		position.set(_lox, position.y(), position.z());
	}
	if (position.x() > _hix)
	{
		outcode |= BOX_HIX;
		position.set(_hix, position.y(), position.z());
	}
	if (position.y() < _loy)
	{
		outcode |= BOX_LOY;
		position.set(position.x(), _loy, position.z());
	}
	if (position.y() > _hiy)
	{
		outcode |= BOX_HIY;
		position.set(position.x(), _hiy, position.z());
	}
	if (position.z() < _loz)
	{
		outcode |= BOX_LOY;
		position.set(position.x(), position.y(), _loz);
	}
	if (position.z() > _hiz)
	{
		outcode |= BOX_HIY;
		position.set(position.x(), position.y(), _hiz);
	}
	return outcode;
}

int cRealBox3::clamp(cVector3 &position, cVector3 &velocity) const
{
	int outcode = clamp(position);
	if (outcode & BOX_LOX) //If you hit a LO X wall, zero out any neg X velocity.
		velocity.set(__max(0.0, velocity.x()), velocity.y());
	if (outcode & BOX_HIX) //If you hit an HI X wall, zero out any pos X velocity.
		velocity.set(__min(0.0, velocity.x()), velocity.y());
	if (outcode & BOX_LOY) //Same deal for Y.
		velocity.set( velocity.x(), 0.0);//__max(0.0, velocity.y()) );
	if (outcode & BOX_HIY) 
		velocity.set( velocity.x(), __min(0.0, velocity.y()) );
	if (outcode & BOX_LOZ) //Same deal for Z.
		velocity.set( velocity.x(),  velocity.y(), __max(0.0, velocity.z()) );
	if (outcode & BOX_HIZ) 
		velocity.set( velocity.x(),  velocity.y(), __min(0.0, velocity.z()) );
	return outcode;
}

int cRealBox3::addBounce(cVector3 &position, cVector3 &velocity, Real bounciness)const
{
	int outcode = BOX_INSIDE;

	position += velocity;
	/* The basic idea is to reverse appropriate velocity component if you've passed an edge,
	and reflect the motion past the edge into motion away from the edge.  Reflection means
	at the right newx would be _hix - (position.x()-_hix), or 2*_hix - position.x()
	and at the newx would be _lox + (_lox - position.x()), or 2*_lox - position.x(). 
		Now add in the notion of a bounciness between 0.0 and 1.0.  First of all the
	reflected newvel is attentuated to -bounciness*oldvel.  Second, the reflected
	position should take into account the fact that the object is moving slower after
	the reflection, so we get, on the right, 
	newx  = _hix - bounciness*(position.x()-_hix) and on the left 
	newx = _lox + bounciness*(_lox - position.x()) or
	newx = 	_lox - bounciness*(position.x() - _lox)*/
#ifdef USEBOUNCINESS
	if (position.x() > _hix)
	{
		velocity.set(-bounciness*velocity.x(), velocity.y());
		position.set(_hix - bounciness*(position.x()-_hix), position.y());
		outcode |= BOX_HIX;
	}
	if (position.x() < _lox)
	{
     	velocity.set(-bounciness*velocity.x(), velocity.y());
		position.set(_lox - bounciness*(position.x()-_lox), position.y());
		outcode |= BOX_LOX;
	}
	if (position.y() > _hiy)
	{
     	velocity.set(velocity.x(), -bounciness*velocity.y());
		position.set(position.x(), _hiy - bounciness*(position.y()-_hiy));
		outcode |= BOX_HIY;
	}
	if (position.y() < _loy)
	{
     	velocity.set(velocity.x(), -bounciness*velocity.y());
		position.set(position.x(), _loy - bounciness*(position.y()-_loy));
		outcode |= BOX_LOY;
	}
	if (position.z() > _hiz)
	{
     	velocity.set(velocity.x(), velocity.y(), -bounciness*velocity.z());
		position.set(position.x(), position.y(), _hiz - bounciness*(position.z()-_hiz));
		outcode |= BOX_HIZ;
	}
	if (position.z() < _loz)
	{
     	velocity.set(velocity.x(), velocity.y(), -bounciness*velocity.z());
		position.set(position.x(), position.y(), _loz - bounciness*(position.z()-_loz));
		outcode |= BOX_LOZ;
	}
	if (outcode != BOX_INSIDE)
		clamp(position); /* Just so some screwy high velocity bounce can't 
			leave you outside the box */
    return outcode;
#else //not USEBOUNCINESS, do it the old way
	if (position.x() > _hix)
	{
		velocity.set(-velocity.x(), velocity.y());
		position.set(2*_hix - position.x(), position.y());
		outcode |= BOX_HIX;
	}
	if (position.x() < _lox)
	{
     	velocity.set(-velocity.x(), velocity.y());
		position.set(2*_lox - position.x(), position.y());
		outcode |= BOX_LOX;
	}
	if (position.y() > _hiy)
	{
     	velocity.set(velocity.x(), -velocity.y());
		position.set(position.x(), 2*_hiy - position.y());
		outcode |= BOX_HIY;
	}
	if (position.y() < _loy)
	{
     	velocity.set(velocity.x(), -velocity.y());
		position.set(position.x(), 2*_loy - position.y());
		outcode |= BOX_LOY;
	}
	if (position.z() > _hiz)
	{
     	velocity.set(velocity.x(), velocity.y(), -velocity.z());
		position.set(position.x(), position.y(), 2*_hiz - position.z());
		outcode |= BOX_HIZ;
	}
	if (position.z() < _loz)
	{
     	velocity.set(velocity.x(), velocity.y(), -velocity.z());
		position.set(position.x(), position.y(), 2*_loz - position.z());
		outcode |= BOX_LOZ;
	}
	if (outcode != BOX_INSIDE)
		clamp(position); /* Just so some screwy high velocity bounce can't 
			leave you outside the box */
    return outcode;
#endif //USEBOUNCINESS switch
}

int cRealBox3::addBounce(cVector3 &position, cVector3 &velocity, Real bounciness, Real dt)const
{
	if (fabs(dt)<SMALL_REAL)
		return BOX_INSIDE; //You're not moving.
	cVector3 dtvelocity = dt*velocity;
	int outcode = addBounce(position, dtvelocity, bounciness);
	velocity = dtvelocity/dt;
	return outcode;
}

/*CRect cRealBox3::realToPixel(const cRealPixelConverter &crealpixelconverter)
{
	int left, top, right, bottom;	
	crealpixelconverter.realToPixel(_lox, _loy, &left, &bottom);
	crealpixelconverter.realToPixel(_hix, _hiy, &right, &top);
	return CRect(left, top, right, bottom);
}
*/

void cRealBox3::draw(cGraphics *pgraphics, int drawflags)
{ 
	if (!zsize())
	{
		/*Draw a basic unfilled rectangle at z=0. */
		cRealBox2 pgbox(*this);
		pgbox.draw(pgraphics, drawflags);
	}
	else 
	{
#define DRAW3DBOX
#ifndef DRAW3DBOX
		/*Draw a basic unfilled rectangle at z=0. */
		cRealBox2 pgbox(*this);
		pgbox.draw(pgraphics, drawflags);
#else //DRAW3DBOX
		pgraphics->drawrectangle(cVector(_lox, _loy, _loz), cVector(_hix, _hiy, _loz), _pcolorstyle);
		pgraphics->drawrectangle(cVector(_lox, _loy, _hiz), cVector(_hix, _hiy, _hiz), _pcolorstyle);
		pgraphics->drawline(cVector(_lox, _loy, _loz), cVector3(_lox, _loy, _hiz), _pcolorstyle);
		pgraphics->drawline(cVector(_lox, _hiy, _loz), cVector3(_lox, _hiy, _hiz), _pcolorstyle);
		pgraphics->drawline(cVector(_hix, _loy, _loz), cVector3(_hix, _loy, _hiz), _pcolorstyle);
		pgraphics->drawline(cVector(_hix, _hiy, _loz), cVector3(_hix, _hiy, _hiz), _pcolorstyle);
#endif //DRAW3DBOX
	}
}

cRealBox3 operator*(Real scale, const cRealBox3 &box)
{
	return cRealBox3(box.center(), scale*box.xsize(),
		scale*box.ysize(), scale*box.zsize());
}

BOOL operator==(const cRealBox3 arect, const cRealBox3 brect)
{
	return (arect._lox==brect._lox && arect._loy==brect._loy && 
		arect._hix==brect._hix && arect._hiy==brect._hiy);
}

BOOL operator!=(const cRealBox3 arect, const cRealBox3 brect)
{ return !(arect==brect);}


