126 lines
4.4 KiB
C#
126 lines
4.4 KiB
C#
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)
|
|
|
|
/// <summary>
|
|
/// Gets the convex hull.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// http://www.softsurfer.com/Archive/algorithm_0109/algorithm_0109.htm
|
|
/// </remarks>
|
|
/// <returns></returns>
|
|
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<Vector2>
|
|
{
|
|
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
|
|
}
|
|
} |