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

#include "StdAfx.h"


using namespace VWFC::Math;
using namespace VWFC::VWObjects;

VWWallObj::VWWallObj(const VWPoint2D& startPt, const VWPoint2D& endPt, double thickness/*=0*/)
{
	fhObject		= ::GS_CreateWall( gCBP, startPt, endPt, thickness );
	VWFC_ASSERT( fhObject );
	if ( fhObject == NULL )
		THROW_VWFC_EXCEPTION( VWCannotCreateObjectException, 0, "cannot create wall" );
}

VWWallObj::VWWallObj(const VWPoint2D& startPt, const VWPoint2D& ptOnArc, const VWPoint2D& endPt, double thickness/*=0*/)
{
	fhObject		= ::GS_CreateRoundWall( gCBP, startPt, ptOnArc, endPt, thickness );
	VWFC_ASSERT( fhObject );
	if ( fhObject == NULL )
		THROW_VWFC_EXCEPTION( VWCannotCreateObjectException, 0, "cannot create wall" );
}

VWWallObj::VWWallObj(const VWLine2D& line, double thickness)
{
	fhObject		= ::GS_CreateWall( gCBP, line.GetStart(), line.GetEnd(), thickness );
	VWFC_ASSERT( fhObject );
	if ( fhObject == NULL )
		THROW_VWFC_EXCEPTION( VWCannotCreateObjectException, 0, "cannot create wall" );
}

VWWallObj::VWWallObj(const VWArc2D& arc, double thickness)
{
	VWPoint2D	startPt, ptOnArc, endPt;
	arc.GetArc( startPt, ptOnArc, endPt );
	fhObject		= ::GS_CreateRoundWall( gCBP, startPt, ptOnArc, endPt, thickness );
	VWFC_ASSERT( fhObject );
	if ( fhObject == NULL )
		THROW_VWFC_EXCEPTION( VWCannotCreateObjectException, 0, "cannot create wall" );
}

VWWallObj::VWWallObj(MCObjectHandle h)
{
	short	type	= ::GS_GetObjectType( gCBP, h );
	VWFC_ASSERT( type == kWallNode || type == kRoundWallNode);
	if ( type != kWallNode && type != kRoundWallNode )
		THROW_VWFC_EXCEPTION( VWBadObjectTypeException, 0, "bad handle type when creating" );
	fhObject		= h;
	VWFC_ASSERT( fhObject != nil );
}

VWWallObj::VWWallObj(const VWWallObj& src)
{
	fhObject		= src.fhObject;
}

VWWallObj::~VWWallObj()
{
	fhObject		= nil;
}

VWWallObj& VWWallObj::operator=(const VWWallObj& src)
{
	fhObject		= src.fhObject;
	return *this;
}

VWPoint2D VWWallObj::GetStartPoint() const
{
	WorldPt			startPt;
	WorldPt			endPt;
	VWFC_ASSERT( fhObject );
	::GS_GetEndPoints( gCBP, fhObject, startPt, endPt );
	return startPt;
}

VWPoint2D VWWallObj::GetEndPoint() const
{
	WorldPt			startPt;
	WorldPt			endPt;
	VWFC_ASSERT( fhObject );
	::GS_GetEndPoints( gCBP, fhObject, startPt, endPt );
	return endPt;
}

void VWWallObj::GetPoints(VWPoint2D& startPt, VWPoint2D& endPt) const
{
	WorldPt			sPt;
	WorldPt			ePt;
	VWFC_ASSERT( fhObject );
	::GS_GetEndPoints( gCBP, fhObject, sPt, ePt );
	startPt			= sPt;
	endPt			= ePt;
}

bool VWWallObj::IsRound() const
{
	VWFC_ASSERT( fhObject );
	short type	= ::GS_GetObjectType( gCBP, fhObject );
	return type == kRoundWallNode;
}

VWArc2D VWWallObj::GetRoundWall() const
{
	VWArc2D		arc;

	VWFC_ASSERT( fhObject );
	short type	= ::GS_GetObjectType( gCBP, fhObject );
	VWFC_ASSERT( type == kRoundWallNode );
	if ( type == kRoundWallNode ) {
		WorldPt			cenPt;
		double			radius;
		double			startAngle, sweepAngle;
		::GS_GetArcInfoN( gCBP, fhObject, startAngle, sweepAngle, cenPt, radius, radius );

		arc.SetArc( cenPt, radius, startAngle, sweepAngle );
	}

	return arc;
}

