//
//	Copyright © 2005 Nemetschek North America 
//	All Rights Reserved.
//
// Created by Vladislav Stanev

#include "StdAfx.h"
//#include "VWPolygon.h"

#include <MCMatrix.h>

using namespace VWFC::Math;

VWPolygon2D::VWPolygon2D()
{
	fdMinX			= 0;
	fdMaxX			= 0;
	fdMinY			= 0;
	fdMaxY			= 0;
	fbIsClosed		= true;
}

VWPolygon2D::VWPolygon2D(const TSTLPoints2DArray& src)
{
	size_t		len		= src.size();
	for(size_t i=0; i<len; i++)
		this->AddVertex( src[ i ] );
	fbIsClosed	= false;
}

VWPolygon2D::VWPolygon2D(const TSTLPoints2DVWArray& src)
{
	size_t		len		= src.size();
	for(size_t i=0; i<len; i++)
		this->AddVertex( src[ i ] );

	fbIsClosed	= false;
}

VWPolygon2D::VWPolygon2D(IPolyDefPtr pPolygonDef)
{
	fdMinX			= 0;
	fdMaxX			= 0;
	fdMinY			= 0;
	fdMaxY			= 0;
	fbIsClosed		= true;

	this->operator = ( pPolygonDef );
}

VWPolygon2D::VWPolygon2D(const VWPolygon2D& src)
{
	fdMinX				= src.fdMinX;
	fdMaxX				= src.fdMaxX;
	fdMinY				= src.fdMinY;
	fdMaxY				= src.fdMaxY;
	fbIsClosed			= src.fbIsClosed;
	
	size_t		len		= src.farrVertices.size();
	for(size_t i=0; i<len; i++)
		farrVertices.push_back( src.farrVertices[ i ] );
}


VWPolygon2D::~VWPolygon2D()
{
	farrVertices.clear();
}

VWPolygon2D& VWPolygon2D::operator=(const TSTLPoints2DArray& src)
{
	this->ClearVertices();
	size_t		len		= src.size();
	for(size_t i=0; i<len; i++)
		this->AddVertex( src[ i ] );
	return *this;
}

VWPolygon2D& VWPolygon2D::operator=(const TSTLPoints2DVWArray& src)
{
	this->ClearVertices();
	size_t		len		= src.size();
	for(size_t i=0; i<len; i++)
		this->AddVertex( src[ i ] );
	return *this;
}

VWPolygon2D& VWPolygon2D::operator=(IPolyDefPtr pPolygonDef)
{
	this->ClearVertices();
	if ( pPolygonDef ) {
		Uint32	cnt		= 0;
		pPolygonDef->GetVertexCount( cnt );
		for(Uint32 i=0; i<cnt; i++) {
			WorldPt		pt;
			EVertexType	vertType	= eVertexType_Corner;
			double		dRadius		= 0;

			pPolygonDef->GetVertexAt( i, pt );
			pPolygonDef->GetVertexType( i, vertType );
			pPolygonDef->GetArcVertexRadius( i, dRadius );
			bool	bVisible	= VCOM_SUCCEEDED( pPolygonDef->GetVertexVisible( i ) );

			SVertex		vertex;
			vertex.type				= vertType;
			vertex.pt.x				= pt.x;
			vertex.pt.y				= pt.y;
			vertex.dArcRadius		= dRadius;
			vertex.bVisible			= bVisible;
			farrVertices.push_back( vertex );
		}

		this->RecalculateBoundingBox();
	}
	return *this;
}

VWPolygon2D& VWPolygon2D::operator=(const VWPolygon2D& src)
{
	fdMinX				= src.fdMinX;
	fdMaxX				= src.fdMaxX;
	fdMinY				= src.fdMinY;
	fdMaxY				= src.fdMaxY;
	fbIsClosed			= src.fbIsClosed;
	
	farrVertices.clear();
	size_t		len		= src.farrVertices.size();
	for(size_t i=0; i<len; i++)
		farrVertices.push_back( src.farrVertices[ i ] );

	return *this;
}

void VWPolygon2D::SetPolygon(const TSTLPoints2DArray& src)
{
	(*this) = src;
}

void VWPolygon2D::SetPolygon(const TSTLPoints2DVWArray& src)
{
	(*this) = src;
}

void VWPolygon2D::SetPolygon(IPolyDefPtr pPolygonDef)
{
	(*this) = pPolygonDef;
}

void VWPolygon2D::GetPolygon(TSTLPoints2DArray& out) const
{
	out.clear();
	size_t		len		= farrVertices.size();
	for(size_t i=0; i<len; i++)
		out.push_back( farrVertices[i].pt );
}

void VWPolygon2D::GetPolygon(TSTLPoints2DVWArray& out) const
{
	out.clear();
	size_t		len		= farrVertices.size();
	for(size_t i=0; i<len; i++)
		out.push_back( farrVertices[i].pt );
}

void VWPolygon2D::GetPolygon(IPolyDefPtr pPolygonDef) const
{
	if ( pPolygonDef ) {
		pPolygonDef->ClearVertices();
		size_t		len		= farrVertices.size();
		for(size_t i=0; i<len; i++) {
			const SVertex&	vertex = farrVertices[ i ];

			if ( vertex.type == eVertexType_Arc ) {
				pPolygonDef->AddVertex( vertex.pt, vertex.dArcRadius );
			}
			else {
				pPolygonDef->AddVertex( vertex.pt, vertex.type );
			}
		}
	}
}

void VWPolygon2D::ClearVertices()
{
	fdMinX			= 0;
	fdMaxX			= 0;
	fdMinY			= 0;
	fdMaxY			= 0;
	farrVertices.clear();
	fbIsClosed		= true;
}

void VWPolygon2D::AddVertex(const VWPoint2D& pt, EVertexType type /*=eVertexType_Corner*/, double dArcRadius /*=0*/)
{
	SVertex		vertex;
	vertex.type				= type;
	vertex.pt				= pt;
	vertex.dArcRadius		= dArcRadius;
	vertex.bVisible			= true;
	farrVertices.push_back( vertex );
	this->RecalculateBoundingBox();
}

void VWPolygon2D::AddVertex(double x, double y, EVertexType type /*=eVertexType_Corner*/, double dArcRadius /*=0*/)
{
	SVertex		vertex;
	vertex.type				= type;
	vertex.pt.x				= x;
	vertex.pt.y				= y;
	vertex.dArcRadius		= dArcRadius;
	vertex.bVisible			= true;
	farrVertices.push_back( vertex );
	this->RecalculateBoundingBox();
}

