//--------------------------------------------------------------------------------------------------
// Name: ValidateExtractedOutline
// Desc: Performs a final validation pass on the extracted outline
//--------------------------------------------------------------------------------------------------
bool CBreakableGlassSystem::ValidateExtractedOutline(SBreakableGlassPhysData& data, SBreakableGlassInitParams& initParams)
{
	bool valid = true;

	// Check for overlapping points (leads to FPE during triangulation)
	if (initParams.pInitialFrag && initParams.numInitialFragPts > 0)
	{
		const Vec2* pPts = initParams.pInitialFrag;
		const uint numEdges = initParams.numInitialFragPts-1;
		const float minEdgeLen = 0.0001f;

		for (uint i = 0, j = 1; i < numEdges; ++i, ++j)
		{
			const Vec2 edge(pPts[i] - pPts[j]);

			if (edge.GetLength2() < minEdgeLen)
			{
				LOG_GLASS_ERROR("Extracted mesh has invalid edges.");

				valid = false;
				break;
			}
		}
	}

	// Check for overlapping UVs (leads to FPE during uv basis calculation)
	if (valid)
	{
		const Vec2 uvPtA(data.uvBasis[0].x, data.uvBasis[0].y);
		const Vec2 uvPtB(data.uvBasis[1].x, data.uvBasis[1].y);
		const Vec2 uvPtC(data.uvBasis[2].x, data.uvBasis[2].y);

		const Vec2 uvEdge0(uvPtC - uvPtA);
		const Vec2 uvEdge1(uvPtB - uvPtA);

		const float dot00 = uvEdge0.Dot(uvEdge0);
		const float dot01 = uvEdge0.Dot(uvEdge1);
		const float dot11 = uvEdge1.Dot(uvEdge1);
		const float epsilon = 0.001f;

		if (fabs_tpl(dot00 * dot11 - dot01 * dot01) < epsilon)
		{
			LOG_GLASS_ERROR("Extracted mesh has invalid uv layout.");
			valid = false;
		}
	}

	return valid;
}//-------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------
// Name: CalculateGlassAnchors
// Desc: Ensures mesh is within bounds and calculates anchor points
//--------------------------------------------------------------------------------------------------
void CBreakableGlassSystem::CalculateGlassAnchors(SBreakableGlassPhysData& data, SBreakableGlassInitParams& initParams)
{
	// Apply half-size offset to uv basis
	const Vec2& size = initParams.size;
	const Vec2 halfSize = size * 0.5f;

	for (uint i = 0; i < 3; ++i)
	{
		data.uvBasis[i].x += halfSize.x;
		data.uvBasis[i].y += halfSize.y;
	}

	// Only continue if non-default mesh used
	Vec2* pPts = initParams.pInitialFrag;
	const uint numPts = initParams.numInitialFragPts;
	
	if (pPts && numPts >= 3)
	{
		Vec2 minPt = size;
		Vec2 maxPt = Vec2Constants<float>::fVec2_Zero;

		// Offset fragment data to glass working space
		for (uint i = 0; i < numPts; ++i)
		{
			pPts[i] += halfSize;

			// Update bounds
			minPt.x = min(minPt.x, pPts[i].x);
			minPt.y = min(minPt.y, pPts[i].y);

			maxPt.x = max(maxPt.x, pPts[i].x);
			maxPt.y = max(maxPt.y, pPts[i].y);	
		}

		// Calculate any final offset towards working bounds
		minPt.x = min(minPt.x, 0.0f);
		minPt.y = min(minPt.y, 0.0f);
		
		maxPt.x = max(maxPt.x - size.x, 0.0f);
		maxPt.y = max(maxPt.y - size.y, 0.0f);

		Vec2 offset;
		offset.x = (maxPt.x > 0.0f) ? -maxPt.x : -minPt.x;
		offset.y = (maxPt.y > 0.0f) ? -maxPt.y : -minPt.y;

		// Adjust bounds if off
		if (offset.GetLength2() > 0.0f)
		{
			for (uint i = 0; i < numPts; ++i)
			{
				pPts[i] += offset;
			}

			for (uint i = 0; i < 3; ++i)
			{
				data.uvBasis[i].x += offset.x;
				data.uvBasis[i].y += offset.y;
			}
		}

		// Determine best anchor points	
		const uint maxNumAnchors = SBreakableGlassInitParams::MaxNumAnchors;
		const uint numAnchors = min(numPts, maxNumAnchors);
		initParams.numAnchors = numAnchors;

		if (numPts <= maxNumAnchors)
		{
			// If few enough points, use them directly
			for (uint i = 0; i < numAnchors; ++i)
			{
				initParams.anchors[i] = pPts[i];
			}	
		}
		else
		{	
			// Else, determine mesh points closest to corners
			const Vec2 anchorPos[maxNumAnchors] =
			{
				Vec2(0.0f,							0.0f),
				Vec2(0.0f,							initParams.size.y),
				Vec2(initParams.size.x, 0.0f),
				Vec2(initParams.size.x, initParams.size.y)
			};

			uint anchorPts[maxNumAnchors] = {0,0,0,0};

			for (uint i = 0; i < maxNumAnchors; ++i)
			{
				float anchorDistSq = FLT_MAX;

				for (uint j = 0; j < numPts; ++j)
				{
					// Store point if new closest found
					const float distSq = (anchorPos[i] - pPts[j]).GetLength2();

					if (distSq < anchorDistSq)
					{
						// Avoid duplicates
						bool duplicate = false;
						for (uint k = 0; k < i; ++k)
						{
							if (anchorPts[k] == j)
							{
								duplicate = true;
								break;
							}
						}

						if (!duplicate)
						{
							anchorDistSq = distSq;
							anchorPts[i] = j;
						}
					}
				}

				// Save closest pt to this corner
				initParams.anchors[i] = pPts[anchorPts[i]];
			}
		}

		// Calculate approx. polygon center
		Vec2 center(Vec2Constants<float>::fVec2_Zero);
		const float fNumPts = (float)numPts;

		for (uint i = 0; i < numPts; ++i)
		{
			center += pPts[i];
		}
		
		center *= __fres(fNumPts);

		// Finally, shift anchors slightly towards center
		const float anchorPtShift = 0.015f;

		for (uint i = 0; i < numAnchors; ++i)
		{
			Vec2 toCenter = (center - initParams.anchors[i]).GetNormalized();
			initParams.anchors[i] += toCenter * anchorPtShift;
		}
	}
}//-------------------------------------------------------------------------------------------------