VWLine2D VWWallObj::GetWall() const
{
	WorldPt			sPt;
	WorldPt			ePt;
	VWFC_ASSERT( fhObject );
	::GS_GetEndPoints( gCBP, fhObject, sPt, ePt );
	return VWLine2D( sPt, ePt );
}

bool VWWallObj::IsRoundCCW() const
{
	bool		ccw		= false;

	VWFC_ASSERT( fhObject );
	short type	= ::GS_GetObjectType( gCBP, fhObject );
	VWFC_ASSERT( type == kRoundWallNode );
	if ( type == kRoundWallNode ) {
		TVariableBlock	value;
		::GS_GetObjectVariable( gCBP, fhObject, ovRoundWallCounterClockwise, value );
		Boolean			isCCW;
		VERIFYN( kVStanev, value.GetBoolean( isCCW ) );
		ccw				= isCCW ? true : false;
	}

	return ccw;
}

// Switch the left and right side of a wall by reversing the direction of the wall.
// This is an interface to the button with the same name on the Object Info palette.
void VWWallObj::ReverseSides()
{
	VWFC_ASSERT( fhObject != nil );
	::GS_ReverseWallSides( gCBP, fhObject );
}

// Gets/Sets the heights of the start and end of the specified wall.
// The distances are measured from the ground plane on the layer in which the wall exists.
void VWWallObj::GetHeights(double& startHeightTop, double& startHeightBottom, double& endHeightTop, double& endHeightBottom) const
{
	VWFC_ASSERT( fhObject != nil );
	::GS_GetWallHeights( gCBP, fhObject, startHeightTop, startHeightBottom, endHeightTop, endHeightBottom );
}

double VWWallObj::GetStartHeightTop() const
{
	VWFC_ASSERT( fhObject != nil );
	double	startHeightTop, startHeightBottom, endHeightTop, endHeightBottom;
	::GS_GetWallHeights( gCBP, fhObject, startHeightTop, startHeightBottom, endHeightTop, endHeightBottom );
	return startHeightTop;
}

double VWWallObj::GetStartHeightBottom() const
{
	VWFC_ASSERT( fhObject != nil );
	double	startHeightTop, startHeightBottom, endHeightTop, endHeightBottom;
	::GS_GetWallHeights( gCBP, fhObject, startHeightTop, startHeightBottom, endHeightTop, endHeightBottom );
	return startHeightBottom;
}

void VWWallObj::SetHeights(double startHeightTop, double startHeightBottom, double endHeightTop, double endHeightBottom)
{
	VWFC_ASSERT( fhObject != nil );
	::GS_SetWallHeights( gCBP, fhObject, startHeightTop, startHeightBottom, endHeightTop, endHeightBottom );
}

// The width of the wall.
double VWWallObj::GetWidth() const
{
	VWFC_ASSERT( fhObject != nil );
	//return ::GS_GetWallWidth( gCBP, fhObject );
	double	width;
	::GS_WrapGetWallWidth( gCBP, fhObject, width );
	return width;
}

void VWWallObj::SetWidth(double width)
{
	VWFC_ASSERT( fhObject != nil );
	::GS_SetWallWidth( gCBP, fhObject, width );
}

// Available in VW Architect only. Moves the wall perpendiclar to its definition line by the specified offset,
// and maintains all wall connections Offsets to the left hand side of the wall are positive, and to the right hand side are negative.
void VWWallObj::MoveByOffset(double& offset)
{
	VWFC_ASSERT( fhObject != nil );
	::GS_MoveWallByOffset( gCBP, fhObject, offset );
}

// COMPONENTS ////////////////////////////////////////////////////////////////////////////////
size_t VWWallObj::GetComponentCount() const
{
	short	componentCount	= 0;
	
	::GS_GetNumberOfComponents( gCBP, fhObject, componentCount );

	return (size_t) componentCount;
}