void VWPolygon2D::InsertVertex(size_t at, double x, double y, EVertexType type /*=eVertexType_Corner*/, double dArcRadius /*=0*/)
{
	SVertex		vertex;
	vertex.type				= type;
	vertex.pt.x				= x;
	vertex.pt.y				= y;
	vertex.dArcRadius		= dArcRadius;
	vertex.bVisible			= true;
	farrVertices.insert( farrVertices.begin() + at, vertex );
	this->RecalculateBoundingBox();
}

void VWPolygon2D::InsertVertex(size_t at, const VWPoint2D& pt, EVertexType type /*=eVertexType_Corner*/, double dArcRadius /*=0*/)
{
	SVertex		vertex;
	vertex.type				= type;
	vertex.pt				= pt;
	vertex.dArcRadius		= dArcRadius;
	vertex.bVisible			= true;
	farrVertices.insert( farrVertices.begin() + at, vertex );
	this->RecalculateBoundingBox();
}

void VWPolygon2D::ClearVertex(size_t at)
{
	farrVertices.erase( farrVertices.begin() + at );
	this->RecalculateBoundingBox();
}

size_t VWPolygon2D::GetVertexCount() const
{
	return farrVertices.size();
}

bool VWPolygon2D::IsEmpty() const
{
	return farrVertices.size() <= 1;
}

VWPoint2D& VWPolygon2D::GetVertexAt(size_t at, bool bLoopVerts /*=false*/)
{
	if ( bLoopVerts ) {
		// loop the vertex indeces inside the array
		VWFC_ASSERT( farrVertices.size() > 0 );
		at		= at % farrVertices.size();
	}
	else {
		VWFC_ASSERT( at < farrVertices.size() );
	}
	return farrVertices[ at ].pt;
}

const VWPoint2D& VWPolygon2D::GetVertexAt(size_t at, bool bLoopVerts /*=false*/) const
{
	if ( bLoopVerts ) {
		// loop the vertex indeces inside the array
		VWFC_ASSERT( farrVertices.size() > 0 );
		at		= at % farrVertices.size();
	}
	else {
		VWFC_ASSERT( at < farrVertices.size() );
	}
	return farrVertices[ at ].pt;
}

EVertexType VWPolygon2D::GetVertexType(size_t at) const
{
	VWFC_ASSERT( at < farrVertices.size() );
	return farrVertices[ at ].type;
}

void VWPolygon2D::SetVertexType(size_t at, EVertexType type)
{
	VWFC_ASSERT( at < farrVertices.size() );
	farrVertices[ at ].type = type;
}

double VWPolygon2D::GetArcVertexRadius(size_t at) const
{
	VWFC_ASSERT( at < farrVertices.size() );
	VWFC_ASSERT( farrVertices[ at ].type == eVertexType_Arc );
	return farrVertices[ at ].dArcRadius;
}

void VWPolygon2D::SetArcVertexRadius(size_t at, double dRadius)
{
	VWFC_ASSERT( at < farrVertices.size() );
	VWFC_ASSERT( farrVertices[ at ].type == eVertexType_Arc );
	farrVertices[ at ].dArcRadius	= dRadius;
}

bool VWPolygon2D::GetVertexVisible(size_t at) const
{
	VWFC_ASSERT( at < farrVertices.size() );
	return farrVertices[ at ].bVisible;
}

void VWPolygon2D::SetVertexVisible(size_t at, bool bVisible)
{
	VWFC_ASSERT( at < farrVertices.size() );
	farrVertices[ at ].bVisible		= bVisible;
}

// return a line from (at, at+1) vertex (at the end the line is (len-1, 0)
VWLine2D VWPolygon2D::GetLineAt(size_t at) const
{
	VWFC_ASSERT( at < farrVertices.size() );
	size_t	next	= at + 1;
	if ( next >= farrVertices.size() )
		next	= 0;

	return VWLine2D( farrVertices[ at ].pt, farrVertices[ next ].pt );
}

void VWPolygon2D::RecalculateBoundingBox()
{
	fdMinX		= 0;
	fdMaxX		= 0;
	fdMinY		= 0;
	fdMaxY		= 0;

	size_t	len	= farrVertices.size();
	if ( len > 0 )
	{
		const	VWPoint2D&	pt	= farrVertices[ 0 ].pt;
		fdMinX		= pt.x;
		fdMaxX		= pt.x;
		fdMinY		= pt.y;
		fdMaxY		= pt.y;

		for(size_t i=1; i<len; i++)
		{
			const	VWPoint2D&	pt	= farrVertices[ i ].pt;
			fdMinX		= min( fdMinX, pt.x );
			fdMaxX		= max( fdMaxX, pt.x );
			fdMinY		= min( fdMinY, pt.y );
			fdMaxY		= max( fdMaxY, pt.y );
		}
	}
}

void VWPolygon2D::SetClosed(bool isClosed)
{
	fbIsClosed		= isClosed;
}

bool VWPolygon2D::IsClosed() const
{
	return fbIsClosed;
}

void VWPolygon2D::SetAllVertexVisible(bool bVisible)
{
	size_t	len	= farrVertices.size();
	for(size_t i=0; i<len; i++) {
	farrVertices[ i ].bVisible		= bVisible;
	}
}

void VWPolygon2D::GetPolygonBounds(double& outMinX, double& outMaxX, double& outMinY, double& outMaxY) const
{
	outMinX		= fdMinX;
	outMaxX		= fdMaxX;
	outMinY		= fdMinY;
	outMaxY		= fdMaxY;
}

VWRectangle2D VWPolygon2D::GetPolygonBounds() const
{
	return VWRectangle2D( fdMinX, fdMaxX, fdMaxY, fdMinY );
}

VWPoint2D VWPolygon2D::GetPolygonBoundsCenter() const
{
	return VWPoint2D( (fdMinX+fdMaxX)/2, (fdMinY+fdMaxY)/2 );
}

