//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================//

#include "stdafx.h"
#include "Box3D.h"
#include "GlobalFunctions.h"
#include "MainFrm.h"	// dvs: hack for tools migration code
#include "MapDoc.h"
#include "MapView2D.h"
#include "Options.h"
#include "Render2D.h"
#include "Render3D.h"
#include "RenderUtils.h"
#include "resource.h"
#include "StatusBarIDs.h"
#include "hammer_mathlib.h"
#include "vgui/Cursor.h"
#include "HammerVGui.h"
#include <VGuiMatSurface/IMatSystemSurface.h>
#include "camera.h"

// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>


#pragma warning(disable:4244)

extern float g_MAX_MAP_COORD; // dvs: move these into Globals.h!!
extern float g_MIN_MAP_COORD; // dvs: move these into Globals.h!!


WorldUnits_t Box3D::m_eWorldUnits = Units_None;


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
Box3D::Box3D(void)
{
	SetEmpty();
	SetDrawFlags(0);
	m_TranslateMode = modeScale;
	m_vTranslationFixPoint.Init();
	m_TranslateHandle.Init();
	m_bEnableHandles = true;
	SetDrawColors(Options.colors.clrToolHandle, Options.colors.clrToolBlock);
}

void Box3D::SetEmpty()
{
	Tool3D::SetEmpty();
	ResetBounds();

	if ( m_pDocument )
	{
		m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_TOOL );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : pView - 
//			pt - Point in client coordinates.
//			bValidOnly - 
// Output : 
//-----------------------------------------------------------------------------
int Box3D::HitTest(CMapView *pView, const Vector2D &ptClient, bool bTestHandles)
{
	bool bHit = false;

	if ( pView->HitTest( ptClient, bmins, bmaxs ) )
	{
		// The point is inside the main rect.
		m_LastHitTestHandle.Init();
		bHit = true;
	}

	if ( !m_bEnableHandles || !bTestHandles )
	{
		// Handles are turned off, so we don't need to do any more testing.
		// Return whether we hit the main rect or not.
		return bHit;
	}

	// check if we hit a handle

	Vector handles[3*3*3];
	int numHandles = GetVisibleHandles( handles, pView, m_TranslateMode );


	Vector vOffset(HANDLE_OFFSET,HANDLE_OFFSET,HANDLE_OFFSET);

	if ( pView->IsOrthographic() )
	{
		vOffset /= pView->GetCamera()->GetZoom();
	}
	else
	{
		vOffset.Init();
	}
		
	
	Vector vCenter = (bmins+bmaxs)/2;
	Vector vDelta = (bmaxs + vOffset) - vCenter;

	for ( int i = 0; i<numHandles; i++ )
	{

		Vector pos = vCenter + vDelta * handles[i];

		if ( HitRect( pView, ptClient, pos, HANDLE_RADIUS ) )
		{
			// remember handle found
			m_LastHitTestHandle = handles[i];
			bHit = true;
			break;
		}
	}

	return bHit;
}


//-----------------------------------------------------------------------------
// Purpose: Set the cursor based on the hit test results and current translate mode.
// Input  : eHandleHit - The handle that the cursor is over.
//			eTransformMode - The current transform mode of the tool - scale, rotate, or shear.
//-----------------------------------------------------------------------------
unsigned long Box3D::UpdateCursor(CMapView *pView, const Vector &vHandleHit, TransformMode_t eTransformMode)
{
	if ( eTransformMode == modeMove || vHandleHit.IsZero() )
		return vgui::dc_sizeall;
	
	if ( eTransformMode == modeNone )
		return vgui::dc_arrow;
		
	if (eTransformMode == modeRotate)
		return  g_pMatSystemSurface->CreateCursorFromFile("Resource/rotate.cur");
	
	// cursor icon depends on handle and map view :

	Vector2D ptOrigin; pView->WorldToClient( ptOrigin, Vector(0,0,0) );
	Vector2D ptHit; pView->WorldToClient( ptHit, vHandleHit );
	Vector2D pt; pt.x = ptHit.x - ptOrigin.x; pt.y = ptHit.y - ptOrigin.y;

	if (eTransformMode == modeScale)
	{
		if ( pt.x > 0 )
		{
			if ( pt.y > 0 )
				return vgui::dc_sizenwse;
			else if ( pt.y < 0 )
				return vgui::dc_sizenesw;
			else
				return vgui::dc_sizewe;
		}
		else  if ( pt.x < 0 )
		{
			if ( pt.y > 0 )
				return vgui::dc_sizenesw;
			else if ( pt.y < 0 )
				return vgui::dc_sizenwse;
			else
				return vgui::dc_sizewe;
				
		}
		else // pt.x == 0 
		{
			if ( pt.y != 0 )
				return vgui::dc_sizens;
			else
				return vgui::dc_sizeall;
		}
	}
	else if (eTransformMode == modeShear)
	{
		if ( pt.x == 0 )
			return vgui::dc_sizewe;
		else
			return vgui::dc_sizens;
	}
	
	return vgui::dc_none;
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : bEnable - 
//-----------------------------------------------------------------------------
void Box3D::EnableHandles(bool bEnable)
{
	m_bEnableHandles = bEnable;
}


//-----------------------------------------------------------------------------
// Purpose: Finds the corner nearest to a given point in world coordinates.
// Output : Returns the corner in world coordinates (axThird is always 0).
//-----------------------------------------------------------------------------
const Vector Box3D::NearestCorner( const Vector2D &vPoint, CMapView *pView, const Vector *pCustomHandleBox )
{
	Vector	vHandles[3*3*3];
	float	fBestDist = 999999.9f;
	Vector	vBestCorner(0,0,0);
	int		nFace = -1;
	Vector	start,end,pos;
	
	pView->BuildRay( vPoint, start,end );
	float dist = IntersectionLineAABBox( bmins, bmaxs, start, end, nFace );

	if ( dist < 0 )
		return vBestCorner;

	// get point where we hit the bbox
	pos = end-start; VectorNormalize( pos );
	pos = start + pos*dist;
	
	// mode rotate has only corner handles
	int nNumHandles = GetVisibleHandles( vHandles, pView, modeRotate );
	
	for ( int i=0; i<nNumHandles; i++ )
	{
		Vector vecCorner;

		HandleToWorld( vecCorner, vHandles[i], pCustomHandleBox );

		float distance = VectorLength( vecCorner - pos );

		if ( distance < fBestDist )
		{
			fBestDist = distance;
			vBestCorner = vecCorner;
		}
	}
	 	
	return vBestCorner;
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : pt - 
//			ptForceMoveRef - 
// Output : Returns TRUE if pt hits a handle or is in box area, FALSE otherwise.
//-----------------------------------------------------------------------------
void Box3D::StartTranslation( 
	CMapView *pView, 
	const Vector2D &vPoint, 
	const Vector &vHandleOrigin, 
	const Vector *pRefPoint,
	const Vector *pCustomHandleBox )
{
	if ( vHandleOrigin.IsZero() )
	{
		// we hit the main body, switch to move translation then
		m_LastTranslateMode = m_TranslateMode;
		m_TranslateMode = modeMove;
	}

	m_TranslateHandle = vHandleOrigin;
	m_bPreventOverlap = true;
	
	if ( pRefPoint )
	{
		// transformation reference point was given
		m_vTranslationFixPoint = *pRefPoint;
	}
	else
	{
		// build reference point based on mode & handle
		if (m_TranslateMode == modeRotate)
		{
			// user center of object for rotation
			m_vTranslationFixPoint = (bmins + bmaxs) / 2;
		}
		else if (m_TranslateMode == modeMove)
		{
			// chose nearest corner to  
			m_vTranslationFixPoint = NearestCorner( vPoint, pView, pCustomHandleBox );
		}
		else
		{
			// find opposite point to handle
			m_vTranslationFixPoint.Init();
			for ( int i=0; i<3; i++ )
			{
				float handle = m_TranslateHandle[i];

				if ( handle > 0 )
				{
					m_vTranslationFixPoint[i] = bmins[i]; 
				}
				else if ( handle < 0 )
				{
					m_vTranslationFixPoint[i] = bmaxs[i]; 
				}
			}
		}
	}

	// get axis normals from picked face
	Vector v1,v2,v3,vOrigin;

	// if no valid translation handle, cull against BBox
	if ( m_TranslateMode == modeMove )
	{
		int nFace;
		pView->BuildRay( vPoint, v1, v2 );
					
		IntersectionLineAABBox( bmins, bmaxs, v1, v2, nFace );

		if ( nFace >= 0 )
		{
			// get axis & normals of face we hit
			GetAxisFromFace( nFace, v1, v2, v3 );
		}
		else
		{
			pView->GetBestTransformPlane( v1,v2,v3 );
		}

		vOrigin = m_vTranslationFixPoint;
	}
	else
	{
		pView->GetBestTransformPlane( v1,v2,v3 );
		HandleToWorld( vOrigin, m_TranslateHandle );
	}

	// set temp transformation plane 
	SetTransformationPlane(vOrigin, v1, v2, v3 );

	// align translation plane to world origin
	ProjectOnTranslationPlane( vec3_origin, vOrigin, 0 );

	// set transformation plane
	SetTransformationPlane(vOrigin, v1, v2, v3 );

	Tool3D::StartTranslation( pView, vPoint, false );

	m_TransformMatrix.Identity();
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : pszBuf - 
//-----------------------------------------------------------------------------
void Box3D::GetStatusString(char *pszBuf)
{
	*pszBuf = '\0';



	Vector mins(0,0,0);
	Vector maxs(0,0,0);

	if ( IsValidBox() )
	{
		mins = bmins;
		maxs = bmaxs;
	}

	if ( IsTranslating() )
	{
		TranslateBox( mins, maxs );
	}

	Vector size = maxs - mins;
	Vector center = ( maxs + mins ) * 0.5f;

	if ( !IsTranslating() || m_TranslateMode == modeScale || m_TranslateMode == modeMove )
	{
		if (!IsEmpty())
		{
			if ( IsTranslating() && m_TranslateMode == modeMove )
			{
				center = m_vTranslationFixPoint;
				TranslatePoint( center );
			}

			switch (m_eWorldUnits)
			{
				case Units_None:
				{
					sprintf(pszBuf, " %dw %dl %dh @(%.0f %.0f %.0f)", 
						(int)fabs(size.x), (int)fabs(size.y), (int)fabs(size.z),
						center.x,center.y,center.z );
					break;
				}

				case Units_Inches:
				{
					sprintf(pszBuf, " %d\"w %d\"l %d\"h", (int)fabs(size.x), (int)fabs(size.y), (int)fabs(size.z));
					break;
				}

				case Units_Feet_Inches:
				{
					int nFeetWide = (int)fabs(size.x) / 12;
					int nInchesWide = (int)fabs(size.x) % 12;

					int nFeetLong = (int)fabs(size.y) / 12;
					int nInchesLong = (int)fabs(size.y) % 12;

					int nFeetHigh = (int)fabs(size.z) / 12;
					int nInchesHigh = (int)fabs(size.z) % 12;

					sprintf(pszBuf, " %d' %d\"w %d' %d\"l %d' %d\"h", nFeetWide, nInchesWide, nFeetLong, nInchesLong, nFeetHigh, nInchesHigh);
					break;
				}
			}
		}
	}
	else if ( m_TranslateMode == modeShear )
	{
		sprintf(pszBuf, " shear: %d %d %d ", (int)m_vTranslation.x, (int)m_vTranslation.y, (int)m_vTranslation.z );
	}
	else if ( m_TranslateMode == modeRotate )
	{
		int rotAxis = GetTransformationAxis();

		if ( rotAxis != -1  )
		{
			sprintf(pszBuf, " %.2f%c", m_vTranslation[abs(rotAxis+2)%3], 0xF8);
		}
		else
		{
			sprintf(pszBuf, " %.2f %.2f %.2f%c", m_vTranslation.x, m_vTranslation.y, m_vTranslation.z, 0xF8);
		}
	}
	else
	{
		Assert( 0 );
	}
	
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void Box3D::UpdateStatusBar()
{
	char szBuf[MAX_PATH];
	GetStatusString(szBuf);
	SetStatusText(SBI_SIZE, szBuf);
}

int Box3D::GetVisibleHandles( Vector *handles, CMapView *pView, int nMode )
{
	bool bCorners, bEdges, bFaces;
	bool bIs2D = pView->IsOrthographic();

	Vector vViewAxis = pView->GetViewAxis();
	Vector vViewPoint; pView->GetCamera()->GetViewPoint( vViewPoint );
	
	if ( bIs2D )
	{
		bCorners = false;
		bEdges	= nMode == modeRotate || nMode == modeScale;
		bFaces = nMode == modeShear || nMode == modeScale;  
	}
	else
	{
		bCorners = nMode == modeRotate || nMode == modeScale;
		bEdges	= nMode == modeScale;
		bFaces = nMode == modeShear; 
	}

	if ( !bCorners && !bEdges && !bFaces )
		return 0;

	int count = 0;

	for ( int x = -1; x < 2; x++ )
	{
		if ( bIs2D && (x != 0) && (fabs(vViewAxis.x) == 1) )
			continue;

		for ( int y = -1; y < 2; y++ )
		{
			if ( bIs2D && (y != 0) && (fabs(vViewAxis.y) == 1) )
				continue;

			for ( int z = -1; z<2; z++)
 			{
				if ( bIs2D && (z != 0) && (fabs(vViewAxis.z) == 1) )
					continue;

				int n = abs(x) + abs(y) + abs(z);

				if ( n == 0 )
				{
					// don't add center as handle
					continue;
				}
				else if ( n == 1 )
				{
					if ( !bFaces )
						continue;
				}
				else if ( n == 2 )
				{
					if ( !bEdges )
						continue;
				}
				else
				{
					if ( !bCorners )
						continue;
				}

				if ( !bIs2D  )
				{
					Vector vHandle; HandleToWorld( vHandle, Vector(x,y,z) );
					Vector vDelta = vHandle - vViewPoint; 
					float fDistance = VectorLength( vDelta );

					// Avoid divide by zero.
					if ( !fDistance )
						continue;

					vDelta /= fDistance; // normalize

					if ( DotProduct(vDelta,vViewAxis) < 0 )
						continue; 

					int nFace;
					float fIntersection = IntersectionLineAABBox( bmins, bmaxs, vViewPoint, vViewPoint+vDelta*99999, nFace );

 					if ( fIntersection >= 0 && fIntersection*1.01 < fDistance )
						continue;
				}
				
				// add handle as visible
				handles[count] = Vector(x,y,z);
				count++;
			}
		}
	}

	return count;
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : ptWorld - point to update with in world coordinates
//			uConstraints - 
//			dragSize - 
// Output : 
//-----------------------------------------------------------------------------
bool Box3D::UpdateTranslation(const Vector &vUpdate, UINT uConstraints)
{
	if (m_TranslateMode == modeNone)
	{
		return false;
	}

	else if ( m_TranslateMode == modeRotate )
	{
 		Vector vCenter; ProjectOnTranslationPlane( m_vTranslationFixPoint, vCenter );
		Vector vStart; HandleToWorld( vStart, m_TranslateHandle );
		Vector v1 = vStart-vCenter; VectorNormalize( v1 );
		Vector v2 = (vStart+vUpdate)-vCenter; VectorNormalize( v2 );
		float volume = DotProduct( m_vPlaneNormal, CrossProduct( v1, v2) );
		float angle = RAD2DEG( acos( DotProduct( v1,v2) ) );

		if (uConstraints & constrainSnap)
		{
			angle += 7.5;
			angle -= fmod(double(angle), double(15.0));
		}
		else
		{
			angle += 0.25;
			angle -= fmod(double(angle), double(.5));
		}

        if ( volume < 0 )
			angle = -angle;

		if ( fabs(m_vPlaneNormal.x) == 1 )
			m_vTranslation.z = (m_vPlaneNormal.x>0)?angle:-angle;
		else if ( fabs(m_vPlaneNormal.y) == 1 )
			m_vTranslation.x = (m_vPlaneNormal.y>0)?angle:-angle;
		else if ( fabs(m_vPlaneNormal.z) == 1 )
			m_vTranslation.y = (m_vPlaneNormal.z>0)?angle:-angle;
	}
	else 
	{
		if ( vUpdate == m_vTranslation )
			return false; // no change

		m_vTranslation = vUpdate;

		// restrict translation, snap to grid, prevent overlap etc
		// make sure reference point snaps if enabled
		if ( uConstraints )
		{
			// project back on projection plane
			Vector pos; 
			
			if ( m_TranslateMode == modeMove )
			{
				// when moving opbject make sure reference point is on grid
				pos = m_vTranslationFixPoint;
			}
			else
			{
				// otherwise translated handle should be on grid
				HandleToWorld( pos, m_TranslateHandle);
			}

			ProjectOnTranslationPlane( pos + m_vTranslation, m_vTranslation, uConstraints );
			m_vTranslation -= pos;
		}

		if ( m_TranslateMode == modeScale )
		{
			for ( int i=0; i<3; i++ )
			{
				float handle = m_TranslateHandle[i];

 				if ( handle > 0 )
				{
					float newMaxs = bmaxs[i] + m_vTranslation[i];

					if( m_bPreventOverlap && newMaxs <= bmins[i] )
					{
						m_vTranslation[i] = bmins[i] - bmaxs[i] + 1;
					}
				}
				else if ( handle < 0 )
				{
					float newMins = bmins[i] + m_vTranslation[i];

					if( m_bPreventOverlap && newMins >= bmaxs[i] )
					{
						m_vTranslation[i] = bmaxs[i] - bmins[i] - 1;
					}
				}
			}
		}
	}

	UpdateTransformMatrix();

	m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_TOOL );

	return true;
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : dwHandleColor - 
//			dwBoxColor - 
//-----------------------------------------------------------------------------
void Box3D::SetDrawColors(COLORREF dwHandleColor, COLORREF dwBoxColor)
{
	if (dwHandleColor != 0xffffffff)
	{
		m_clrHandle = dwHandleColor;
	}

	if (dwBoxColor != 0xffffffff)
	{
		m_clrBox = dwBoxColor;
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pt - 
//-----------------------------------------------------------------------------
void Box3D::TranslatePoint(Vector& pt)
{
	TransformPoint( m_TransformMatrix, pt );
}

const VMatrix& Box3D::GetTransformMatrix()
{
	return m_TransformMatrix;
}

void Box3D::UpdateTransformMatrix()
{
	m_TransformMatrix.Identity();

	if ( m_TranslateMode == modeNone )
	{
		return;
	}
	else if ( m_TranslateMode == modeMove )
	{
		m_TransformMatrix.SetTranslation( m_vTranslation );
		return;
	}
	else if ( m_TranslateMode == modeScale )
	{
		Vector vScale( 1,1,1);
		Vector vMove(0,0,0);
		Vector vSize = bmaxs-bmins;

		for ( int i=0; i<3; i++ )
		{
			float handle = m_TranslateHandle[i];

			if ( vSize[i] == 0 )
				continue;

			if ( handle > 0 )
			{
				vScale[i] = (m_vTranslation[i]+vSize[i]) / vSize[i];
				vMove[i] = m_vTranslation[i] / 2;
			}
			else if ( handle < 0 )
			{
				vScale[i] = (-m_vTranslation[i]+vSize[i]) / vSize[i];
 				vMove[i] = m_vTranslation[i] / 2;
			}
		}

		m_TransformMatrix = m_TransformMatrix.Scale( vScale );
		m_TransformMatrix.SetTranslation( vMove );
	}
	else if ( m_TranslateMode == modeShear )
	{
		Vector vSize = bmaxs-bmins;

		int axisS = -1; // shear axis that wont change
		int axisA = -1; // first shear axis
		int axisB = -1; // second shear axis
		
		for ( int i=0; i<3; i++ )
		{
 			float handle = m_TranslateHandle[i];

			if ( handle > 0 )
			{
				Assert( axisS == -1);
				axisS = i;
			}
			else if ( handle < 0 )
			{
				Assert( axisS == -1);
				axisS = i;
				vSize *= -1;
			}
			else
			{
				if ( axisA == -1 )
					axisA = i;
				else
					axisB = i;
			}
		}

		Assert( (axisA!=-1) && (axisB!=-1) && (axisS!=-1) );
		
 		m_TransformMatrix.m[axisA][axisS] = (m_vTranslation[axisA])/(vSize[axisS]);
 		m_TransformMatrix.m[axisB][axisS] = (m_vTranslation[axisB])/(vSize[axisS]);
	}
	else if ( m_TranslateMode == modeRotate )
	{
		QAngle angle = *(QAngle*)&m_vTranslation; // buuuhhh
 		m_TransformMatrix.SetupMatrixOrgAngles( vec3_origin, angle );
	}

	// apply m_vTranslationFixPoint offset

	Vector offset;
	m_TransformMatrix.V3Mul( m_vTranslationFixPoint, offset );
	offset = m_vTranslationFixPoint - offset;

	m_TransformMatrix.m[0][3] += offset[0];
	m_TransformMatrix.m[1][3] += offset[1];
	m_TransformMatrix.m[2][3] += offset[2];
}

void Box3D::TranslateBox(Vector& mins, Vector& maxs)
{
	if ( m_TranslateMode == modeNone )
	{
		return;
	}

	if ( m_TranslateMode == modeMove )
	{
		mins += m_vTranslation;
		maxs += m_vTranslation;
	}

	else if ( m_TranslateMode == modeScale )
	{
		for ( int i=0; i<3; i++ )
		{
			float handle = m_TranslateHandle[i];

			if ( handle > 0 )
			{
				maxs[i] += m_vTranslation[i];
			}
			else if ( handle < 0 )
			{
				mins[i] += m_vTranslation[i];
			}
		}
	}

	else if ( m_TranslateMode == modeShear )
	{
		TranslatePoint( mins );
		TranslatePoint( maxs );
	}
	else if ( m_TranslateMode == modeRotate )
	{
		TranslatePoint( mins );
		TranslatePoint( maxs );
	}

	NormalizeBox( mins, maxs );
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : bSave - 
//-----------------------------------------------------------------------------
void Box3D::FinishTranslation(bool bSave)
{
	if( bSave )
	{
		Vector newMins = bmins;
		Vector newMaxs = bmaxs;

		TranslateBox( newMins, newMaxs );
		LimitBox( newMins, newMaxs, g_MAX_MAP_COORD ); 

		SetBounds( newMins, newMaxs );

		m_bEmpty = false;
	}

	// if we are finished with moving the selection, switch back to the
	// original translation mode
	if ( m_TranslateMode == modeMove )
	{
		m_TranslateMode = m_LastTranslateMode;
	}

	Tool3D::FinishTranslation(bSave);
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void Box3D::ToggleTranslateMode(void)
{
	if( m_TranslateMode == modeMove )
	{
		m_TranslateMode = modeScale;
	}
	else if( m_TranslateMode == modeScale )
	{
		m_TranslateMode = modeRotate;
	}
	else if( m_TranslateMode == modeRotate )
	{
		m_TranslateMode = modeShear;
	}
	else if( m_TranslateMode == modeShear )
	{
		m_TranslateMode = modeScale; // don't go back to move mode
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : dwFlags - 
//-----------------------------------------------------------------------------
void Box3D::SetDrawFlags(DWORD dwFlags)
{
	m_dwDrawFlags = dwFlags;
}

void Box3D::RenderHandles2D(CRender2D *pRender, const Vector &mins, const Vector &maxs)
{
	Vector handles[3*3*3];

	int numHandles = GetVisibleHandles( handles, pRender->GetView(), m_TranslateMode );

	if ( numHandles == 0 )
		return;

	pRender->SetHandleColor( GetRValue(m_clrHandle), GetGValue(m_clrHandle), GetBValue(m_clrHandle) );

	if ( m_TranslateMode == modeRotate )
	{
		pRender->SetHandleStyle( HANDLE_RADIUS, CRender::HANDLE_CIRCLE );
		
	}
	else
	{
		pRender->SetHandleStyle( HANDLE_RADIUS, CRender::HANDLE_SQUARE );
	}
	
	
	Vector vCenter = (mins+maxs)/2;
	Vector vDelta = maxs - vCenter;
	Vector2D vOffset; 

	bool bPopMode = pRender->BeginClientSpace();
	
	for ( int i=0; i<numHandles; i++)
	{
		pRender->TransformNormal( vOffset, handles[i] );
		vOffset.x = fsign(vOffset.x);
		vOffset.y = fsign(vOffset.y);
		vOffset*=HANDLE_OFFSET;

		Vector pos = vCenter + vDelta * handles[i];
		pRender->DrawHandle( pos, &vOffset );
	}

	if ( bPopMode )
		pRender->EndClientSpace();
}

void Box3D::RenderHandles3D(CRender3D *pRender, const Vector &mins, const Vector &maxs)
{
	Vector handles[3*3*3];

	int numHandles = GetVisibleHandles( handles, pRender->GetView(), m_TranslateMode );

	if ( numHandles == 0 )
		return;

	Vector vCenter = (mins+maxs)/2;
	Vector vDelta = maxs - vCenter;

	if ( m_TranslateMode == modeRotate )
	{
		pRender->SetHandleStyle( HANDLE_RADIUS, CRender::HANDLE_CIRCLE );

	}
	else
	{
		pRender->SetHandleStyle( HANDLE_RADIUS, CRender::HANDLE_SQUARE );
	}

	pRender->SetHandleColor( GetRValue(m_clrHandle), GetGValue(m_clrHandle), GetBValue(m_clrHandle) );
	pRender->PushRenderMode( RENDER_MODE_FLAT_NOZ );
	
	bool bPopMode = pRender->BeginClientSpace();

	for ( int i=0; i<numHandles; i++)
	{
		Vector pos = vCenter + vDelta * handles[i];

		pRender->DrawHandle( pos );
	}

	if ( bPopMode )
		pRender->EndClientSpace();

	pRender->PopRenderMode();
}

void Box3D::HandleToWorld( Vector &vWorld, const Vector &vHandle, const Vector *pCustomHandleBox)
{
	Vector vCenter, vDelta;
	if ( pCustomHandleBox )
	{
		vCenter = (pCustomHandleBox[0] + pCustomHandleBox[1]) / 2;
		vDelta = pCustomHandleBox[1] - vCenter;
	}
	else
	{
		vCenter = (bmins+bmaxs)/2;
		vDelta = bmaxs - vCenter;
	}
	
	vWorld = vCenter + (vDelta * vHandle);
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pDC - 
//			bounds - 
//-----------------------------------------------------------------------------
void Box3D::RenderTool2D(CRender2D *pRender)
{
	Vector mins = bmins;
	Vector maxs = bmaxs;
	CMapView2D *pView = (CMapView2D*)pRender->GetView();

	Assert( pRender );

	if ( IsTranslating() )
	{
		TranslateBox( mins, maxs );
	}
	else if ( IsEmpty() )
	{
		return;
	}


	if ( m_dwDrawFlags & boundstext)
	{
		DrawBoundsText(pRender, mins, maxs, DBT_TOP | DBT_LEFT);
	}

	if ( IsTranslating() )
	{
	    pRender->PushRenderMode( RENDER_MODE_DOTTED );
		pRender->SetDrawColor( GetRValue(Options.colors.clrToolDrag), GetGValue(Options.colors.clrToolDrag), GetBValue(Options.colors.clrToolDrag) );
	}
	else if (!(m_dwDrawFlags & thicklines))
	{
		pRender->PushRenderMode( RENDER_MODE_DOTTED );
		pRender->SetDrawColor( GetRValue(m_clrBox), GetGValue(m_clrBox), GetBValue(m_clrBox) );
	}
	else
	{
		pRender->PushRenderMode( RENDER_MODE_FLAT_NOZ );
		pRender->SetDrawColor( GetRValue(m_clrBox), GetGValue(m_clrBox), GetBValue(m_clrBox) );
	}

	// render bounds
	if ( !IsTranslating() || m_TranslateMode == modeScale || m_TranslateMode == modeMove )
	{
		// draw simple rectangle
		pRender->DrawRectangle( mins, maxs, false, 0 );
	}
	else
	{
		// during rotation or shearing, draw transformed bounding box

		Vector v[4];
		
		// init all points to center
		v[0] = v[1] = v[2] = v[3] = (bmins+bmaxs) / 2;

		int axis = pView->axHorz;

		v[0][axis] = v[1][axis] = bmins[axis];
		v[2][axis] = v[3][axis] = bmaxs[axis];

		axis = pView->axVert;

		v[1][axis] = v[2][axis] = bmins[axis];
		v[0][axis] = v[3][axis] = bmaxs[axis];

		for ( int i=0; i<4; i++)
		{
			TranslatePoint( v[i] );
		}
		
		pRender->DrawLine( v[0], v[1] );
		pRender->DrawLine( v[1], v[2] );
		pRender->DrawLine( v[2], v[3] );
		pRender->DrawLine( v[3], v[0] );
	}

	pRender->PopRenderMode();

	// draw a cross for translation origin in move or rotation mode
	if ( IsTranslating() )
	{
		if ( m_TranslateMode == modeMove || m_TranslateMode == modeRotate )
		{
			Vector vec = m_vTranslationFixPoint;

			if ( m_TranslateMode == modeMove  )
 			{
				TranslatePoint( vec );
			}

			// draw 'X'
			pRender->SetHandleStyle( 7, CRender::HANDLE_CROSS );
			pRender->SetHandleColor( GetRValue(Options.colors.clrToolDrag), GetGValue(Options.colors.clrToolDrag), GetBValue(Options.colors.clrToolDrag) );
			pRender->DrawHandle( vec );
			
		}
	}
	else if ( m_bEnableHandles )
	{
		RenderHandles2D( pRender, mins, maxs );
	}
}


//-----------------------------------------------------------------------------
// Purpose: Renders this region as a wireframe box.
// Input  : pRender - 3D Renderer.
//-----------------------------------------------------------------------------
void Box3D::RenderTool3D(CRender3D *pRender)
{
    if ( IsTranslating() )
	{
		VMatrix matrix = GetTransformMatrix();
		pRender->BeginLocalTransfrom( matrix );
	}
	else if (IsEmpty())
	{
		return;
	}

	pRender->PushRenderMode( RENDER_MODE_FLAT );
	pRender->SetDrawColor( GetRValue(m_clrBox), GetGValue(m_clrBox), GetBValue(m_clrBox) );
	pRender->DrawBox( bmins, bmaxs );
	pRender->PopRenderMode();

	if ( IsTranslating() )
	{
		pRender->EndLocalTransfrom();

		if ( m_TranslateMode == modeMove || m_TranslateMode == modeRotate )
		{
			Vector vec = m_vTranslationFixPoint;

			if ( m_TranslateMode == modeMove  )
			{
				TranslatePoint( vec );
			}

			// draw 'X'
			pRender->PushRenderMode( RENDER_MODE_FLAT_NOZ );
			pRender->SetHandleStyle( 7, CRender::HANDLE_CROSS );
			pRender->SetHandleColor( GetRValue(Options.colors.clrToolDrag), GetGValue(Options.colors.clrToolDrag), GetBValue(Options.colors.clrToolDrag) );
			pRender->DrawHandle( vec );
			pRender->PopRenderMode();

		}
	}
	else if ( m_bEnableHandles )
	{
		RenderHandles3D( pRender, bmins, bmaxs );
	};

	

	
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *vecStart - 
//			*mins - 
//			*maxs - 
//-----------------------------------------------------------------------------
void Box3D::StartNew( CMapView *pView, const Vector2D &vPoint, const Vector &vecStart, const Vector &vecSize )
{
	//Setup our info
	m_TranslateMode	= modeScale;
	m_TranslateHandle = Vector( 1, 1, 1 );
	bmins = vecStart;
	bmaxs = vecStart+vecSize;
	NormalizeBox( bmins, bmaxs );

	StartTranslation( pView, vPoint, Vector( 1, 1, 1 )  );

	m_bPreventOverlap = false;
	m_bEmpty = false;
}