SWallComponentInfo VWWallObj::GetComponentInfo(size_t index) const
{
	VWFC_ASSERT( fhObject != nil );
	VWFC_ASSERT(index < 32768);

	SWallComponentInfo info;

	VWFC_VERIFY( ::GS_GetComponentName( gCBP,  fhObject, (CBSignedShort) index, info.componentName ) );
	VWFC_VERIFY( ::GS_GetComponentWidth( gCBP,  fhObject, (CBSignedShort) index, info.width ) );
	VWFC_VERIFY( ::GS_GetComponentClass( gCBP,  fhObject, (CBSignedShort) index, info.componentClass ) );

	// fill
	CBUnsignedShort		fillFore, fillBack;
	VWFC_VERIFY( ::GS_GetComponentUseFillClassAttr( gCBP,  fhObject, (CBSignedShort) index, info.fillUseClass ) );
	VWFC_VERIFY( ::GS_GetComponentFill( gCBP,  fhObject, (CBSignedShort) index, info.fillPat ) );
	VWFC_VERIFY( ::GS_GetComponentFillColors( gCBP,  fhObject, (CBSignedShort) index, fillFore, fillBack ) );
	info.fillColorFore.SetColor( (UInt8) fillFore );
	info.fillColorBack.SetColor( (UInt8) fillBack );

	// lines
	CBUnsignedShort		penLeftFore, penLeftBack, penRightFore, penRightBack;
	VWFC_VERIFY( ::GS_GetComponentUsePenClassAttr( gCBP,  fhObject, (CBSignedShort) index, info.penLeftUseClass, info.penRightUseClass ) );
	VWFC_VERIFY( ::GS_GetComponentPenColors( gCBP,  fhObject, (CBSignedShort) index, penLeftFore, penLeftBack, penRightFore, penRightBack ) );
	VWFC_VERIFY( ::GS_GetComponentPenStyles( gCBP,  fhObject, (CBSignedShort) index, info.penLeftStyle, info.penRightStyle ) );
	VWFC_VERIFY( ::GS_GetComponentPenWeights( gCBP,  fhObject, (CBSignedShort) index, info.penLeftWeight, info.penRightWeight ) );

	info.penLeftColorFore.SetColor( (UInt8) penLeftFore );
	info.penLeftColorBack.SetColor( (UInt8) penLeftBack );
	info.penRightColorFore.SetColor( (UInt8) penRightFore );
	info.penRightColorBack.SetColor( (UInt8) penRightBack );

	return info;
}

void VWWallObj::SetComponentInfo(size_t index, const SWallComponentInfo& info)
{
	VWFC_ASSERT( fhObject != nil );
	VWFC_ASSERT(index < 32768);

	InternalName		compName;
	memcpy( compName, info.componentName, sizeof(InternalIndex) );
	VWFC_VERIFY( ::GS_SetComponentName( gCBP,  fhObject, (CBSignedShort) index, compName ) );
	VWFC_VERIFY( ::GS_SetComponentWidth( gCBP,  fhObject, (CBSignedShort) index, info.width ) );
	VWFC_VERIFY( ::GS_SetComponentClass( gCBP,  fhObject, (CBSignedShort) index, info.componentClass ) );

	// fill
	VWFC_VERIFY( ::GS_SetComponentUseFillClassAttr( gCBP,  fhObject, (CBSignedShort) index, info.fillUseClass ) );
	VWFC_VERIFY( ::GS_SetComponentFill( gCBP,  fhObject, (CBSignedShort) index, info.fillPat ) );
	VWFC_VERIFY( ::GS_SetComponentFillColors( gCBP,  fhObject, (CBSignedShort) index, info.fillColorFore.GetColorIndex(), info.fillColorBack.GetColorIndex() ) );

	// lines
	VWFC_VERIFY( ::GS_SetComponentUsePenClassAttr( gCBP,  fhObject, (CBSignedShort) index, info.penLeftUseClass, info.penRightUseClass ) );
	VWFC_VERIFY( ::GS_SetComponentPenColors( gCBP,  fhObject, (CBSignedShort) index, info.penLeftColorFore.GetColorIndex(), info.penLeftColorBack.GetColorIndex(), info.penRightColorFore.GetColorIndex(), info.penRightColorBack.GetColorIndex() ) );
	VWFC_VERIFY( ::GS_SetComponentPenStyles( gCBP,  fhObject, (CBSignedShort) index, info.penLeftStyle, info.penRightStyle ) );
	VWFC_VERIFY( ::GS_SetComponentPenWeights( gCBP,  fhObject, (CBSignedShort) index, info.penLeftWeight, info.penRightWeight ) );
}