// reverse the order of the points (if CW to CCW or CCW to CW)
void VWPolygon2D::ReversePoints(const bool& kbDoFullReversion  /* = false */)
{
 	size_t	len	= farrVertices.size();
	for(size_t i=0; i<len / 2; i++)
	{
		/*
			Get the vertices to be exchanged as references, so that to avoid the multiple
			use of the array access operator. A problem could arise if copies of the vertices
			are generated and modified instead of editing the elements of the farrVertices STL vector.
			Actually, STL vector's operator[] returns either a modifiable reference or a constant
			reference to the visited element. Therefore, it is guaranteed that the following expression:
			"SVertex& leftVertex = farrVertices[ i ];" won't make the compiler copy the vertex
			"farrVertices[ i ]" to a temporary implicit ( hidden ) variable of SVertex type
			and point the reference "SVertex& leftVertex" to it.
			Another possible flaw when using references to dynamically allocated data
			would occur if the it is expanded or diminished e.g. the reserved memory
			for the data is reallocated. Then there is a great chance to lose the reference.
			This case is impossible to be encountered, since we are only modifying the data
			e.g. what farrVertices points to, not adding or removing data elements
			( changing its size thus changing the value held by farrVertices ).
		*/
		SVertex&		leftVertex 			= farrVertices[ i ];
		VWPoint2D		temp				= leftVertex.pt;
		const size_t	knRghtVertexIndex	= len-i-1;
		SVertex&		rghtVertex 			= farrVertices[ knRghtVertexIndex ];
		leftVertex.pt						= rghtVertex.pt;
		rghtVertex.pt						= temp;

		if (kbDoFullReversion) {
			// Obtain the additional data from the LEFT vertex.
			const EVertexType	kLeftVertexType			= leftVertex.type;
			const double		kdLeftVertexArcRadius	= leftVertex.dArcRadius;
			const bool			kbLeftVertexIsVisible	= leftVertex.bVisible;

			// Copy the data of the RIGHT vertex to that of the LEFT one.
			leftVertex.type			= rghtVertex.type;
			leftVertex.dArcRadius	= rghtVertex.dArcRadius;
			leftVertex.bVisible		= rghtVertex.bVisible;

			// Set the data of the RIGHT vertex with that, which has already been
			// gathered above from the LEFT vertex.
			rghtVertex.type			= kLeftVertexType;
			rghtVertex.dArcRadius	= kdLeftVertexArcRadius;
			rghtVertex.bVisible		= kbLeftVertexIsVisible;
		}
	}

	this->RecalculateBoundingBox();
}

// return true if the passed polygon has more than 3 different vertices
bool VWPolygon2D::IsValidPolygon(double dEpsilon) const
{
	size_t	len	= farrVertices.size();
	if ( len < 3 )
		return false;

	size_t		count	= 1;
	VWPoint2D	pt		= farrVertices[ 0 ].pt;
	for(size_t i=1; i<len; i++)
	{
		double_gs	dist	= ( pt - farrVertices[ i ].pt ).Magnitude();
		if ( dist > dEpsilon )
		{
			count++;
			if ( count >= 3 )
				return true;
		}

		pt			= farrVertices[ i ].pt;
	}

	return count >= 3;
}

// removed all same points in the polygon
void VWPolygon2D::CleanUpPolygon(double dEpsilon)
{
	VWPoint2D					lastPt;
	TVertexArray::iterator		it		= farrVertices.begin();
	while( it != farrVertices.end() )
	{
		if ( it == farrVertices.begin() ) 
		{
			lastPt		= it->pt;
			++it;
		}
		else
		{
			double_gs	dist	= ( lastPt - it->pt ).Magnitude();
			if ( dist < dEpsilon )
			{
				// remove the unnecessary point
				farrVertices.erase( it );
				// start all over
				it		= farrVertices.begin();
			}
			else {
				lastPt		= it->pt;
				++it;
			}
		}
	}

	this->RecalculateBoundingBox();
}

// get the bouding box which is parallel to given line
// the result polygon contains 4 vertices of the bounding box
void VWPolygon2D::GetPolygonBounds(const VWLine2D& line, VWPolygon2D& outBounds) const
{
	VWPoint2D			basePt1		= line.GetStart();

	VWPoint2D			minPt, maxPt;
	VWPoint2D			minProjPt, maxProjPt;
	// min and max distance from the begining of the line (face bearing line)
	double_gs			minProjDist, maxProjDist;
	// mian am max dist from the poly to the line
	double_gs			minDist, maxDist;
	size_t				vertCnt		= farrVertices.size();
	for(size_t v=0; v<vertCnt; v++)
	{
		VWPoint2D	pt			= farrVertices[ v ].pt;
		VWPoint2D	projPt		= line.PtPerpLine( pt );
		double_gs	dist		= ( projPt - pt ).Magnitude();
		double_gs	projDist	= ( projPt - basePt1 ).Magnitude();
		if ( v == 0 )
		{
			// first time - init
			minPt			= maxPt				= pt;
			minProjPt		= maxProjPt			= projPt;
			minProjDist		= maxProjDist		= projDist;
			minDist			= maxDist			= dist;
		}
		else
		{
			if ( projDist < minProjDist )
			{
				minPt			= pt;
				minProjPt		= projPt;
				minProjDist		= projDist;
			}
			if ( projDist > maxProjDist )
			{
				maxPt			= pt;
				maxProjPt		= projPt;
				maxProjDist		= projDist;
			}
			if ( dist < minDist )
				minDist		= dist;
			if ( dist > maxDist )
				maxDist		= dist;
		}
	}

	VWPoint2D	minDir	= (minPt - minProjPt).Normalize();
	VWPoint2D	maxDir	= (maxPt - maxProjPt).Normalize();

	// calculate the four points of the rotated bounding box
	VWPoint2D	a		= minProjPt + minDir * minDist;
	VWPoint2D	b		= maxProjPt + minDir * minDist;
	VWPoint2D	c		= minProjPt + maxDir * maxDist;
	VWPoint2D	d		= maxProjPt + maxDir * maxDist;

	outBounds.ClearVertices();
	outBounds.AddVertex( a );
	outBounds.AddVertex( b );
	outBounds.AddVertex( d );
	outBounds.AddVertex( c );
}

