axiosengine/axios/Common/ConvexHull/GiftWrap.cs

99 lines
3.7 KiB
C#

using System;
namespace FarseerPhysics.Common.ConvexHull
{
public static class GiftWrap
{
// From Eric Jordan's convex decomposition library (box2D rev 32)
/// <summary>
/// Find the convex hull of a point cloud using "Gift-wrap" algorithm - start
/// with an extremal point, and walk around the outside edge by testing
/// angles.
///
/// Runs in O(N*S) time where S is number of sides of resulting polygon.
/// Worst case: point cloud is all vertices of convex polygon: O(N^2).
/// There may be faster algorithms to do this, should you need one -
/// this is just the simplest. You can get O(N log N) expected time if you
/// try, I think, and O(N) if you restrict inputs to simple polygons.
/// Returns null if number of vertices passed is less than 3.
/// Results should be passed through convex decomposition afterwards
/// to ensure that each shape has few enough points to be used in Box2d.
///
/// Warning: May be buggy with colinear points on hull.
/// </summary>
/// <param name="vertices">The vertices.</param>
/// <returns></returns>
public static Vertices GetConvexHull(Vertices vertices)
{
if (vertices.Count < 3)
return vertices;
int[] edgeList = new int[vertices.Count];
int numEdges = 0;
float minY = float.MaxValue;
int minYIndex = vertices.Count;
for (int i = 0; i < vertices.Count; ++i)
{
if (vertices[i].Y < minY)
{
minY = vertices[i].Y;
minYIndex = i;
}
}
int startIndex = minYIndex;
int winIndex = -1;
float dx = -1.0f;
float dy = 0.0f;
while (winIndex != minYIndex)
{
float maxDot = -2.0f;
float nrm;
for (int i = 0; i < vertices.Count; ++i)
{
if (i == startIndex)
continue;
float newdx = vertices[i].X - vertices[startIndex].X;
float newdy = vertices[i].Y - vertices[startIndex].Y;
nrm = (float)Math.Sqrt(newdx * newdx + newdy * newdy);
nrm = (nrm == 0.0f) ? 1.0f : nrm;
newdx /= nrm;
newdy /= nrm;
//Dot products act as proxy for angle
//without requiring inverse trig.
float newDot = newdx * dx + newdy * dy;
if (newDot > maxDot)
{
maxDot = newDot;
winIndex = i;
}
}
edgeList[numEdges++] = winIndex;
dx = vertices[winIndex].X - vertices[startIndex].X;
dy = vertices[winIndex].Y - vertices[startIndex].Y;
nrm = (float)Math.Sqrt(dx * dx + dy * dy);
nrm = (nrm == 0.0f) ? 1.0f : nrm;
dx /= nrm;
dy /= nrm;
startIndex = winIndex;
}
Vertices returnVal = new Vertices(numEdges);
for (int i = 0; i < numEdges; i++)
{
returnVal.Add(vertices[edgeList[i]]);
//Debug.WriteLine(string.Format("{0}, {1}", vertices[edgeList[i]].X, vertices[edgeList[i]].Y));
}
//Not sure if we need this
//returnVal.MergeParallelEdges(Settings.b2_angularSlop);
return returnVal;
}
}
}