using System.Collections.Generic; using Microsoft.Xna.Framework; namespace FarseerPhysics.Common.ConvexHull { public static class ChainHull { //Andrew's monotone chain 2D convex hull algorithm. //Copyright 2001, softSurfer (www.softsurfer.com) /// /// Gets the convex hull. /// /// /// http://www.softsurfer.com/Archive/algorithm_0109/algorithm_0109.htm /// /// public static Vertices GetConvexHull(Vertices P) { P.Sort(new PointComparer()); Vector2[] H = new Vector2[P.Count]; Vertices res = new Vertices(); int n = P.Count; int bot, top = -1; // indices for bottom and top of the stack int i; // array scan index // Get the indices of points with min x-coord and min|max y-coord int minmin = 0, minmax; float xmin = P[0].X; for (i = 1; i < n; i++) if (P[i].X != xmin) break; minmax = i - 1; if (minmax == n - 1) { // degenerate case: all x-coords == xmin H[++top] = P[minmin]; if (P[minmax].Y != P[minmin].Y) // a nontrivial segment H[++top] = P[minmax]; H[++top] = P[minmin]; // add polygon endpoint for (int j = 0; j < top + 1; j++) { res.Add(H[j]); } return res; } top = res.Count - 1; // Get the indices of points with max x-coord and min|max y-coord int maxmin, maxmax = n - 1; float xmax = P[n - 1].X; for (i = n - 2; i >= 0; i--) if (P[i].X != xmax) break; maxmin = i + 1; // Compute the lower hull on the stack H H[++top] = P[minmin]; // push minmin point onto stack i = minmax; while (++i <= maxmin) { // the lower line joins P[minmin] with P[maxmin] if (MathUtils.Area(P[minmin], P[maxmin], P[i]) >= 0 && i < maxmin) continue; // ignore P[i] above or on the lower line while (top > 0) // there are at least 2 points on the stack { // test if P[i] is left of the line at the stack top if (MathUtils.Area(H[top - 1], H[top], P[i]) > 0) break; // P[i] is a new hull vertex else top--; // pop top point off stack } H[++top] = P[i]; // push P[i] onto stack } // Next, compute the upper hull on the stack H above the bottom hull if (maxmax != maxmin) // if distinct xmax points H[++top] = P[maxmax]; // push maxmax point onto stack bot = top; // the bottom point of the upper hull stack i = maxmin; while (--i >= minmax) { // the upper line joins P[maxmax] with P[minmax] if (MathUtils.Area(P[maxmax], P[minmax], P[i]) >= 0 && i > minmax) continue; // ignore P[i] below or on the upper line while (top > bot) // at least 2 points on the upper stack { // test if P[i] is left of the line at the stack top if (MathUtils.Area(H[top - 1], H[top], P[i]) > 0) break; // P[i] is a new hull vertex else top--; // pop top point off stack } H[++top] = P[i]; // push P[i] onto stack } if (minmax != minmin) H[++top] = P[minmin]; // push joining endpoint onto stack for (int j = 0; j < top + 1; j++) { res.Add(H[j]); } return res; } #region Nested type: PointComparer public class PointComparer : Comparer { public override int Compare(Vector2 a, Vector2 b) { int f = a.X.CompareTo(b.X); return f != 0 ? f : a.Y.CompareTo(b.Y); } } #endregion } }