// get the bounding box which is parallel to the given direction (a similar approach).
// the result is 4 vertices (UL, UR, LL, LR) of the bounding box
// XXX Bobi: in contrast to the previous, this one first polygonizes 
// the polygon and then gets the bounding box. It works for arbitrary polylines.
void VWPolygon2D::GetPolygonBounds(const VWPoint2D& vDirection, VWPoint2D& vUL, VWPoint2D& vUR, VWPoint2D& vLL, VWPoint2D& vLR) const
{
	// the direction have to be defined
	ASSERTN( kVStanev, ( vDirection.Magnitude() > VWPoint2D::sEpsilon ) );

	// get polygonized polygon
	VWPolygon2D			poly2DPolygonized;
	VWPolygon2DObj		poly2DObj( ::GS_ConvertToPolygonN( gCBP, VWPolygon2DObj( *this ), false , 32 ) );
	poly2DObj.GetPolygon( poly2DPolygonized );
	poly2DObj.DeleteObject( false );

	// normalize the vector
	VWPoint2D			vDir		= vDirection;
	vDir.Normalize();

	VWPoint2D			vPerpDir	= vDir.Perp();
	vPerpDir.Normalize();

	// four zero vectors
	VWPoint2D			vIMax, vIMin, vJMax, vJMin;

	const VWPoint2D&	vInit		= poly2DPolygonized.GetVertexAt( 0 );
	size_t				vertCount	= poly2DPolygonized.GetVertexCount();

	for ( size_t i = 1 ; i < vertCount ; i ++ ) {

		// get current
		const VWPoint2D&	vCurr	= poly2DPolygonized.GetVertexAt( i ) - vInit;

		// project the current onto directional vector and on its perpendicular
		VWPoint2D			vPara	= vDir     * ( vCurr.DotProduct( vDir     ) / vDir.DotProduct( vDir         ) );
		VWPoint2D			vPerp	= vPerpDir * ( vCurr.DotProduct( vPerpDir ) / vPerpDir.DotProduct( vPerpDir ) );

		// get normalized vectors
		VWPoint2D			vParaNorm	= vPara;
		VWPoint2D			vPerpNorm	= vPerp;
		vParaNorm.Normalize();
		vPerpNorm.Normalize();
		
		if ( ( vParaNorm - vDir ).Magnitude() < VWPoint2D::sEpsilon ) {
			if ( vPara.Magnitude() > vIMax.Magnitude() )
				vIMax	= vPara;
		}
		else {
			if ( vPara.Magnitude() > vIMin.Magnitude() )
				vIMin	= vPara;
		}

		if ( ( vPerpNorm - vPerpDir ).Magnitude() < VWPoint2D::sEpsilon ) {
			if ( vPerp.Magnitude() > vJMax.Magnitude() )
				vJMax	= vPerp;
		}
		else {
			if ( vPerp.Magnitude() > vJMin.Magnitude() )
				vJMin	= vPerp;
		}
	}

	// find the bounding box polygon in the given direction
	vUL	=	vInit + vIMin + vJMax;
	vUR	=	vInit + vIMax + vJMax;
	vLL	=	vInit + vIMin + vJMin;
	vLR	=	vInit + vIMax + vJMin;
}

/////////////////////////////////////////////////////////////////////////////////
// Mathematics
/////////////////////////////////////////////////////////////////////////////////

void VWPolygon2D::Transform(const VWTransformMatrix& mat)
{
	fdMinX		= 0;
	fdMaxX		= 0;
	fdMinY		= 0;
	fdMaxY		= 0;

	size_t	len	= farrVertices.size();
	for(size_t i=0; i<len; i++)
	{
		VWPoint2D		pt		= farrVertices[ i ].pt;
		if ( i == 0 )
		{
			fdMinX		= pt.x;
			fdMaxX		= pt.x;
			fdMinY		= pt.y;
			fdMaxY		= pt.y;
		}
		else
		{
			fdMinX		= min( fdMinX, pt.x );
			fdMaxX		= max( fdMaxX, pt.x );
			fdMinY		= min( fdMinY, pt.y );
			fdMaxY		= max( fdMaxY, pt.y );
		}
		VWPoint3D		p( pt.x, pt.y, 0 );
		p = mat.PointTransform( p );
		farrVertices[ i ].pt.SetPoint( p.x, p.y );
	}
}

void VWPolygon2D::MovePolygon(double cx, double cy)
{
	fdMinX		= 0;
	fdMaxX		= 0;
	fdMinY		= 0;
	fdMaxY		= 0;

	size_t	len	= farrVertices.size();
	for(size_t i=0; i<len; i++)
	{
		farrVertices[i].pt.x		+= cx;
		farrVertices[i].pt.y		+= cy;

		if ( i == 0 )
		{
			fdMinX		= farrVertices[i].pt.x;
			fdMaxX		= farrVertices[i].pt.x;
			fdMinY		= farrVertices[i].pt.y;
			fdMaxY		= farrVertices[i].pt.y;
		}
		else
		{
			fdMinX		= min( fdMinX, farrVertices[i].pt.x );
			fdMaxX		= max( fdMaxX, farrVertices[i].pt.x );
			fdMinY		= min( fdMinY, farrVertices[i].pt.y );
			fdMaxY		= max( fdMaxY, farrVertices[i].pt.y );
		}
	}
}

// rotate the polygon by the center of the bounding box
void VWPolygon2D::Rotate(double degAngle)
{
	double_gs	x		= fdMinX + ( fdMaxX - fdMinX ) / 2;
	double_gs	y		= fdMinY + ( fdMaxY - fdMinY ) / 2;
	this->MovePolygon( -x, -y );

	double_gs	alpha	= MathUtils::Deg2Rad( degAngle );
	double_gs	s		= sin( alpha );
	double_gs	c		= cos( alpha );
	TransformMatrix		rotationMatrix;
	rotationMatrix.SetToIdentity();
	rotationMatrix.SetRow0( NormalVector(  c, s, 0 ) );
	rotationMatrix.SetRow1( NormalVector( -s, c, 0 ) );
	rotationMatrix.SetRow2( NormalVector(  0, 0, 1 ) );
	this->Transform( rotationMatrix );

	this->MovePolygon( x, y );
}

// rotate by specified center
void VWPolygon2D::Rotate(const VWPoint2D& pt, double degAngle)
{
	this->MovePolygon( -pt.x, -pt.y );

	double_gs	alpha	= MathUtils::Deg2Rad( degAngle );
	double_gs	s		= sin( alpha );
	double_gs	c		= cos( alpha );
	TransformMatrix		rotationMatrix;
	rotationMatrix.SetToIdentity();
	rotationMatrix.SetRow0( NormalVector(  c, s, 0 ) );
	rotationMatrix.SetRow1( NormalVector( -s, c, 0 ) );
	rotationMatrix.SetRow2( NormalVector(  0, 0, 1 ) );
	this->Transform( rotationMatrix );

	this->MovePolygon( pt.x, pt.y );
}