bool VWWallObj::InsertNewComponent(size_t beforeIndex, const SWallComponentInfo& info)
{
	VWFC_ASSERT( fhObject != nil );
	
	return ::GS_InsertNewComponent( gCBP, fhObject, (short) beforeIndex, info.width, info.fillPat, (CBSignedByte)info.penLeftWeight, (CBSignedByte)info.penRightWeight, (CBSignedByte)info.penLeftStyle, (CBSignedByte)info.penRightStyle );
}

bool VWWallObj::DeleteComponent(size_t index)
{
	return GS_DeleteComponent( gCBP, fhObject, (short) index );
}

// BREAKS //////////////////////////////////////////////////////////////////////////////////
void VWWallObj::EnumerateBreaks(IWallBreakListener* pBreakListener) const
{
	VWFC_ASSERT( fhObject != nil );
	::GS_ForEachBreak( gCBP, fhObject, VWWallObj::GetAllBreaksCallback, pBreakListener );
}

Boolean VWWallObj::GetAllBreaksCallback(MCObjectHandle, long index, double off, long breakType, void* breakData, CallBackPtr , void* env)
{
	IWallBreakListener* pBreakListener	= (IWallBreakListener*) env;

	if ( pBreakListener ) {
		if ( breakType == kSymbolBreak ) {
			SymbolBreakType*	pBreak	= (SymbolBreakType*) breakData;
			if ( pBreak ) {
				SWallSymbolBreak	symBreak;
				symBreak.index			= (size_t) index;
				symBreak.offset			= off;
				symBreak.symBreak		= *pBreak;
				pBreakListener->NotifySymbolBreak( symBreak );
			}
		}
		else if ( breakType == kHalfBreak ) {
			HalfBreakType*		pBreak	= (HalfBreakType*) breakData;
			if ( pBreak ) {
				SWallHalfBreak		halfBreak;
				halfBreak.index			= (size_t) index;
				halfBreak.offset		= off;
				halfBreak.halfBreak	= *pBreak;
				pBreakListener->NotifyHalfBreak( halfBreak );
			}
		}
		else if ( breakType == kCapBreak ) {
			CapBreakType*		pBreak	= (CapBreakType*) breakData;
			if ( pBreak ) {
				SWallCapBreak		capBreak;
				capBreak.index			= (size_t) index;
				capBreak.offset			= off;
				capBreak.capBreak		= *pBreak;
				pBreakListener->NotifyCapBreak( capBreak );
			}
		}
		else if ( breakType == kPeakBreak ) {
			PeakBreakType*		pBreak	= (PeakBreakType*) breakData;
			if ( pBreak ) {
				SWallPeakBreak		peakBreak;
				peakBreak.index			= (size_t) index;
				peakBreak.offset		= off;
				peakBreak.peakBreak		= *pBreak;
				pBreakListener->NotifyPeakBreak( peakBreak );
			}
		}
	}

	return false;
}

class VWWallDefaultBreakEnumerator : public IWallBreakListener
{
public:
					VWWallDefaultBreakEnumerator()						{ fparrSymbolBreaks = NULL; fparrHalfBreaks = NULL; fparrCapBreaks = NULL; fparrPeakBreaks = NULL; }
	virtual void	NotifySymbolBreak(const SWallSymbolBreak& brk)		{ if ( fparrSymbolBreaks ) fparrSymbolBreaks->push_back( brk ); }
	virtual void	NotifyHalfBreak(const SWallHalfBreak& brk)			{ if ( fparrHalfBreaks ) fparrHalfBreaks->push_back( brk ); }
	virtual void	NotifyCapBreak(const SWallCapBreak& brk)			{ if ( fparrCapBreaks ) fparrCapBreaks->push_back( brk ); }
	virtual void	NotifyPeakBreak(const SWallPeakBreak& brk)			{ if ( fparrPeakBreaks ) fparrPeakBreaks->push_back( brk ); }
public:
	TWallSymbolBreaksArray*		fparrSymbolBreaks;
	TWallHalfBreaksArray*		fparrHalfBreaks;
	TWallCapBreaksArray*		fparrCapBreaks;
	TWallPeakBreaksArray*		fparrPeakBreaks;
};

