//========= Copyright © 1996-2001, Valve LLC, All rights reserved. ============
//
//
// $NoKeywords: $
//=============================================================================

#include <stdafx.h>
#include <malloc.h>
#include "FaceEditSheet.h"
#include "MainFrm.h"
#include "GlobalFunctions.h"
#include "MapDisp.h"
#include "MapFace.h"
#include "UtlVector.h"


//============================================================================
//
//         e1
//     c1------c2
//     |        |
//  e0 |        | e2		
//     |        |
//     c0------c3
//         e3 
//
// Note: edges refer to internal edge points only, corners "contain" all surfaces
//       touching the corner (surfaces that only touch the corner, as well as those
//       "edges" that end/begin at the corner(s))
//
#define DISPSEW_POINT_TOLERANCE		1.0f		// one unit

#define DISPSEW_EDGE_COUNT		    4
#define DISPSEW_AT_EDGE_COUNT		2
#define DISPSEW_CORNER_COUNT        4
#define DISPSEW_AT_CORNER_COUNT     16 

#define DISPSEW_NULL_INDEX			-99999

#define DISPSEW_EDGE_NORMAL			0
#define DISPSEW_EDGE_TJSTART		1
#define DISPSEW_EDGE_TJEND			2
#define DISPSEW_EDGE_TJ				3


#define DISPSEW_FACES_AT_EDGE	3
#define DISPSEW_FACES_AT_CORNER	16
#define DISPSEW_FACES_AT_TJUNC	8

struct SewEdgeData_t
{
	int			faceCount;								// number of faces contributing to the edge sew
	CMapFace	*pFaces[DISPSEW_FACES_AT_EDGE];			// the faces contributing to the edge sew
	int			ndxEdges[DISPSEW_FACES_AT_EDGE];		// the faces' edge indices contributing to the edge sew
	int			type[DISPSEW_FACES_AT_EDGE];			// the type of edge t-junction, match t-junction start, etc....
};

struct SewCornerData_t
{
	int			faceCount;								// number of faces contributing to the corner sew
	CMapFace	*pFaces[DISPSEW_FACES_AT_CORNER];		// the faces contributing to the corner sew
	int			ndxCorners[DISPSEW_FACES_AT_CORNER];	// the faces' corner indices contributing to the corner sew
};

struct SewTJuncData_t
{
	int			faceCount;								// number of faces contributing to the t-junction sew
	CMapFace	*pFaces[DISPSEW_FACES_AT_TJUNC];			// the faces contributing to the t-junction sew
	int			ndxCorners[DISPSEW_FACES_AT_TJUNC];		// the faces' corner indices contributing to the t-junction sew
	int			ndxEdges[DISPSEW_FACES_AT_TJUNC];		// the faces' edge (midpoint) indices contributing to the t-junction sew
};

static CUtlVector<SewEdgeData_t*> s_EdgeData;
static CUtlVector<SewCornerData_t*> s_CornerData;
static CUtlVector<SewTJuncData_t*> s_TJData;

// local functions
void SewCorner_Build( void );
void SewCorner_Resolve( void );
void SewCorner_Destroy( SewCornerData_t *pCornerData );
void SewTJunc_Build( void );
void SewTJunc_Resolve( void );
void SewTJunc_Destroy( SewTJuncData_t *pTJData );
void SewEdge_Build( void );
void SewEdge_Resolve( void );
void SewEdge_Destroy( SewEdgeData_t *pEdgeData );