// calculate the area of the polygon
// A = 1/2 Sum(i:=0 to N-1)( x(i)*y(i+1) - x(i+1)*y(i) )
double VWPolygon2D::GetArea() const
{
	double	dArea			= 0;

	size_t	nVertexCount	= farrVertices.size();
	if ( nVertexCount == 0 )
		return dArea;

	VWPoint2D		a		= farrVertices[ 0 ].pt;
	VWPoint2D		b;
	VWPoint2D		c;

	for(size_t i=1; i<nVertexCount-1; i++) {

		b		= farrVertices[ i ].pt;
		c		= farrVertices[ i + 1 ].pt;

		// Get the area
		double_gs	triArea		= VWPoint2D::TriArea( a, b, c );

		// Calculate the area
		dArea		= dArea + triArea;
	}

	/*double dOldArea = dArea;
	/***********************************
	 * other NOT working VERSION
	 **********************************

	dArea			= 0;
	for(size_t i=0; i<nVertexCount-1; i++) {
		const	VWPoint2D&	p0		= farrVertices[ i ].pt;
		const	VWPoint2D&	p1		= farrVertices[ i + 1 ].pt;

		dArea		+= p0.x*p1.y - p1.x*p0.y;
	}
	dArea		/= 2;

	VWFC_ASSERT( VWFC::Math::MathUtils::Equalish( dArea, dOldArea, 0.0001 ) );
	*/

	return dArea;
}

bool VWPolygon2D::IsClockWise() const
{
	double	area	= this->GetArea();
	return area <= 0;
}

// calculate the perimeter of the polygon
double VWPolygon2D::GetPerimeter() const
{
	double	dPerimeter			= 0;

	size_t	nVertexCount	= farrVertices.size();
	if ( nVertexCount == 0 )
		return dPerimeter;

	size_t	len	= nVertexCount;
	if ( ! fbIsClosed )
		len--;
	for(size_t i=0; i<len; i++) {
		VWPoint2D	a, b;
		a			= farrVertices[ i ].pt;
		b			= i+1 < nVertexCount ? farrVertices[ i+1 ].pt : farrVertices[ 0 ].pt;

		dPerimeter	+= a.DistanceTo( b );
	}

	return dPerimeter;
}

// check if polygon is equalish to this
bool VWPolygon2D::EqualishTo(const VWPolygon2D& poly) const
{
	size_t	thisCnt	= this->GetVertexCount();
	size_t	polyCnt	= poly.GetVertexCount();

	bool	bEqual	= true;
	if ( thisCnt == polyCnt ) {

		// find positions coincidence
		bool	bPosCoincide	= false;
		size_t	thisSupl, polySupl;
		for ( size_t i = 0 ; i < thisCnt ; i ++ ) {
			for ( size_t j = 0 ; j < thisCnt ; j ++ ) {

				const VWPoint2D&	pt1	= this->GetVertexAt( i );
				const VWPoint2D&	pt2	= poly.GetVertexAt( j );
				if ( MathUtils::Equalish( pt1, pt2, VWPoint2D::sEpsilon ) ) {
					thisSupl		= i;
					polySupl		= j;
					bPosCoincide	= true;
					break;
				}
			}
			if ( bPosCoincide )
				break;
		}
		
		if ( bPosCoincide && thisCnt > 0 ) {
			for ( size_t i = 0 ; i < thisCnt ; i ++ ) {

				// corresponding indices
				size_t	thisIndex	= ( i + thisSupl ) % thisCnt;
				size_t	polyIndex	= ( i + polySupl ) % thisCnt;

				// check types
				EVertexType	vType1	= this->GetVertexType( thisIndex );
				EVertexType	vType2	= poly.GetVertexType( polyIndex );
				if ( vType1 != vType2 ) {
					bEqual	= false;
					break;
				}
				// check radiuses
				if ( vType1 == eVertexType_Arc && 
					this->GetArcVertexRadius( thisIndex ) != poly.GetArcVertexRadius( polyIndex ) ) {
					bEqual	= false;
					break;
				}
				// check positions
				if ( ! MathUtils::Equalish( this->GetVertexAt( thisIndex ), poly.GetVertexAt( polyIndex ), VWPoint2D::sEpsilon ) ) {
					bEqual	= false;
					break;
				}
			}
		}
		else {
			bEqual	= false;
		}
	}
	else {
		bEqual	= false;
	}
	return bEqual;
}

// is specified point on the edge of the specified polygon
bool VWPolygon2D::IsPtOnPoly2D(const VWPoint2D& pt, double dEpsilon) const
{
	bool	onPoly	= false;
	bool	inPoly	= false;
	this->IsPtOnPoly2D( pt.x, pt.y, onPoly, inPoly, dEpsilon );
	return onPoly;
}

// determines if a point is in the area of a given polygon (the 2D projection)
bool VWPolygon2D::IsPtInPoly2D(double ptX, double ptY, double dEpsilon) const
{
	bool	onPoly	= false;
	bool	inPoly	= false;
	this->IsPtOnPoly2D( ptX, ptY, onPoly, inPoly, dEpsilon );
	return inPoly;
}

bool VWPolygon2D::IsPtInPoly2D(const VWPoint2D& pt, double dEpsilon) const
{
	bool	onPoly	= false;
	bool	inPoly	= false;
	this->IsPtOnPoly2D( pt.x, pt.y, onPoly, inPoly, dEpsilon );
	return inPoly;
}

bool VWPolygon2D::IsPtOnPoly2D(double ptX, double ptY, double dEpsilon) const
{
	bool	onPoly	= false;
	bool	inPoly	= false;
	this->IsPtOnPoly2D( ptX, ptY, onPoly, inPoly, dEpsilon );
	return onPoly;
}

// combined operations
bool VWPolygon2D::IsPtInOrOnPoly2D(double ptX, double ptY, double dEpsilon) const
{
	bool	onPoly	= false;
	bool	inPoly	= false;
	this->IsPtOnPoly2D( ptX, ptY, onPoly, inPoly, dEpsilon );
	return onPoly || inPoly;
}

bool VWPolygon2D::IsPtInOrOnPoly2D(const VWPoint2D& pt, double dEpsilon) const
{
	bool	onPoly	= false;
	bool	inPoly	= false;
	this->IsPtOnPoly2D( pt.x, pt.y, onPoly, inPoly, dEpsilon );
	return onPoly || inPoly;
}

void VWPolygon2D::IsPtOnPoly2D(const VWPoint2D& pt, bool& outOnPoly, bool& outInPoly, double dEpsilon) const
{
	this->IsPtOnPoly2D( pt.x, pt.y, outOnPoly, outInPoly, dEpsilon );
}