void VWWallObj::EnumerateBreaks(TWallSymbolBreaksArray& outArrSymbolBreaks) const
{
	outArrSymbolBreaks.clear();
	VWWallDefaultBreakEnumerator	enumerator;
	enumerator.fparrSymbolBreaks		= & outArrSymbolBreaks;
	this->EnumerateBreaks( & enumerator );
}

void VWWallObj::EnumerateBreaks(TWallHalfBreaksArray& outArrHalfBreaks) const
{
	outArrHalfBreaks.clear();
	VWWallDefaultBreakEnumerator	enumerator;
	enumerator.fparrHalfBreaks		= & outArrHalfBreaks;
	this->EnumerateBreaks( & enumerator );
}

void VWWallObj::EnumerateBreaks(TWallCapBreaksArray& outArrCapBreaks) const
{
	outArrCapBreaks.clear();
	VWWallDefaultBreakEnumerator	enumerator;
	enumerator.fparrCapBreaks		= & outArrCapBreaks;
	this->EnumerateBreaks( & enumerator );
}

void VWWallObj::EnumerateBreaks(TWallPeakBreaksArray& outArrPeakBreaks) const
{
	outArrPeakBreaks.clear();
	VWWallDefaultBreakEnumerator	enumerator;
	enumerator.fparrPeakBreaks		= & outArrPeakBreaks;
	this->EnumerateBreaks( & enumerator );
}

size_t VWWallObj::AddBreak(const SWallSymbolBreak& brk)
{
	return (size_t) ::GS_AddBreak( gCBP, fhObject, brk.offset, kSymbolBreak, (void*) & brk.symBreak );
}

size_t VWWallObj::AddBreak(const SWallHalfBreak& brk)
{
	return (size_t) ::GS_AddBreak( gCBP, fhObject, brk.offset, kHalfBreak, (void*) & brk.halfBreak );
}

size_t VWWallObj::AddBreak(const SWallCapBreak& brk)
{
	return (size_t) ::GS_AddBreak( gCBP, fhObject, brk.offset, kCapBreak, (void*) & brk.capBreak );
}

size_t VWWallObj::AddBreak(const SWallPeakBreak& brk)
{
	return (size_t) ::GS_AddBreak( gCBP, fhObject, brk.offset, kPeakBreak, (void*) & brk.peakBreak );
}

void VWWallObj::DeleteBreak(size_t index)
{
	::GS_DeleteBreak( gCBP, fhObject, DemoteTo<short>( kVStanev, index ) );
}

// Return true if the specified object handle is 2D locus
bool VWWallObj::IsWallObject(MCObjectHandle hObj)
{
	short	type	= ::GS_GetObjectType( gCBP, hObj );
	return type == kWallNode || type == kRoundWallNode;
}

TSTLPolygons2DArray VWWallObj::GetWall2DPolygons_() const
{
	TSTLPolygons2DArray	polygonArr;
	VWTransformMatrix	theMat;

	// go through children objects
	this->GetWall2DPolygons( theMat, *this, polygonArr );
	
	return	polygonArr;
}

