using System; namespace FarseerPhysics.Common.ConvexHull { public static class GiftWrap { // From Eric Jordan's convex decomposition library (box2D rev 32) /// /// 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. /// /// The vertices. /// 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; } } }