void VWPolygon2D::IsPtOnPoly2D(double ptX, double ptY, bool& outOnPoly, bool& outInPoly, double dEpsilon) const
{
	VWPoint2D	inPt( ptX, ptY );

	outOnPoly			= false;
	int		cn			= 0;    // the crossing number counter

	size_t	polyPtCnt	= farrVertices.size();
	// loop through all edges of the polygon
	for (size_t i=0; i<polyPtCnt; i++) {    // edge from V[i] to V[i+1]
		const VWPoint2D&	pt		= farrVertices[i].pt;
		const VWPoint2D&	nextPt	= i+1 < polyPtCnt ? farrVertices[i+1].pt : farrVertices[0].pt;

		outOnPoly	= VWLine2D::PtOnLine( inPt, pt, nextPt, dEpsilon );
		// if it is on poly break -- we have 'on poly' and dont have 'in poly'
		if ( outOnPoly ) 
			break;

		double				Vix		= pt.x;
		double				Viy		= pt.y;
		double				Vi1x	= nextPt.x;
		double				Vi1y	= nextPt.y;

		if (((Viy <= ptY) && (Vi1y > ptY))    // an upward crossing
			|| ((Viy > ptY) && (Vi1y <= ptY))) { // a downward crossing
			// compute the actual edge-ray intersect x-coordinate
			double vt = (double)(ptY - Viy) / (Vi1y - Viy);
			if ( ptX < Vix + vt * (Vi1x - Vix) ) // P.x < intersect
				++cn;   // a valid crossing of y=P.y right of P.x
		}
	}

	// if we have 'on poly' means that we dont have 'in poly'
	if ( ! outOnPoly ) {
		outInPoly	= ( cn & 1 ) ? true : false;    // 0 if even (out), and 1 if odd (in)
	}
	else {
		outInPoly	= false;
	}

/*	size_t	len		= farrVertices.size();
	if ( len <= 2 )
		return false;

	// find Inside poly 
	VWPoint2D	p	= pt;
	VWPoint2D	thisPt	= farrVertices[ len - 1 ].pt;
	VWPoint2D	lastPt;

	bool right1		= thisPt.x >= pt.x;
	bool high1		= thisPt.y >= pt.y;

	bool right2, high2;

	short	cross	= 0;
	for (size_t i=0; i<len; i++)
	{
		lastPt	= thisPt;
		thisPt	= farrVertices[ i ].pt;

		if ( thisPt.Equal( lastPt, dEpsilon ) )
			continue;

		VWLine2D	line( thisPt, lastPt );
		if ( line.PtOnLine( p, dEpsilon ) )
		{
			cross	= 0;
			break;
		}

		right2 = thisPt.x >= pt.x;
		high2  = thisPt.y >= pt.y;

		if ( high1 != high2 )
		{
			if ( right1 != right2 )
			{
				// cross=maybe			
				double_gs	test = lastPt.x + (thisPt.x - lastPt.x)*(p.y - lastPt.y)/(double_gs)(thisPt.y - lastPt.y);

				if (test == 0)
				{
					cross = 0;
					break;
				//	return false;
				}
				else if (test >= p.x) 
					cross++;
			}
			else {
				if (right1) {
					if ( thisPt.x == pt.x && lastPt.x == pt.x ) {
						//AND right2 follows, since theY are Equalrich:test this for new Clip,
						// if pt lay on horizontal edge, then it is not
						// Inside
						
						cross	= 0;
						break;
						//return false;
					}
					cross++;
				}
			}
		}
		right1	= right2;
		high1	= high2;
	}


	bool res = (cross & 1) != 0L;
	return res;*/
}

// return true if the specified line intersects the poly
bool VWPolygon2D::IsIntersecting(const VWLine2D& line, double dEpsilon, VWPoint2D* /*out*/ pInsertPt/*=NULL*/) const
{
	// not using epsilon -- but we should
	dEpsilon;

	size_t		vertsCnt	= farrVertices.size();
	size_t		len			= vertsCnt;
	if ( len == 0 )
		return false;

	// dont check the last legment if this is open poly
	if ( ! fbIsClosed )
		len --;

	if ( len == 0 )
		return false;

	VWPoint2D	insertPt;
	VWPoint2D	p			= farrVertices[ 0 ].pt;
	for(size_t i=1; i<=len; i++)
	{
		VWPoint2D		q;
		if ( i == vertsCnt )	q	= farrVertices[ 0 ].pt;
		else					q	= farrVertices[ i ].pt;

		VWLine2D	curLine( p, q );
		if ( line.IntersectLineSegments( curLine, insertPt ) )
		{
			if ( pInsertPt )
				(*pInsertPt)		= insertPt;
			return true;
		}

		p		= q;
	}

	return false;
}

bool VWPolygon2D::IsIntersecting(const VWPoint2D& a, const VWPoint2D& b, double dEpsilon, VWPoint2D* /*out*/ pInsertPt/*=NULL*/) const
{
	// not using epsilon -- but we should
	dEpsilon;

	size_t		len			= farrVertices.size();
	if ( len == 0 )
		return false;

	// dont check the last legment if this is open poly
	if ( ! fbIsClosed )
		len --;

	if ( len == 0 )
		return false;

	VWPoint2D	insertPt;
	VWPoint2D	p			= farrVertices[ 0 ].pt;
	for(size_t i=1; i<=len; i++)
	{
		VWPoint2D		q;
		if ( i == len )	q	= farrVertices[ 0 ].pt;
		else			q	= farrVertices[ i ].pt;

		VWLine2D	curLine( p, q );
		if ( curLine.IntersectLineSegments( a, b, insertPt ) )
		{
			if ( pInsertPt )
				(*pInsertPt)		= insertPt;
			return true;
		}

		p		= q;
	}

	return false;
}

// return true if the specified line intersects the poly
bool VWPolygon2D::IsIntersecting(const VWLine2D& line, double dEpsilon, TSTLPoints2DArray& outArrIntersectPts) const
{
	bool bSimilarPts = true;
	size_t		vertsCnt	= farrVertices.size();
	size_t		len			= vertsCnt;
	if ( len == 0 )
		return false;

	// dont check the last legment if this is open poly
	if ( ! fbIsClosed )
		len --;

	if ( len == 0 )
		return false;

	VWPoint2D	insertPt;
	VWPoint2D	p			= farrVertices[ 0 ].pt;
	bool foundIntersectPoint = false;
	for(size_t i=1; i<=len; i++)
	{
		VWPoint2D		q;
		if ( i == vertsCnt )	q	= farrVertices[ 0 ].pt;
		else					q	= farrVertices[ i ].pt;

		VWLine2D	curLine( p, q );
		if ( line.IntersectLineSegments( curLine, insertPt ) )
		{
			size_t arraySize = outArrIntersectPts.size();

			if( arraySize == 0 ) {
				outArrIntersectPts.push_back( insertPt );
				foundIntersectPoint = true;
			}
			else {
				for( size_t k = 0; k < arraySize; k++ ) {
					if( !WorldPtsAreRelativelyClose( insertPt, outArrIntersectPts[k], dEpsilon ) ) {
						bSimilarPts = false;
					}
				}

				if( !bSimilarPts ) {
					outArrIntersectPts.push_back( insertPt );
					foundIntersectPoint = true;
				}
			}
		}

		p		= q;
	}

	return foundIntersectPoint;
}