void VWWallObj::GetWall2DPolygons(const VWTransformMatrix& theMat, MCObjectHandle hGroup, TSTLPolygons2DArray& polygonArr) const
{
	for(	MCObjectHandle hObj = ::GS_FirstMemberObj( gCBP, hGroup ) ; 
			hObj ; 
			hObj = GS_NextObject( gCBP, hObj ) )
	{
		// check the type
		VWObject	obj( hObj );
		short		type = obj.GetType();

		// if it is a poly
		if ( type == kPolygonNode || type == kPolylineNode || type == kBoxNode ) {

			// get polygon
			VWPolygon2DObj	polyObj( hObj );
			VWPolygon2D		outPolygon;
			polyObj.GetPolygon( outPolygon );
			outPolygon.CleanUpPolygon( VWPoint2D::sEpsilon );
			outPolygon.SetClosed( true );

			// dispose the polygon
			outPolygon.PointsTransform( theMat );

			// add this polygon only if it is a correct one
			if ( abs( outPolygon.GetArea() ) > VWPoint2D::sEpsilon ) {

				// add to array: if newly found polygon contains fully an already added then replace it.
				// if it is fully inside an already added polygon, then do not add this.
				bool	bAdded	= false;
				size_t	polyCnt	= polygonArr.size();
				for ( size_t i = 0 ; i < polyCnt ; i ++ ) {
					VWPolygon2D&	poly	= polygonArr[ i ];
					if ( outPolygon.IsFullyInside( poly, VWPoint2D::sEpsilon ) ) {
						poly	= outPolygon;
						bAdded	= true;
						break;
					}
					if ( poly.IsFullyInside( outPolygon, VWPoint2D::sEpsilon ) ) {
						bAdded	= true;
						break;
					}
				}
				if ( !bAdded ) {
					polygonArr.push_back( outPolygon );
				}
			}
		}
		// a parametric built-in object in the wall
		else if ( type == kParametricNode ) {

			// get local matrix
			VWTransformMatrix	theLocalMat;
			obj.GetObjectMatrix( theLocalMat );
			theLocalMat			= theMat * theLocalMat;

			// call recursively
			this->GetWall2DPolygons( theLocalMat, obj, polygonArr );
		}
		// a symbol built-in object in the wall
		else if ( type == kSymbolNode ) {

			// get local matrix
			VWTransformMatrix	theLocalMat;
			obj.GetObjectMatrix( theLocalMat );
			theLocalMat			= theMat * theLocalMat;

			// call recursively
			this->GetWall2DPolygons( theLocalMat, ::GS_GetDefinition( gCBP, obj ), polygonArr );
		}
		else if ( type == kGroupNode ) {

			// recursively look inside the group
			this->GetWall2DPolygons( theMat, obj, polygonArr );
		}
	}
}

MCObjectHandle VWWallObj::GetWallFootPrint() const
{
	MCObjectHandle footPrint = nil;
	
	MCObjectHandle newWall = ::GS_DuplicateObject( gCBP, *this );
	
	if ( newWall )
	{
		// remove all symbol breaks
		VWWallObj wallObj( newWall );
		wallObj.GetObjectAttribs().SetFillSolid();

		TWallSymbolBreaksArray	symbolBreaksArr;
		wallObj.EnumerateBreaks( symbolBreaksArr );

		for ( size_t i = 0 ; i < symbolBreaksArr.size() ; i ++ )
		{
			wallObj.DeleteBreak( symbolBreaksArr[ i ].index );
		}
		
		wallObj.ResetObject();
		
		MCObjectHandle trav = ::GS_FirstMemberObj( gCBP, newWall );
		MCObjectHandle curentFootPrint = nil;
		
		double area = 0;

		while ( trav )
		{
			short type = ::GS_GetObjectType( gCBP, trav );
			if ( type == kPolygonNode || type == kPolylineNode )
			{
				// get area
				double thisArea;
				::GS_CalcAreaN( gCBP, trav, thisArea );

				if ( thisArea > area )
				{	
					area = thisArea;
					curentFootPrint = trav;
				}
			}
			trav = ::GS_NextObject( gCBP, trav );
		}
			
		footPrint = ::GS_DuplicateObject( gCBP, curentFootPrint );

		wallObj.DeleteObject( false );
	}

	return footPrint;
}