//-----------------------------------------------------------------------------
// Purpose: compare two point positions to see if they are equivolent given a
//          tolerance
//-----------------------------------------------------------------------------
bool PointCompareWithTolerance( Vector const& pt1, Vector const& pt2, float tolerance )
{
	for( int i = 0 ; i < 3 ; i++ )
	{
		if( fabs( pt1[i] - pt2[i] ) > tolerance )
			return false;
	}
	
	return true;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool EdgeCompare( Vector *pEdgePts1, Vector *pEdgePts2, int &edgeType1, int &edgeType2 )
{
	Vector edge1[3];
	Vector edge2[3];

	//
	// create edges and midpoints
	//
	edge1[0] = pEdgePts1[0];
	edge1[1] = ( pEdgePts1[0] + pEdgePts1[1] ) * 0.5f;
	edge1[2] = pEdgePts1[1];

	edge2[0] = pEdgePts2[0];
	edge2[1] = ( pEdgePts2[0] + pEdgePts2[1] ) * 0.5f;
	edge2[2] = pEdgePts2[1];
	
	// assume edge type to be normal (will get overridden if otherwise)
	edgeType1 = DISPSEW_EDGE_NORMAL;
	edgeType2 = DISPSEW_EDGE_NORMAL;

	//
	// compare points and determine how many are shared between the two edges
	//
	int overlapCount = 0;
	int ndxEdge1[2];
	int ndxEdge2[2];

	for( int ndx1 = 0; ndx1 < 3; ndx1++ )
	{
		for( int ndx2 = 0; ndx2 < 3; ndx2++ )
		{
			if( PointCompareWithTolerance( edge1[ndx1], edge2[ndx2], DISPSEW_POINT_TOLERANCE ) )
			{
				// no midpoint to midpoint sharing allowed (midpoints are odd index values)
				if( ( ndx1%2 != 0 ) && ( ndx2%2 != 0 ) )
					continue;

				// sanity check
				assert( overlapCount >= 0 );
				assert( overlapCount < 2 );

				ndxEdge1[overlapCount] = ndx1;
				ndxEdge2[overlapCount] = ndx2;

				overlapCount++;
				break;
			}
		}
	}

	if( overlapCount != 2 )
		return false;

	// handle edge1 as t-junction edge
	if( ndxEdge1[0]%2 != 0 )
	{
		edgeType1 = DISPSEW_EDGE_TJ;
		
		if( ndxEdge1[1] == 0 )
		{
			edgeType2 = DISPSEW_EDGE_TJSTART;
		}
		else if( ndxEdge1[1] == 2 )
		{
			edgeType2 = DISPSEW_EDGE_TJEND;			
		}
	}
	else if( ndxEdge1[1]%2 != 0 ) 
	{
		edgeType1 = DISPSEW_EDGE_TJ;

		if( ndxEdge1[0] == 0 )
		{
			edgeType2 = DISPSEW_EDGE_TJSTART;
		}
		else if( ndxEdge1[0] == 2 )
		{
			edgeType2 = DISPSEW_EDGE_TJEND;
		}
	}

	// handle edge2 as t-junction edge
	if( ndxEdge2[0]%2 != 0 )
	{
		edgeType2 = DISPSEW_EDGE_TJ;

		if( ndxEdge2[1] == 0 )
		{
			edgeType1 = DISPSEW_EDGE_TJSTART;
		}
		else if( ndxEdge2[1] == 2 )
		{
			edgeType1 = DISPSEW_EDGE_TJEND;
		}
	}
	else if( ndxEdge2[1]%2 != 0 ) 
	{
		edgeType2 = DISPSEW_EDGE_TJ;

		if( ndxEdge2[0] == 0 )
		{
			edgeType1 = DISPSEW_EDGE_TJSTART;
		}
		else if( ndxEdge2[0] == 2 )
		{
			edgeType1 = DISPSEW_EDGE_TJEND;
		}
	}

	return true;	
}


//-----------------------------------------------------------------------------
// Purpose: get a point from the surface at the given index, will get the point
//          from the displacement surface if it exists, it will get it from the
//          base face otherwise
//-----------------------------------------------------------------------------
inline void GetPointFromSurface( CMapFace *pFace, int ndxPt, Vector &pt )
{
	EditDispHandle_t dispHandle = pFace->GetDisp();
	if( dispHandle != EDITDISPHANDLE_INVALID )
	{
		CMapDisp *pDisp = EditDispMgr()->GetDisp( dispHandle );
		pDisp->GetSurfPoint( ndxPt, pt );
	}
	else
	{
		pFace->GetPoint( pt, ndxPt );
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int GetEdgePointIndex( CMapDisp *pDisp, int edgeIndex, int edgePtIndex, bool bCCW )
{
	int height = pDisp->GetHeight();
	int width = pDisp->GetWidth();

	if( bCCW )
	{
		switch( edgeIndex )
		{
		case 0: { return ( edgePtIndex * height ); }
		case 1: { return ( ( ( height - 1 ) * width ) + edgePtIndex ); }
		case 2: { return ( ( height * width - 1 ) - ( edgePtIndex * height ) ); }
		case 3: { return ( ( width - 1 ) - edgePtIndex ); }
		default: { return -1; }
		}
	}
	else
	{
		switch( edgeIndex )
		{
		case 0: { return ( ( ( height - 1 ) * width ) - ( edgePtIndex * height ) ); }
		case 1: { return ( ( height * width - 1 ) - edgePtIndex ); }
		case 2: { return ( ( width - 1 ) + ( edgePtIndex * height ) ); }
		case 3: { return ( edgePtIndex ); }
		default: { return -1; }
		}
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int GetCornerPointIndex( CMapDisp *pDisp, int cornerIndex )
{
	int width = pDisp->GetWidth();
	int height = pDisp->GetHeight();

	switch( cornerIndex )
	{
	case 0: { return 0; }
	case 1: { return ( ( height - 1 ) * width ); }
	case 2: { return ( height * width - 1 ); }
	case 3: { return ( width - 1 ); }
	default: { return -1; }
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int GetTJuncIndex( CMapDisp *pDisp, int ndxEdge )
{
	int width = pDisp->GetWidth();
	int height = pDisp->GetHeight();

	switch( ndxEdge )
	{
	case 0: { return( height * ( width / 2 ) ); }
	case 1: { return( ( ( height - 1 ) * width ) + ( width / 2 ) ); }
	case 2: { return( ( height * ( width / 2 ) ) + ( width - 1 ) ); }
	case 3: { return( width / 2 ); }
	default: { return -1; }
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void AverageVectorFieldData( CMapDisp *pDisp1, int ndx1, CMapDisp *pDisp2, int ndx2 )
{
	//
	// average the positions at each index
	// position = dispVector * dispDist
	//
	float dist1 = pDisp1->GetFieldDistance( ndx1 );
	float dist2 = pDisp2->GetFieldDistance( ndx2 );
	
	Vector v1, v2;
	pDisp1->GetFieldVector( ndx1, v1 );
	pDisp2->GetFieldVector( ndx2, v2 );

	v1 *= dist1;
	v2 *= dist2;

	Vector vAvg;
	vAvg = ( v1 + v2 ) * 0.5f;

	float distAvg = VectorNormalize( vAvg );

	pDisp1->SetFieldDistance( ndx1, distAvg );
	pDisp2->SetFieldDistance( ndx2, distAvg );
	pDisp1->SetFieldVector( ndx1, vAvg );
	pDisp2->SetFieldVector( ndx2, vAvg );

	//
	// average the subdivion positions and normals
	//
	pDisp1->GetSubdivPosition( ndx1, v1 );
	pDisp2->GetSubdivPosition( ndx2, v2 );
	vAvg = ( v1 + v2 ) * 0.5f;
	pDisp1->SetSubdivPosition( ndx1, vAvg );
	pDisp2->SetSubdivPosition( ndx2, vAvg );

	pDisp1->GetSubdivNormal( ndx1, v1 );
	pDisp2->GetSubdivNormal( ndx2, v2 );
	vAvg = v1 + v2;
	VectorNormalize( vAvg );
	pDisp1->SetSubdivNormal( ndx1, vAvg );
	pDisp2->SetSubdivNormal( ndx2, vAvg );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void BlendVectorFieldData( CMapDisp *pDisp1, int ndxSrc1, int ndxDst1, 
						   CMapDisp *pDisp2, int ndxSrc2, int ndxDst2,
						   float blendFactor )
{
	//
	// to blend positions -- calculate the positions at the end points
	// find the new point along the parameterized line and calculate the
	// new field vector direction and distance (position)
	//
	float dist1 = pDisp1->GetFieldDistance( ndxSrc1 );
	float dist2 = pDisp2->GetFieldDistance( ndxSrc2 );
	
	Vector v1, v2;
	pDisp1->GetFieldVector( ndxSrc1, v1 );
	pDisp2->GetFieldVector( ndxSrc2, v2 );

	v1 *= dist1;
	v2 *= dist2;

	Vector vBlend;
	vBlend = v1 + ( v2 - v1 ) * blendFactor;

	float distBlend = VectorNormalize( vBlend );

	pDisp1->SetFieldDistance( ndxDst1, distBlend );
	pDisp2->SetFieldDistance( ndxDst2, distBlend );
	pDisp1->SetFieldVector( ndxDst1, vBlend );
	pDisp2->SetFieldVector( ndxDst2, vBlend );

	//
	// blend subdivision positions and normals as before,
	// this isn't truly accurate, but I am not sure what these
	// values mean in the edge sewing case anyway???
	//
	pDisp1->GetSubdivPosition( ndxSrc1, v1 );
	pDisp2->GetSubdivPosition( ndxSrc2, v2 );
	vBlend = v1 + ( v2 - v1 ) * blendFactor;
	pDisp1->SetSubdivPosition( ndxDst1, vBlend );
	pDisp2->SetSubdivPosition( ndxDst2, vBlend );

	pDisp1->GetSubdivNormal( ndxSrc1, v1 );
	pDisp2->GetSubdivNormal( ndxSrc2, v2 );
	vBlend = v1 + ( v2 - v1 ) * blendFactor;
	pDisp1->SetSubdivNormal( ndxDst1, vBlend );
	pDisp2->SetSubdivNormal( ndxDst2, vBlend );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline bool Face_IsSolid( CMapFace *pFace )
{
	return ( pFace->GetDisp() == EDITDISPHANDLE_INVALID );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void Faces_Update( void )
{
	//
	// get the "faces" selection list (contains displaced and non-displaced faces)
	//
	CFaceEditSheet *pSheet = GetMainWnd()->GetFaceEditSheet();
	if( !pSheet )
		return;

	//
	// for each face in list
	//
	int faceCount = pSheet->GetFaceListCount();

	for( int ndxFace = 0; ndxFace < faceCount; ndxFace++ )
	{
		// get the current face
		CMapFace *pFace = pSheet->GetFaceListDataFace( ndxFace );
		if( !pFace )
			continue;

		// only update displacement surfaces
		EditDispHandle_t dispHandle = pFace->GetDisp();
		if( dispHandle == EDITDISPHANDLE_INVALID )
			continue;

		CMapDisp *pDisp = EditDispMgr()->GetDisp( dispHandle );
		pDisp->UpdateData();
	}
}


//-----------------------------------------------------------------------------
// Purpose: given a face with a displacement surface, "sew" all edges to all
//          neighboring displacement and non-displacement surfaces 
//          found in the selection set
//-----------------------------------------------------------------------------
void FaceListSewEdges( void )
{
	// build data
	SewCorner_Build();
	SewTJunc_Build();
	SewEdge_Build();

	// resolve data
	SewCorner_Resolve();
	SewTJunc_Resolve();
	SewEdge_Resolve();

	// destroy all corners, t-junctions, edges
	int count = s_CornerData.Size();
	for( int i = 0; i < count; i++ )
	{
		SewCorner_Destroy( s_CornerData.Element( i ) );
	}

	count = s_TJData.Size();
	for( i = 0; i < count; i++ )
	{
		SewTJunc_Destroy( s_TJData.Element( i ) );
	}
	
	count = s_EdgeData.Size();
	for( i = 0; i < count; i++ )
	{
		SewEdge_Destroy( s_EdgeData.Element( i ) );
	}

	// flush all of the sewing data buffers
	s_CornerData.Purge();
	s_TJData.Purge();
	s_EdgeData.Purge();

	// update the faces
	Faces_Update();
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
SewCornerData_t *SewCorner_Create( void )
{
	SewCornerData_t *pCornerData = new SewCornerData_t;
	if( pCornerData )
	{
		// initialize the data
		pCornerData->faceCount = 0;
		return pCornerData;
	}

	return NULL;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void SewCorner_Destroy( SewCornerData_t *pCornerData )
{
	if( pCornerData )
	{
		delete pCornerData;
		pCornerData = NULL;
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool SewCorner_IsSolid( SewCornerData_t *pCornerData )
{
	for( int i = 0; i < pCornerData->faceCount; i++ )
	{
		if( Face_IsSolid( pCornerData->pFaces[i] ) )
			return true;
	}

	return false;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void SewCorner_Add( SewCornerData_t *pCornerData, CMapFace *pFace, int ndx )
{
	assert( pCornerData->faceCount >= 0 );
	assert( pCornerData->faceCount < DISPSEW_FACES_AT_CORNER );

	pCornerData->pFaces[pCornerData->faceCount] = pFace;
	pCornerData->ndxCorners[pCornerData->faceCount] = ndx;
	pCornerData->faceCount++;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void SewCorner_AddToList( SewCornerData_t *pCornerData )
{
	// get the current corner point
	Vector pt;
	GetPointFromSurface( pCornerData->pFaces[0], pCornerData->ndxCorners[0], pt );

	//
	// check to see if the corner point already exists in the corner data list
	//
	int cornerCount = s_CornerData.Size();

	for( int i = 0; i < cornerCount; i++ )
	{
		//
		// get the compare corner point
		//
		SewCornerData_t *pCmpData = s_CornerData.Element( i );
		if( !pCmpData )
			continue;

		Vector cmpPt;
		GetPointFromSurface( pCmpData->pFaces[0], pCmpData->ndxCorners[0], cmpPt );

		// compare the points - return if found
		if( PointCompareWithTolerance( pt, cmpPt, DISPSEW_POINT_TOLERANCE ) )
		{
			SewCorner_Destroy( pCornerData );
			return;
		}
	}

	// unique corner point -- add it to the list
	s_CornerData.AddToTail( pCornerData );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void SewCorner_Build( void )
{
	//
	// get the "faces" selection list (contains displaced and non-displaced faces)
	//
	CFaceEditSheet *pSheet = GetMainWnd()->GetFaceEditSheet();
	if( !pSheet )
		return;

	//
	// for each face in list
	//
	int faceCount = pSheet->GetFaceListCount();

	for( int ndxFace = 0; ndxFace < faceCount; ndxFace++ )
	{
		// get the current face
		CMapFace *pFace = pSheet->GetFaceListDataFace( ndxFace );
		if( !pFace )
			continue;

		//
		// for each face point
		//
		int ptCount = pFace->GetPointCount();
		for( int ndxPt = 0; ndxPt < ptCount; ndxPt++ )
		{
			// get the current point
			Vector pt;
			GetPointFromSurface( pFace, ndxPt, pt );

			// allocate new corner point
			SewCornerData_t *pCornerData = SewCorner_Create();
			if( !pCornerData )
				return;

			//
			// compare this point to all of the other points on all the other faces in the list
			//
			for( int ndxFace2 = 0; ndxFace2 < faceCount; ndxFace2++ )
			{
				// don't compare to itself
				if( ndxFace == ndxFace2 )
					continue;

				// get the current compare face
				CMapFace *pFace2 = pSheet->GetFaceListDataFace( ndxFace2 );
				if( !pFace2 )
					continue;

				//
				// for each compare face point
				//
				int ptCount2 = pFace2->GetPointCount();
				for( int ndxPt2 = 0; ndxPt2 < ptCount2; ndxPt2++ )
				{
					// get the current compare point
					Vector pt2;
					GetPointFromSurface( pFace2, ndxPt2, pt2 );

					// compare pt1 and pt2
					if( PointCompareWithTolerance( pt, pt2, DISPSEW_POINT_TOLERANCE ) )
					{
						SewCorner_Add( pCornerData, pFace2, ndxPt2 );
					}
				}
			}

			// had neighbors -- add base point and add it to corner list
			if( pCornerData->faceCount > 0 )
			{
				SewCorner_Add( pCornerData, pFace, ndxPt );
				SewCorner_AddToList( pCornerData );
			}
			// no neighbors -- de-allocate
			else
			{
				SewCorner_Destroy( pCornerData );
			}
		}
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void SewCorner_ResolveDisp( SewCornerData_t *pCornerData )
{
	// the field data accumulators
	float avgDist = 0.0f;
	Vector vAvgField( 0.0f, 0.0f, 0.0f );
	Vector vAvgSubdivPos( 0.0f, 0.0f, 0.0f );
	Vector vAvgSubdivNormal( 0.0f, 0.0f, 0.0f );

	// for all the faces at the corner
	for( int i = 0; i < pCornerData->faceCount; i++ )
	{
		// get the current corner face
		CMapFace *pFace = pCornerData->pFaces[i];
		if( !pFace )
			continue;

		// get the current displacement surface to reset, if solid = done!
		EditDispHandle_t dispHandle = pFace->GetDisp();
		if( dispHandle == EDITDISPHANDLE_INVALID )
			continue;
		
		CMapDisp *pDisp = EditDispMgr()->GetDisp( dispHandle );

		// get the corner index
		int ndxPt = GetCornerPointIndex( pDisp, pCornerData->ndxCorners[i] );
		if( ndxPt == -1 )
			continue;

		float dist = pDisp->GetFieldDistance( ndxPt );
		avgDist += dist;

		Vector vTmp;
		pDisp->GetFieldVector( ndxPt, vTmp );
		vAvgField += vTmp;

		pDisp->GetSubdivPosition( ndxPt, vTmp );
		vAvgSubdivPos += vTmp;

		pDisp->GetSubdivNormal( ndxPt, vTmp );
		vAvgSubdivNormal += vTmp;
	}

	// calculate the average
	avgDist /= pCornerData->faceCount;
	vAvgField /= pCornerData->faceCount;
	vAvgSubdivPos /= pCornerData->faceCount;
	vAvgSubdivNormal /= pCornerData->faceCount;

	for( i = 0; i < pCornerData->faceCount; i++ )
	{
		// get the current corner face
		CMapFace *pFace = pCornerData->pFaces[i];
		if( !pFace )
			continue;

		// get the current displacement surface to reset, if solid = done!
		EditDispHandle_t dispHandle = pFace->GetDisp();
		if( dispHandle == EDITDISPHANDLE_INVALID )
			continue;

		CMapDisp *pDisp = EditDispMgr()->GetDisp( dispHandle );

		// get the corner index
		int ndxPt = GetCornerPointIndex( pDisp, pCornerData->ndxCorners[i] );
		if( ndxPt == -1 )
			continue;

		// set the averaged values
		pDisp->SetFieldDistance( ndxPt, avgDist );
		pDisp->SetFieldVector( ndxPt, vAvgField );
		pDisp->SetSubdivPosition( ndxPt, vAvgSubdivPos );
		pDisp->SetSubdivNormal( ndxPt, vAvgSubdivNormal );
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void SewCorner_ResolveSolid( SewCornerData_t *pCornerData )
{
	// create a clear vector - to reset the offset vector
	Vector vClear( 0.0f, 0.0f, 0.0f );

	// for all the faces at the corner
	for( int i = 0; i < pCornerData->faceCount; i++ )
	{
		// get the current corner face
		CMapFace *pFace = pCornerData->pFaces[i];
		if( !pFace )
			continue;

		// get the current displacement surface to reset, if solid = done!
		EditDispHandle_t dispHandle = pFace->GetDisp();
		if( dispHandle == EDITDISPHANDLE_INVALID )
			continue;

		CMapDisp *pDisp = EditDispMgr()->GetDisp( dispHandle );

		// get the face normal -- to reset the field vector
		Vector vNormal;
		pDisp->GetSurfNormal( vNormal );

		// get the corner index
		int ndxPt = GetCornerPointIndex( pDisp, pCornerData->ndxCorners[i] );
		if( ndxPt == -1 )
			continue;

		//
		// reset all neighbor surface data - field vector, distance, and offset
		//
		pDisp->SetFieldDistance( ndxPt, 0.0f );
		pDisp->SetFieldVector( ndxPt, vNormal );
		pDisp->SetSubdivPosition( ndxPt, vClear );
		pDisp->SetSubdivNormal( ndxPt, vNormal );
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void SewCorner_Resolve( void )
{
	// get the number of corners in the corner list
	int cornerCount = s_CornerData.Size();

	// resolve each corner
	for( int i = 0; i < cornerCount; i++ )
	{
		// get the current corner data struct
		SewCornerData_t *pCornerData = s_CornerData.Element( i );
		if( !pCornerData )
			continue;

		// determine if any of the faces is solid
		bool bSolid = SewCorner_IsSolid( pCornerData );

		// solid at corner -- reset corner data
		if( bSolid )
		{
			SewCorner_ResolveSolid( pCornerData );
		}
		// all disps at corner -- average
		else
		{
			SewCorner_ResolveDisp( pCornerData );
		}
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
SewTJuncData_t *SewTJunc_Create( void )
{
	SewTJuncData_t *pTJData = new SewTJuncData_t;
	if( pTJData )
	{
		// initialize the data
		pTJData->faceCount = 0;
		return pTJData;
	}

	return NULL;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void SewTJunc_Destroy( SewTJuncData_t *pTJData )
{
	if( pTJData )
	{
		delete pTJData;
		pTJData = NULL;
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool SewTJunc_IsSolid( SewTJuncData_t *pTJData )
{
	for( int i = 0; i < pTJData->faceCount; i++ )
	{
		if( Face_IsSolid( pTJData->pFaces[i] ) )
			return true;
	}

	return false;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void SewTJunc_Add( SewTJuncData_t *pTJData, CMapFace *pFace, int ndxCorner, int ndxEdge )
{
	assert( pTJData->faceCount >= 0 );
	assert( pTJData->faceCount < DISPSEW_FACES_AT_TJUNC );

	pTJData->pFaces[pTJData->faceCount] = pFace;
	pTJData->ndxCorners[pTJData->faceCount] = ndxCorner;
	pTJData->ndxEdges[pTJData->faceCount] = ndxEdge;
	pTJData->faceCount++;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void SewTJunc_AddToList( SewTJuncData_t *pTJData )
{
	// get the current t-junction point
	Vector pt;
	GetPointFromSurface( pTJData->pFaces[0], pTJData->ndxCorners[0], pt );

	//
	// check to see if the t-junction point already exists in the t-junction data list
	//
	int tjCount = s_TJData.Size();
	for( int i = 0; i < tjCount; i++ )
	{
		// get the compare t-junction point
		SewTJuncData_t *pCmpData = s_TJData.Element( i );
		if( !pCmpData )
			continue;

		Vector cmpPt;
		GetPointFromSurface( pCmpData->pFaces[0], pCmpData->ndxCorners[0], cmpPt );

		// compare the points - return if found
		if( PointCompareWithTolerance( pt, cmpPt, DISPSEW_POINT_TOLERANCE ) )
		{
			SewTJunc_Destroy( pTJData );
			return;
		}
	}

	// unique t-junction point -- add it to the list
	s_TJData.AddToTail( pTJData );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void SewTJunc_Build( void )
{
	//
	// get the "faces" selection list (contains displaced and non-displaced faces)
	//
	CFaceEditSheet *pSheet = GetMainWnd()->GetFaceEditSheet();
	if( !pSheet )
		return;

	//
	// for each face in list
	//
	int faceCount = pSheet->GetFaceListCount();

	for( int ndxFace = 0; ndxFace < faceCount; ndxFace++ )
	{
		// get the current face
		CMapFace *pFace = pSheet->GetFaceListDataFace( ndxFace );
		if( !pFace )
			continue;

		//
		// for each face point
		//
		int ptCount = pFace->GetPointCount();
		for( int ndxPt = 0; ndxPt < ptCount; ndxPt++ )
		{
			// get the current t-junction point
			Vector pt, tmpPt1, tmpPt2;
			GetPointFromSurface( pFace, ndxPt, tmpPt1 );
			GetPointFromSurface( pFace, (ndxPt+1)%ptCount, tmpPt2 );
			pt = ( tmpPt1 + tmpPt2 ) * 0.5f;

			// allocate new corner point
			SewTJuncData_t *pTJData = SewTJunc_Create();
			if( !pTJData )
				return;

			//
			// compare this point to all of the other points on all the other faces in the list
			//
			for( int ndxFace2 = 0; ndxFace2 < faceCount; ndxFace2++ )
			{
				// don't compare to itself
				if( ndxFace == ndxFace2 )
					continue;

				// get the current compare face
				CMapFace *pFace2 = pSheet->GetFaceListDataFace( ndxFace2 );
				if( !pFace2 )
					continue;

				//
				// for each compare face point
				//
				int ptCount2 = pFace2->GetPointCount();
				for( int ndxPt2 = 0; ndxPt2 < ptCount2; ndxPt2++ )
				{
					// get the current compare point
					Vector pt2;
					GetPointFromSurface( pFace2, ndxPt2, pt2 );

					// compare pt1 and pt2
					if( PointCompareWithTolerance( pt, pt2, DISPSEW_POINT_TOLERANCE ) )
					{
						SewTJunc_Add( pTJData, pFace2, ndxPt2, -1 );
					}
				}
			}

			// had neighbors -- add base point and add it to corner list
			if( pTJData->faceCount > 0 )
			{
				SewTJunc_Add( pTJData, pFace, -1, ndxPt );
				SewTJunc_AddToList( pTJData );
			}
			// no neighbors -- de-allocate
			else
			{
				SewTJunc_Destroy( pTJData );
			}
		}
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void SewTJunc_ResolveDisp( SewTJuncData_t *pTJData )
{
	// the field data accumulators
	float avgDist = 0.0f;
	Vector vAvgField( 0.0f, 0.0f, 0.0f );
	Vector vAvgSubdivPos( 0.0f, 0.0f, 0.0f );
	Vector vAvgSubdivNormal( 0.0f, 0.0f, 0.0f );

	// for all the faces at the t-junction
	for( int i = 0; i < pTJData->faceCount; i++ )
	{
		// get the current t-junction face
		CMapFace *pFace = pTJData->pFaces[i];
		if( !pFace )
			continue;

		// get the current displacement surface to reset, if solid = done!
		EditDispHandle_t dispHandle = pFace->GetDisp();
		if( dispHandle == EDITDISPHANDLE_INVALID )
			continue;

		CMapDisp *pDisp = EditDispMgr()->GetDisp( dispHandle );

		// get the t-junction index
		int ndxPt = -1;
		if( pTJData->ndxCorners[i] != -1 )
		{
			ndxPt = GetCornerPointIndex( pDisp, pTJData->ndxCorners[i] );
		}
		else if( pTJData->ndxEdges[i] != -1 )
		{
			int ndxEdgePt = pDisp->GetWidth() / 2;
			ndxPt = GetEdgePointIndex( pDisp, pTJData->ndxEdges[i], ndxEdgePt, true );
		}

		if( ndxPt == -1 )
			continue;

		float dist = pDisp->GetFieldDistance( ndxPt );
		avgDist += dist;

		Vector vTmp;
		pDisp->GetFieldVector( ndxPt, vTmp );
		vAvgField += vTmp;

		pDisp->GetSubdivPosition( ndxPt, vTmp );
		vAvgSubdivPos += vTmp;

		pDisp->GetSubdivNormal( ndxPt, vTmp );
		vAvgSubdivNormal += vTmp;
	}

	// calculate the average
	avgDist /= pTJData->faceCount;
	vAvgField /= pTJData->faceCount;
	vAvgSubdivPos /= pTJData->faceCount;
	vAvgSubdivNormal /= pTJData->faceCount;

	for( i = 0; i < pTJData->faceCount; i++ )
	{
		// get the current t-junction face
		CMapFace *pFace = pTJData->pFaces[i];
		if( !pFace )
			continue;

		// get the current displacement surface to reset, if solid = done!
		EditDispHandle_t dispHandle = pFace->GetDisp();
		if( dispHandle == EDITDISPHANDLE_INVALID )
			continue;

		CMapDisp *pDisp = EditDispMgr()->GetDisp( dispHandle );

		// get the t-junction index
		int ndxPt = -1;
		if( pTJData->ndxCorners[i] != -1 )
		{
			ndxPt = GetCornerPointIndex( pDisp, pTJData->ndxCorners[i] );
		}
		else if( pTJData->ndxEdges[i] != -1 )
		{
			int ndxEdgePt = pDisp->GetWidth() / 2;
			ndxPt = GetEdgePointIndex( pDisp, pTJData->ndxEdges[i], ndxEdgePt, true );
		}

		if( ndxPt == -1 )
			continue;

		// set the averaged values
		pDisp->SetFieldDistance( ndxPt, avgDist );
		pDisp->SetFieldVector( ndxPt, vAvgField );
		pDisp->SetSubdivPosition( ndxPt, vAvgSubdivPos );
		pDisp->SetSubdivNormal( ndxPt, vAvgSubdivNormal );
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void SewTJunc_ResolveSolid( SewTJuncData_t *pTJData )
{
	// create a clear vector - to reset the offset vector
	Vector vClear( 0.0f, 0.0f, 0.0f );

	// for all the faces at the t-junction
	for( int i = 0; i < pTJData->faceCount; i++ )
	{
		// get the current t-junction face
		CMapFace *pFace = pTJData->pFaces[i];
		if( !pFace )
			continue;

		// get the current displacement surface to reset, if solid = done!
		EditDispHandle_t dispHandle = pFace->GetDisp();
		if( dispHandle == EDITDISPHANDLE_INVALID )
			continue;

		CMapDisp *pDisp = EditDispMgr()->GetDisp( dispHandle );

		// get the face normal -- to reset the field vector
		Vector vNormal;
		pDisp->GetSurfNormal( vNormal );

		// get the t-junction index
		int ndxPt = -1;
		if( pTJData->ndxCorners[i] != -1 )
		{
			ndxPt = GetCornerPointIndex( pDisp, pTJData->ndxCorners[i] );
		}
		else if( pTJData->ndxEdges[i] != -1 )
		{
			int ndxEdgePt = pDisp->GetWidth() / 2;
			ndxPt = GetEdgePointIndex( pDisp, pTJData->ndxEdges[i], ndxEdgePt, true );
		}

		if( ndxPt == -1 )
			continue;

		//
		// reset all neighbor surface data - field vector, distance, and offset
		//
		pDisp->SetFieldDistance( ndxPt, 0.0f );
		pDisp->SetFieldVector( ndxPt, vNormal );
		pDisp->SetSubdivPosition( ndxPt, vClear );
		pDisp->SetSubdivNormal( ndxPt, vNormal );
	}
}



//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void SewTJunc_Resolve( void )
{
	// get the number of t-junctions in the t-junction list
	int tjCount = s_TJData.Size();

	// resolve each t-junction
	for( int i = 0; i < tjCount; i++ )
	{
		// get the current t-junction data struct
		SewTJuncData_t *pTJData = s_TJData.Element( i );
		if( !pTJData )
			continue;

		// determine if any of the faces is solid
		bool bSolid = SewTJunc_IsSolid( pTJData );

		// solid at t-junction -- reset t-junction data
		if( bSolid )
		{
			SewTJunc_ResolveSolid( pTJData );
		}
		// all disps at t-junction -- average
		else
		{
			SewTJunc_ResolveDisp( pTJData );
		}
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
SewEdgeData_t *SewEdge_Create( void )
{
	SewEdgeData_t *pEdgeData = new SewEdgeData_t;
	if( pEdgeData )
	{
		// initialize the data
		pEdgeData->faceCount = 0;
		return pEdgeData;
	}

	return NULL;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void SewEdge_Destroy( SewEdgeData_t *pEdgeData )
{
	if( pEdgeData )
	{
		delete pEdgeData;
		pEdgeData = NULL;
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline bool SewEdge_IsSolidNormal( SewEdgeData_t *pEdgeData )
{
	for( int i = 0; i < pEdgeData->faceCount; i++ )
	{
		if( Face_IsSolid( pEdgeData->pFaces[i] ) )
			return true;
	}

	return false;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline int SewEdge_TJIndex( SewEdgeData_t *pEdgeData, int type )
{
	for( int i = 0; i < pEdgeData->faceCount; i++ )
	{
		if( pEdgeData->type[i] == type )
			return i;
	}

	return -1;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline bool SewEdge_IsSolidTJunc( SewEdgeData_t *pEdgeData, int type )
{
	for( int i = 0; i < pEdgeData->faceCount; i++ )
	{	
		if( pEdgeData->type[i] != type )
			continue;

		if( Face_IsSolid( pEdgeData->pFaces[i] ) )
			return true;
	}

	return false;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void SewEdge_Add( SewEdgeData_t *pEdgeData, CMapFace *pFace, int ndxEdge, int type )
{
	assert( pEdgeData->faceCount >= 0 );
	assert( pEdgeData->faceCount < DISPSEW_FACES_AT_EDGE );

	pEdgeData->pFaces[pEdgeData->faceCount] = pFace;
	pEdgeData->ndxEdges[pEdgeData->faceCount] = ndxEdge;
	pEdgeData->type[pEdgeData->faceCount] = type; 
	pEdgeData->faceCount++;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void SewEdge_AddToListMerge( SewEdgeData_t *pEdgeData, SewEdgeData_t *pCmpData )
{
	for( int i = 0; i < pEdgeData->faceCount; i++ )
	{
		// t-junction edges already exist in both (skip it!)
		if( pEdgeData->type[i] == DISPSEW_EDGE_TJ )
			continue;

		for( int j = 0; j < pCmpData->faceCount; j++ )
		{
			// t-junction edges already exist in both (skip it!)
			if( pCmpData->type[j] == DISPSEW_EDGE_TJ )
				continue;

			if( pEdgeData->type[i] == pCmpData->type[j] )
				break;
		}

		// no match found -- add it
		if( j == pCmpData->faceCount )
		{
			SewEdge_Add( pCmpData, pEdgeData->pFaces[i], pEdgeData->ndxEdges[i], pEdgeData->type[i] );
		}
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void SewEdge_AddToListTJunc( SewEdgeData_t *pEdgeData )
{
	// find the t-junction edge
	int ndxTJ = SewEdge_TJIndex( pEdgeData, DISPSEW_EDGE_TJ );
	if( ndxTJ == -1 )
		return;

	// get the t-junction edge point count
	int ptCount = pEdgeData->pFaces[ndxTJ]->GetPointCount();

	// get the current t-junction edge (edge points)
	Vector edgePts[2];
	GetPointFromSurface( pEdgeData->pFaces[ndxTJ], pEdgeData->ndxEdges[ndxTJ], edgePts[0] );
	GetPointFromSurface( pEdgeData->pFaces[ndxTJ], (pEdgeData->ndxEdges[ndxTJ]+1)%ptCount, edgePts[1] );

	//
	// check to see if the edge already exists in the edge data list
	//
	int edgeCount = s_EdgeData.Size();
	for( int i = 0; i < edgeCount; i++ )
	{
		// get the edge points to compare against
		SewEdgeData_t *pCmpData = s_EdgeData.Element( i );
		if( !pCmpData )
			continue;

		// get the compare t-junction edge
		int ndxCmp = SewEdge_TJIndex( pCmpData, DISPSEW_EDGE_TJ );
		if( ndxCmp == -1 )
			continue;

		// get the compare face point count
		int ptCount2 = pCmpData->pFaces[ndxCmp]->GetPointCount();
		
		Vector edgePts2[2];
		GetPointFromSurface( pCmpData->pFaces[ndxCmp], pCmpData->ndxEdges[ndxCmp], edgePts2[0] );
		GetPointFromSurface( pCmpData->pFaces[ndxCmp], (pCmpData->ndxEdges[ndxCmp]+1)%ptCount2, edgePts2[1] );
		
		// compare the edges -- return if found
		int edgeType1, edgeType2;
		if( EdgeCompare( edgePts, edgePts2, edgeType1, edgeType2 ) )
		{
			SewEdge_AddToListMerge( pEdgeData, pCmpData );
			SewEdge_Destroy( pEdgeData );
			return;
		}
	}

	// unique edge -- add it to the list
	s_EdgeData.AddToTail( pEdgeData );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void SewEdge_AddToListNormal( SewEdgeData_t *pEdgeData )
{
	// get the face point count
	int ptCount = pEdgeData->pFaces[0]->GetPointCount();

	// get the current edge (edge points)
	Vector edgePts[2];
	GetPointFromSurface( pEdgeData->pFaces[0], pEdgeData->ndxEdges[0], edgePts[0] );
	GetPointFromSurface( pEdgeData->pFaces[0], (pEdgeData->ndxEdges[0]+1)%ptCount, edgePts[1] );

	//
	// check to see if the edge already exists in the edge data list
	//
	int edgeCount = s_EdgeData.Size();
	for( int i = 0; i < edgeCount; i++ )
	{
		// get the edge points to compare against
		SewEdgeData_t *pCmpData = s_EdgeData.Element( i );
		if( !pCmpData )
			continue;

		// compare against each edge (all colinear) in struct
		for( int j = 0; j < pCmpData->faceCount; j++ )
		{
			// get the compare face point count
			int ptCount2 = pCmpData->pFaces[j]->GetPointCount();

			Vector edgePts2[2];
			GetPointFromSurface( pCmpData->pFaces[j], pCmpData->ndxEdges[j], edgePts2[0] );
			GetPointFromSurface( pCmpData->pFaces[j], (pCmpData->ndxEdges[j]+1)%ptCount2, edgePts2[1] );
		
			// compare the edges -- return if found
			int edgeType1, edgeType2;
			if( EdgeCompare( edgePts, edgePts2, edgeType1, edgeType2 ) )
			{
				SewEdge_Destroy( pEdgeData );
				return;
			}
		}
	}

	// unique edge -- add it to the list
	s_EdgeData.AddToTail( pEdgeData );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void SewEdge_AddToList( SewEdgeData_t *pEdgeData )
{
	// if this is a "normal" edge - handle it
	if( pEdgeData->type[0] == DISPSEW_EDGE_NORMAL )
	{
		SewEdge_AddToListNormal( pEdgeData );
		return;
	}

	// this is a "t-junction" edge - handle it
	SewEdge_AddToListTJunc( pEdgeData );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void SewEdge_Build( void )
{
	//
	// get the "faces" selection list (contains displaced and non-displaced faces)
	//
	CFaceEditSheet *pSheet = GetMainWnd()->GetFaceEditSheet();
	if( !pSheet )
		return;

	//
	// for each face in list
	//
	int faceCount = pSheet->GetFaceListCount();

	for( int ndxFace = 0; ndxFace < faceCount; ndxFace++ )
	{
		// get the current face
		CMapFace *pFace = pSheet->GetFaceListDataFace( ndxFace );
		if( !pFace )
			continue;

		//
		// for each face edge
		//
		int ptCount = pFace->GetPointCount();
		for( int ndxPt = 0; ndxPt < ptCount; ndxPt++ )
		{
			// get the current edge points
			int type1_keep;
			Vector edgePts[2];
			GetPointFromSurface( pFace, ndxPt, edgePts[0] );
			GetPointFromSurface( pFace, (ndxPt+1)%ptCount, edgePts[1] );

			// allocate new edge
			SewEdgeData_t *pEdgeData = SewEdge_Create();
			if( !pEdgeData )
				return;

			//
			// compare this edge to all of the other edges on all the other faces in the list
			//
			for( int ndxFace2 = 0; ndxFace2 < faceCount; ndxFace2++ )
			{
				// don't compare to itself
				if( ndxFace == ndxFace2 )
					continue;

				// get the current compare face
				CMapFace *pFace2 = pSheet->GetFaceListDataFace( ndxFace2 );
				if( !pFace2 )
					continue;

				//
				// for each compare face edge
				//
				int ptCount2 = pFace2->GetPointCount();
				for( int ndxPt2 = 0; ndxPt2 < ptCount2; ndxPt2++ )
				{
					// get the current compare edge point
					Vector edgePts2[2];
					GetPointFromSurface( pFace2, ndxPt2, edgePts2[0] );
					GetPointFromSurface( pFace2, (ndxPt2+1)%ptCount2, edgePts2[1] );

					// compare pt1 and pt2
					int type1, type2;
					if( EdgeCompare( edgePts, edgePts2, type1, type2 ) )
					{
						SewEdge_Add( pEdgeData, pFace2, ndxPt2, type2 );
						type1_keep = type1;
					}
				}
			}

			// had neighbors -- add base point and add it to corner list
			if( pEdgeData->faceCount > 0 )
			{
				SewEdge_Add( pEdgeData, pFace, ndxPt, type1_keep );
				SewEdge_AddToList( pEdgeData );
			}
			// no neighbors -- de-allocate
			else
			{
				SewEdge_Destroy( pEdgeData );
			}
		}
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void SewEdge_ResolveDispTJunc( SewEdgeData_t *pEdgeData, int ndxTJ, int ndxTJNeighbor, bool bStart )
{
	//
	// handle displacement sewing to displacement t-junction edge
	//
	EditDispHandle_t tjEdgeHandle = pEdgeData->pFaces[ndxTJ]->GetDisp();
	EditDispHandle_t edgeHandle = pEdgeData->pFaces[ndxTJNeighbor]->GetDisp();
	if( ( tjEdgeHandle == EDITDISPHANDLE_INVALID ) || ( edgeHandle == EDITDISPHANDLE_INVALID ) )
		return;

	CMapDisp *pTJEdgeDisp = EditDispMgr()->GetDisp( tjEdgeHandle );
	CMapDisp *pEdgeDisp = EditDispMgr()->GetDisp( edgeHandle );

	//
	// get the t-junction edge interval (or half of it)
	//
	int tjWidth = pTJEdgeDisp->GetWidth();
	int tjInterval = pTJEdgeDisp->GetWidth() / 2;

	//
	// get edge interval
	//
	int edgeWidth = pEdgeDisp->GetWidth();
	int edgeInterval = pEdgeDisp->GetWidth();

	int ratio = ( edgeInterval - 1 ) / tjInterval;

	bool bFlip = ( ratio < 1 );
	if( bFlip )
	{
		ratio = tjInterval / ( edgeInterval - 1 );
	}

	//
	// average the "like" points
	//
	if( bStart )
	{
		if( bFlip )
		{
			for( int i = 1, j = ratio; i < edgeInterval; i++, j += ratio )
			{
				int ndxTJPt, ndxEdgePt;
				ndxTJPt = GetEdgePointIndex( pTJEdgeDisp, pEdgeData->ndxEdges[ndxTJ], j, true );
				ndxEdgePt = GetEdgePointIndex( pEdgeDisp, pEdgeData->ndxEdges[ndxTJNeighbor], i, false );

				// average
				AverageVectorFieldData( pEdgeDisp, ndxEdgePt, pTJEdgeDisp, ndxTJPt );
			}
		}
		else
		{
			for( int i = 1, j = ratio; i < tjInterval; i++, j += ratio )
			{
				int ndxTJPt, ndxEdgePt;
				
				ndxTJPt = GetEdgePointIndex( pTJEdgeDisp, pEdgeData->ndxEdges[ndxTJ], i, true );
				ndxEdgePt = GetEdgePointIndex( pEdgeDisp, pEdgeData->ndxEdges[ndxTJNeighbor], j, false );
				
				// average
				AverageVectorFieldData( pEdgeDisp, ndxEdgePt, pTJEdgeDisp, ndxTJPt );
			}
		}
	}
	else
	{
		if( bFlip )
		{
			for( int i = 1, j = ratio; i < edgeWidth; i++, j += ratio )
			{
				int ndxTJPt, ndxEdgePt;
				ndxTJPt = GetEdgePointIndex( pTJEdgeDisp, pEdgeData->ndxEdges[ndxTJ], j, false );
				ndxEdgePt = GetEdgePointIndex( pEdgeDisp, pEdgeData->ndxEdges[ndxTJNeighbor], i, true );

				// average
				AverageVectorFieldData( pEdgeDisp, ndxEdgePt, pTJEdgeDisp, ndxTJPt );
			}
		}
		else
		{
			for( int i = ( tjInterval + 1 ), j = ratio; i < ( tjWidth - 1 ); i++, j += ratio )
			{
				int ndxTJPt, ndxEdgePt;
				ndxTJPt = GetEdgePointIndex( pTJEdgeDisp, pEdgeData->ndxEdges[ndxTJ], i, true );
				ndxEdgePt = GetEdgePointIndex( pEdgeDisp, pEdgeData->ndxEdges[ndxTJNeighbor], j, false );
				
				// average
				AverageVectorFieldData( pEdgeDisp, ndxEdgePt, pTJEdgeDisp, ndxTJPt );
			}
		}
	}

	//
	// linearly interpolate the "unlike" points
	//
	float blendRatio = 1.0f / ratio;
	
	if( bFlip )
	{
		for( int i = 0; i < ( tjWidth - ratio ); i += ratio )
		{
			int ndxStart = i;
			int ndxEnd = ( i + ratio );
			
			int ndxStartPt = GetEdgePointIndex( pTJEdgeDisp, pEdgeData->ndxEdges[ndxTJ], ndxStart, true );
			int ndxEndPt = GetEdgePointIndex( pTJEdgeDisp, pEdgeData->ndxEdges[ndxTJ], ndxEnd, true );
			
			for( int j = ( ndxStart + 1 ); j < ndxEnd; j++ )
			{
				float blend = blendRatio * ( j - ndxStart );
				
				int ndxDst = GetEdgePointIndex( pTJEdgeDisp, pEdgeData->ndxEdges[ndxTJ], j, true );
				
				BlendVectorFieldData( pTJEdgeDisp, ndxStartPt, ndxDst, pTJEdgeDisp, ndxEndPt, ndxDst, blend );
			}
		}
	}
	else
	{
		for( int i = 0; i < ( edgeWidth - ratio ); i += ratio )
		{
			int ndxStart = i;
			int ndxEnd = ( i + ratio );
			
			int ndxStartPt = GetEdgePointIndex( pEdgeDisp, pEdgeData->ndxEdges[ndxTJNeighbor], ndxStart, true );
			int ndxEndPt = GetEdgePointIndex( pEdgeDisp, pEdgeData->ndxEdges[ndxTJNeighbor], ndxEnd, true );
			
			for( int j = ( ndxStart + 1 ); j < ndxEnd; j++ )
			{
				float blend = blendRatio * ( j - ndxStart );
				
				int ndxDst = GetEdgePointIndex( pEdgeDisp, pEdgeData->ndxEdges[ndxTJNeighbor], j, true );
				
				BlendVectorFieldData( pEdgeDisp, ndxStartPt, ndxDst, pEdgeDisp, ndxEndPt, ndxDst, blend );
			}
		}
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void SewEdge_ResolveSolidTJunc( SewEdgeData_t *pEdgeData, int type, bool bStart )
{
	// create an empty vector to reset the offset with
	Vector vClear( 0.0f, 0.0f, 0.0f );

	for( int i = 0; i < pEdgeData->faceCount; i++ )
	{	
		if( pEdgeData->type[i] != type )
			continue;

		// get the displacement surface associated with the face
		EditDispHandle_t dispHandle = pEdgeData->pFaces[i]->GetDisp();
		if( dispHandle == EDITDISPHANDLE_INVALID )
			continue;

		CMapDisp *pDisp = EditDispMgr()->GetDisp( dispHandle );

		// get surface normal, to reset vector field to base state
		Vector vNormal;
		pDisp->GetSurfNormal( vNormal );

		// reset tjstart and tjend
		if( type != DISPSEW_EDGE_TJ )
		{
			//
			// for all points along the edge -- reset
			//
			int width = pDisp->GetWidth();
			for( int j = 1; j < ( width - 1 ); j++ )
			{
				// get the edge point index
				int ndxPt = GetEdgePointIndex( pDisp, pEdgeData->ndxEdges[i], j, true );
				if( ndxPt == -1 )
					continue;
				
				//
				// reset displacement data (dist, field vector, and offset vector)
				//
				pDisp->SetFieldDistance( ndxPt, 0.0f );
				pDisp->SetFieldVector( ndxPt, vNormal );
				pDisp->SetSubdivPosition( ndxPt, vClear );
				pDisp->SetSubdivNormal( ndxPt, vNormal );
			}
		}
		// reset tj (upper and lower)
		else
		{
			//
			// for all points along the edge -- reset
			//
			int width = pDisp->GetWidth();
			int widthDiv2 = width / 2;

			if( bStart )
			{
				for( int j = 1; j < widthDiv2; j++ )
				{
					// get the edge point index
					int ndxPt = GetEdgePointIndex( pDisp, pEdgeData->ndxEdges[i], j, true );
					if( ndxPt == -1 )
						continue;
					
					//
					// reset displacement data (dist, field vector, and offset vector)
					//
					pDisp->SetFieldDistance( ndxPt, 0.0f );
					pDisp->SetFieldVector( ndxPt, vNormal );
					pDisp->SetSubdivPosition( ndxPt, vClear );
					pDisp->SetSubdivNormal( ndxPt, vNormal );
				}
			}
			else
			{
				for( int j = ( widthDiv2 + 1 ); j < ( width - 1 ); j++ )
				{
					// get the edge point index
					int ndxPt = GetEdgePointIndex( pDisp, pEdgeData->ndxEdges[i], j, true );
					if( ndxPt == -1 )
						continue;
					
					//
					// reset displacement data (dist, field vector, and offset vector)
					//
					pDisp->SetFieldDistance( ndxPt, 0.0f );
					pDisp->SetFieldVector( ndxPt, vNormal );
					pDisp->SetSubdivPosition( ndxPt, vClear );
					pDisp->SetSubdivNormal( ndxPt, vNormal );
				}
			}
		}
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void SewEdge_ResolveDispNormal( SewEdgeData_t *pEdgeData )
{
	//
	// get displacement surfaces -- if any
	//
	EditDispHandle_t handle1 = pEdgeData->pFaces[0]->GetDisp();
	EditDispHandle_t handle2 = pEdgeData->pFaces[1]->GetDisp();
	if( ( handle1 == EDITDISPHANDLE_INVALID ) || ( handle2 == EDITDISPHANDLE_INVALID ) )
		return;

	CMapDisp *pEdgeDisp1 = EditDispMgr()->GetDisp( handle1 );
	CMapDisp *pEdgeDisp2 = EditDispMgr()->GetDisp( handle2 );
	
	//
	// sew displacement edges
	//
	
	//
	// find displacement with smallest/largest interval
	//
	CMapDisp *pSmDisp, *pLgDisp;
	int smInterval, lgInterval;
	int ndxSmEdge, ndxLgEdge;
	
	if( pEdgeDisp1->GetWidth() > pEdgeDisp2->GetWidth() )
	{
		pSmDisp = pEdgeDisp2;
		ndxSmEdge = pEdgeData->ndxEdges[1];
		smInterval = pEdgeDisp2->GetWidth();
		
		pLgDisp = pEdgeDisp1;
		ndxLgEdge = pEdgeData->ndxEdges[0];
		lgInterval = pEdgeDisp1->GetWidth();
	}
	else
	{
		pSmDisp = pEdgeDisp1;
		ndxSmEdge = pEdgeData->ndxEdges[0];
		smInterval = pEdgeDisp1->GetWidth();
		
		pLgDisp = pEdgeDisp2;
		ndxLgEdge = pEdgeData->ndxEdges[1];
		lgInterval = pEdgeDisp2->GetWidth();
	}
	
	// calculate the ratio
	int ratio = ( lgInterval - 1 ) / ( smInterval - 1 );
	
	//
	// average "like" points
	//
	for( int ndxSm = 1, ndxLg = ratio; ndxSm < ( smInterval - 1 ); ndxSm++, ndxLg += ratio )
	{
		int ndxSmPt = GetEdgePointIndex( pSmDisp, ndxSmEdge, ndxSm, true );
		int ndxLgPt = GetEdgePointIndex( pLgDisp, ndxLgEdge, ndxLg, false );
		
		// average
		AverageVectorFieldData( pSmDisp, ndxSmPt, pLgDisp, ndxLgPt );
	}
	
	//
	// linearly interpolate the "unlike" points
	//
	float blendRatio = 1.0f / ratio;
	
	for( ndxLg = 0; ndxLg < ( lgInterval - 1 ); ndxLg += ratio )
	{
		int ndxStart = ndxLg;
		int ndxEnd = ( ndxLg + ratio );
		
		int ndxStartPt = GetEdgePointIndex( pLgDisp, ndxLgEdge, ndxStart, true );
		int ndxEndPt = GetEdgePointIndex( pLgDisp, ndxLgEdge, ndxEnd, true );
		
		for( int ndx = ( ndxStart + 1 ); ndx < ndxEnd; ndx++ )
		{
			float blend = blendRatio * ( ndx - ndxStart );
			int ndxDst = GetEdgePointIndex( pLgDisp, ndxLgEdge, ndx, true );
			BlendVectorFieldData( pLgDisp, ndxStartPt, ndxDst, pLgDisp, ndxEndPt, ndxDst, blend );
		}
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void SewEdge_ResolveSolidNormal( SewEdgeData_t *pEdgeData )
{
	// create an empty vector to reset the offset with
	Vector vClear( 0.0f, 0.0f, 0.0f );

	for( int i = 0; i < pEdgeData->faceCount; i++ )
	{
		// get the displacement surface associated with the face
		EditDispHandle_t handle = pEdgeData->pFaces[i]->GetDisp();
		if( handle == EDITDISPHANDLE_INVALID )
			continue;

		CMapDisp *pDisp = EditDispMgr()->GetDisp( handle );

		// get surface normal, to reset vector field to base state
		Vector vNormal;
		pDisp->GetSurfNormal( vNormal );

		//
		// for all points along the edge -- reset
		//
		int width = pDisp->GetWidth();
		for( int j = 0; j < width; j++ )
		{
			// get the edge point index
			int ndxPt = GetEdgePointIndex( pDisp, pEdgeData->ndxEdges[i], j, true );
			if( ndxPt == -1 )
				continue;
			
			//
			// reset displacement data (dist, field vector, and offset vector)
			//
			pDisp->SetFieldDistance( ndxPt, 0.0f );
			pDisp->SetFieldVector( ndxPt, vClear );
			pDisp->SetSubdivPosition( ndxPt, vClear );
			pDisp->SetSubdivNormal( ndxPt, vNormal );
		}
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void SewEdge_Resolve( void )
{
	// get the number of edges in the edge list
	int edgeCount = s_EdgeData.Size();

	// resolve each edge
	for( int i = 0; i < edgeCount; i++ )
	{
		// get the current edge data struct
		SewEdgeData_t *pEdgeData = s_EdgeData.Element( i );
		if( !pEdgeData )
			continue;

		// handle "normal" edge
		if( pEdgeData->type[0] == DISPSEW_EDGE_NORMAL )
		{
			// solid "normal" edge
			if( SewEdge_IsSolidNormal( pEdgeData ) )
			{
				SewEdge_ResolveSolidNormal( pEdgeData );
			}
			// disps "normal" edge
			else
			{
				SewEdge_ResolveDispNormal( pEdgeData );
			}
		}
		// handle "t-junction" edge
		else
		{
			int ndxTJ = SewEdge_TJIndex( pEdgeData, DISPSEW_EDGE_TJ );
			int ndxTJStart = SewEdge_TJIndex( pEdgeData, DISPSEW_EDGE_TJSTART );
			int ndxTJEnd = SewEdge_TJIndex( pEdgeData, DISPSEW_EDGE_TJEND );

			if( SewEdge_IsSolidTJunc( pEdgeData, DISPSEW_EDGE_TJ ) )
			{
				// reset both start and end t-junction edges if they exist
				if( ndxTJStart != -1 )
				{	
					SewEdge_ResolveSolidTJunc( pEdgeData, DISPSEW_EDGE_TJSTART, false );
				}

				if( ndxTJEnd != -1 )
				{
					SewEdge_ResolveSolidTJunc( pEdgeData, DISPSEW_EDGE_TJEND, false );
				}

				continue;
			}

			// handle start edge
			if( ndxTJStart != -1 )
			{
				if( SewEdge_IsSolidTJunc( pEdgeData, DISPSEW_EDGE_TJSTART ) )
				{
					SewEdge_ResolveSolidTJunc( pEdgeData, DISPSEW_EDGE_TJ, true );
				}
				else
				{
					SewEdge_ResolveDispTJunc( pEdgeData, ndxTJ, ndxTJStart, true );
				}
			}

			// handle end edge
			if( ndxTJEnd != -1 )
			{
				if( SewEdge_IsSolidTJunc( pEdgeData, DISPSEW_EDGE_TJEND ) )
				{
					SewEdge_ResolveSolidTJunc( pEdgeData, DISPSEW_EDGE_TJ, false );
				}
				else
				{
					SewEdge_ResolveDispTJunc( pEdgeData, ndxTJ, ndxTJEnd, false );
				}
			}
		}
	}
}