bool VWPolygon2D::IsIntersecting(const VWPoint2D& a, const VWPoint2D& b, double dEpsilon, TSTLPoints2DArray& outArrIntersectPts) const
{
	VWLine2D tempLine( a, b );

	return this->IsIntersecting( tempLine, dEpsilon, outArrIntersectPts );

	/*
	// not using epsilon -- but we should
	dEpsilon;

	size_t		len			= farrVertices.size();
	if ( len == 0 )
		return false;

	// dont check the last legment if this is open poly
	if ( ! fbIsClosed )
		len --;

	if ( len == 0 )
		return false;

	VWPoint2D	insertPt;
	VWPoint2D	p			= farrVertices[ 0 ].pt;
	bool foundIntersectPoint = false;
	for(size_t i=1; i<=len; i++)
	{
		VWPoint2D		q;
		if ( i == len )	q	= farrVertices[ 0 ].pt;
		else			q	= farrVertices[ i ].pt;

		VWLine2D	curLine( p, q );
		if ( curLine.IntersectLineSegments( a, b, insertPt ) )
		{
			outArrIntersectPts.push_back( insertPt );
			foundIntersectPoint = true;
		}

		p		= q;
	}

	return foundIntersectPoint;*/
}

// return true if the given polygon and this polygon intersects each other
// intersect 'true' means that they have intersection points or same points
bool VWPolygon2D::IsIntersecting(const VWPolygon2D& poly, double dEpsilon) const
{
	size_t		polyLen		= poly.farrVertices.size();
	if ( polyLen == 0 )
		return false;

	// dont consider the last line (end-start) if poly is open
	if ( ! poly.IsClosed() )
		polyLen--;

	bool		bInters		= false;
	for(size_t i=0; i<polyLen; i++)
	{
		VWLine2D		line	= poly.GetLineAt( i );
		if ( this->IsIntersecting( line, dEpsilon, NULL ) ) {
			bInters		= true;
			break;
		}
	}

	return bInters;
}

// returns true if the given polygon is fully within this polygon
bool VWPolygon2D::IsFullyInside(const VWPolygon2D& poly, double dEpsilon) const
{
	bool	bIsFullyInside	= false;
	bool	bInters			= this->IsIntersecting( poly, dEpsilon );
	if ( bInters ) {
		bIsFullyInside	= false;
	}
	else {
		bIsFullyInside			= true;

		size_t		polyLen		= poly.farrVertices.size();
		for(size_t i=0; i<polyLen; i++)
		{
			VWPoint2D		pt		= poly.farrVertices[ i ].pt;
			bool			onPoly	= false;
			bool			inPoly	= false;
			this->IsPtOnPoly2D( pt, onPoly, inPoly, dEpsilon );

			if ( ! onPoly && ! inPoly ) {
				bIsFullyInside	= false;
				break;	// STOP!
			}
		}
	}

	return bIsFullyInside;
}

// returns true if the given polygon is fully outside this polygon
bool VWPolygon2D::IsFullyOutside(const VWPolygon2D& poly, double dEpsilon) const
{
	bool	bIsFullyOutside	= false;
	bool	bInters			= this->IsIntersecting( poly, dEpsilon );
	if ( bInters ) {
		bIsFullyOutside	= false;
	}
	else {
		bIsFullyOutside			= true;

		size_t		polyLen		= poly.farrVertices.size();
		for(size_t i=0; i<polyLen; i++) {
			VWPoint2D		pt		= poly.farrVertices[ i ].pt;
			bool			onPoly	= false;
			bool			inPoly	= false;
			this->IsPtOnPoly2D( pt, onPoly, inPoly, dEpsilon );

			if ( onPoly || inPoly ) {
				bIsFullyOutside	= false;
				break;	// STOP!
			}
		}
	}

	return bIsFullyOutside;
}


// returns true if the given polygon is fully within this polygon, including having common points
bool VWPolygon2D::IsInside(const VWPolygon2D& poly, double dEpsilon) const
{
	bool	bIsInside			= true;

	size_t		polyLen		= poly.farrVertices.size();
	for(size_t i=0; i<polyLen; i++)
	{
		VWPoint2D		pt		= poly.farrVertices[ i ].pt;
		bool			onPoly	= false;
		bool			inPoly	= false;
		this->IsPtOnPoly2D( pt, onPoly, inPoly, dEpsilon );

		if ( ! onPoly && ! inPoly ) {
			bIsInside	= false;
			break;	// STOP!
		}
	}

	return bIsInside;
}

// returns true if the given polygon is fully outside this polygon, including having common points
bool VWPolygon2D::IsOutside(const VWPolygon2D& poly, double dEpsilon) const
{
	bool bIsOutside			= true;

	size_t		polyLen		= poly.farrVertices.size();
	for(size_t i=0; i<polyLen; i++) {
		VWPoint2D		pt		= poly.farrVertices[ i ].pt;
		bool			onPoly	= false;
		bool			inPoly	= false;
		this->IsPtOnPoly2D( pt, onPoly, inPoly, dEpsilon );

		if ( onPoly || inPoly ) {
			bIsOutside	= false;
			break;	// STOP!
		}
	}

	return bIsOutside;
}

// get geometry center of the polygon
VWPoint2D VWPolygon2D::GetCenter() const
{
	VWPoint2D	centerPt;

	size_t		polyLen		= farrVertices.size();
	for(size_t i=0; i<polyLen; i++) {
		const VWPoint2D&	pt		= farrVertices[ i ].pt;
		if ( i == 0 )
			centerPt		= pt;
		else
			centerPt		+= pt;
	}

	centerPt	/= polyLen;

	return centerPt;
}