void VWWallObj::GetWallFootPrintEx(VWPolygon2D& footPoly) const
{

	
	footPoly.ClearVertices();

	if (fhObject == NULL || IsRound())
		return;
	
	VWPoint2D wallStartPt, wallEndPt;
	this->GetPoints(wallStartPt, wallEndPt);

	double			distStart = 1E300,	distEnd = 1E300;
	MCObjectHandle  polyStart = nil,	polyEnd = nil;
	MCObjectHandle  trav = ::GS_FirstMemberObj( gCBP, *this );

	// Find closest polygons to start and end
	while ( trav )
	{
		short type = ::GS_GetObjectType( gCBP, trav );
		if ( type == kPolygonNode || type == kPolylineNode || type == kBoxNode )
		{
			VWPolygon2DObj	wallPolyObj( trav );
			
			size_t count = wallPolyObj.GetVertexCount();
			if (count > 2) {
			for (size_t i = 0; i < count; i++){
				VWPoint2D ptPoly = wallPolyObj.GetVertexPoint( i );
				double dist1 = ptPoly.DistanceTo( wallStartPt );
				double dist2 = ptPoly.DistanceTo( wallEndPt   );

				if ( distStart > dist1 ) {
					 distStart = dist1;
					 polyStart = trav;
				}

				if ( distEnd > dist2 ) {
					 distEnd = dist2;
					 polyEnd = trav;
				}
			}
		}
		}
		trav = ::GS_NextObject( gCBP, trav );
	}

	// Find closest vertices from the closest polygons
	if (polyStart && polyEnd){
		VWPoint2D startPt1, startPt2, endPt1, endPt2;

		{ // 2 points near wall start
			VWPolygon2DObj	wallPolyObj( polyStart );
			size_t count =  wallPolyObj.GetVertexCount();
			double			dist2Pts = 1E300;

			for (size_t i = 0; i < count; i++){
				VWPoint2D ptPoly1 = wallPolyObj.GetVertexPoint( i );
				VWPoint2D ptPoly2 = wallPolyObj.GetVertexPoint((i < count-1) ? i + 1 : 0 );

				double dist = ptPoly1.DistanceTo( wallStartPt ) + ptPoly2.DistanceTo( wallStartPt );

				if ( dist2Pts > dist ) {
					 dist2Pts = dist;
					 startPt1 = ptPoly1; startPt2 = ptPoly2;
				}
			}
		}

		{ // 2 points near wall end
			VWPolygon2DObj	wallPolyObj( polyEnd );
			size_t count =  wallPolyObj.GetVertexCount();
			double			dist2Pts = 1E300;

			for (size_t i = 0; i < count; i++){
				VWPoint2D ptPoly1 = wallPolyObj.GetVertexPoint( i );
				VWPoint2D ptPoly2 = wallPolyObj.GetVertexPoint((i < count-1) ? i + 1 : 0 );

				double dist = ptPoly1.DistanceTo( wallEndPt ) + ptPoly2.DistanceTo( wallEndPt );

				if ( dist2Pts > dist ) {
					 dist2Pts = dist;
					 endPt1 = ptPoly1; endPt2 = ptPoly2;
				}
			}
		}

		// Extend to full width if components present
		if (this->GetComponentCount() > 1)
		{
			if ( !MathUtils::Equalish( startPt1.DistanceTo( startPt2 ), 0.0, VWPoint2D::sEpsilon ) && 
				 !MathUtils::Equalish( endPt1.DistanceTo( endPt2 ),     0.0, VWPoint2D::sEpsilon )		)
			{
				double	halfWallWidth	= this->GetWidth() / 2;
				VWPoint2D		dirConn	= (startPt2  - startPt1).Normalize();

				startPt1				= wallStartPt + halfWallWidth * dirConn;
				startPt2				= wallStartPt - halfWallWidth * dirConn;

				dirConn					= (endPt2  - endPt1).Normalize();
				endPt1					= wallEndPt + halfWallWidth * dirConn;
				endPt2					= wallEndPt - halfWallWidth * dirConn;
			}
		}
		
		// Build the polygon
		VWLine2D line ( startPt2, endPt1 );

		footPoly.AddVertex( startPt1 );
		footPoly.AddVertex( startPt2 );

		if (line.AreOnSameSide( startPt1, endPt2 )){	
			footPoly.AddVertex( endPt1 );
			footPoly.AddVertex( endPt2 );
		}
		else {
			footPoly.AddVertex( endPt2 );
			footPoly.AddVertex( endPt1 );
		}

		footPoly.SetClosed( true );
	}
}

