123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 |
- 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;
- }
- }
- }
|