// get random point that lies in the poly
VWPoint2D VWPolygon2D::GetRandomPtInPoly(double dEpsilon) const
{
	VWPoint2D	pt;
	do
	{
		pt.x	= MathUtils::RealRandom( fdMinX, fdMaxX );
		pt.y	= MathUtils::RealRandom( fdMinY, fdMaxY );
	}while ( !this->IsPtInPoly2D( pt, dEpsilon ) );
	return pt;
}


// get bounding points along line
// the result points are projection on the line of the two points from the poly wich are bound points along the line
void VWPolygon2D::GetBoundingProjOnLine(VWPoint2D&/*in-out*/ minProjPt, VWPoint2D&/*in-out*/ maxProjPt, double dEpsilon) const
{
	// not using epsilon -- but we should
	dEpsilon;

	VWLine2D			line( minProjPt, maxProjPt );
	
	// min and max distance from the begining of the line (face bearing line)
	double				minProjDist;
	double				maxProjDist;
	size_t				vertCnt	= farrVertices.size();
	for(size_t v=0; v<vertCnt; v++)
	{
		VWPoint2D	pt			= farrVertices[ v ].pt;
		VWPoint2D	projPt		= line.PtPerpLine( pt );
		//double		dist		= ( projPt - pt ).Magnitude();
		double		projDist	= ( projPt - line.GetStart() ).Magnitude();
		if ( v == 0 )
		{
			// first time - init
			minProjPt = maxProjPt			= projPt;
			minProjDist = maxProjDist		= projDist;
		}
		else
		{
			if ( projDist < minProjDist )
			{
				minProjPt		= projPt;
				minProjDist		= projDist;
			}
			if ( projDist > maxProjDist )
			{
				maxProjPt		= projPt;
				maxProjDist		= projDist;
			}
		}
	}
}

// get an inside point near the vertex at
VWPoint2D VWPolygon2D::GetInsidePtNearVertexAt(size_t at)
{
	VWFC_ASSERT( at < farrVertices.size() );

	const double	kNearPtEpsilon	= 1000 * VWPoint2D::sEpsilon;

	VWPoint2D	pt		= this->GetVertexAt( at );
	VWPoint2D	prevPt;
	if ( at < 1 )	prevPt	= this->GetVertexAt( this->GetVertexCount() - 1 );
	else			prevPt	= this->GetVertexAt( at - 1 );
	VWPoint2D		nextPt	= this->GetVertexAt( at + 1, true );

	VWLine2D	line( prevPt, nextPt );
	VWPoint2D	midDir	= line.CenterPt() - pt;
	if ( midDir.Magnitude() < VWPoint2D::sEpsilon ) {
		VWPoint2D	vec		= nextPt - pt;
		midDir.SetPoint( vec.y, -vec.x );
	}
	midDir.Normalize();

	VWPoint2D	checkPt	= pt + midDir * kNearPtEpsilon;
	if ( this->IsPtInPoly2D( checkPt, VWPoint2D::sEpsilon ) ) {
		return	checkPt;
	}
	else {
		checkPt	= pt - midDir * kNearPtEpsilon;
		VWFC_ASSERT( this->IsPtInPoly2D( checkPt, VWPoint2D::sEpsilon ) );
		return	checkPt;
	}
}

// get set of inside points near polygon's vertices
TSTLPoints2DArray VWPolygon2D::GetInsideNearPts()
{
	size_t	ptsCnt	= this->GetVertexCount();
	TSTLPoints2DArray	resultArray;
	for ( size_t i = 0 ; i < ptsCnt ; i ++ ) {
		resultArray.push_back( this->GetInsidePtNearVertexAt( i ) );
	}
	return	resultArray;
}

void VWPolygon2D::PointsTransform(const VWTransformMatrix& matrix)
{
 	size_t	len	= farrVertices.size();
	for(size_t i=0; i<len; i++) {
		VWPoint2D&	pt		= farrVertices[i].pt;
		pt					= matrix.PointTransform( pt );
	}
	this->RecalculateBoundingBox();
}

void VWPolygon2D::InversePointsTransform(const VWTransformMatrix& matrix)
{
 	size_t	len	= farrVertices.size();
	for(size_t i=0; i<len; i++) {
		VWPoint2D&	pt		= farrVertices[i].pt;
		pt					= matrix.InversePointTransform( pt );
	}
	this->RecalculateBoundingBox();
}

// is pt (x0,y0) is inside or/and on the specified triangle (x1,y1):(x2,y2):(x3,y3)
void VWPolygon2D::IsPtInTriangle(double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3, bool& outInTri, bool& outOnTri)
{
	double		b0	=  (x2 - x1) * (y3 - y1) - (x3 - x1) * (y2 - y1);
	double		b1	= ((x2 - x0) * (y3 - y0) - (x3 - x0) * (y2 - y0)) / b0;
	double		b2	= ((x3 - x0) * (y1 - y0) - (x1 - x0) * (y3 - y0)) / b0;
	double		b3	= ((x1 - x0) * (y2 - y0) - (x2 - x0) * (y1 - y0)) / b0;

//	outInTri		= b1 > 0 && b2 > 0 && b3 > 0;
//	outOnTri		= (b1 == 0 && b2 > 0 && b3 > 0)		||
//					  (b1 > 0 && b2 == 0 && b3 > 0)		||
//					  (b1 > 0 && b2 > 0 && b3 == 0)		||
//					  // is on points
//					  (b1 > 0 && b2 == 0 && b3 == 0)	||
//					  (b1 == 0 && b2 > 0 && b3 == 0)	||
//					  (b1 == 0 && b2 == 0 && b3 > 0) ;

#define EQ_ZERO(val)	MathUtils::Equalish( val, 0.0, VWPoint2D::sEpsilon )
#define GR_ZERO(val)	MathUtils::Greaterish( val, 0.0, VWPoint2D::sEpsilon )

	outInTri		= GR_ZERO(b1) && GR_ZERO(b2) && GR_ZERO(b3);
	outOnTri		= ( EQ_ZERO(b1) && GR_ZERO(b2) && GR_ZERO(b3) )		||
					  ( GR_ZERO(b1) && EQ_ZERO(b2) && GR_ZERO(b3) )		||
					  ( GR_ZERO(b1) && GR_ZERO(b2) && EQ_ZERO(b3))		||
					  // is on points
					  ( GR_ZERO(b1) && EQ_ZERO(b2) && EQ_ZERO(b3) )	||
					  ( EQ_ZERO(b1) && GR_ZERO(b2) && EQ_ZERO(b3) )	||
					  ( EQ_ZERO(b1) && EQ_ZERO(b2) && GR_ZERO(b3) ) ;

#undef EQ_ZERO
#undef GR_ZERO
}

