凸包とは、 Google検索:凸包
板の上に幾つも釘を打ち、外周から輪ゴムをかけて、 ゴムと接している釘で構成される多角形。
グラハムスキャン
まず、端にある点を基準として、それに対する角度の順に点を並び替える。
並び替えた順に点を見ていき、 一つ前の点H 現在の点I 次点J の角度∠HIJ が マイナスの場合、凹みであると判断して、点Iを削除する。
そして、その次の点Kとの ∠HJK を見る。
//凸包を得る
static public Vec2D[] convex_hull(Vec2D[] points)
{
List<KeyValuePair<double, Vec2D>> dict = new List<KeyValuePair<double, Vec2D>>();
//Y最小値を見つける
int max = 0;
for (int i = 1; i < points.Length; i++) {
if (points[max].y > points[i].y)
max = i;
else if (points[max].y == points[i].y && points[max].x < points[i].x) {
max = i;
}
}
//角度とともにlist に格納する
for (int i = 0; i < points.Length; i++) {
if (i == max) {
dict.Add(new KeyValuePair<double, Vec2D>(0, points[i]));
} else {
double theta = Math.Atan2(points[i].y - points[max].y, points[i].x - points[max].x);
if (theta < 0) theta = 2 * Math.PI + theta;
dict.Add(new KeyValuePair<double, Vec2D>(theta, points[i]));
}
}
//ソートする
System.Comparison<KeyValuePair<double, Vec2D>> cmp = new Comparison<KeyValuePair<double, Vec2D>>(cmpfunc);
dict.Sort(cmp);
//順に比較し、不要な点を排除していく
List<int> del = new List<int>();
for (int i = 1; i < dict.Count; i++) {
int h = (i - 1 + dict.Count) % dict.Count;
int j = (i + 1) % dict.Count;
double theta1, theta2;
theta1 = Math.Atan2(dict[i].Value.y - dict[h].Value.y, dict[i].Value.x - dict[h].Value.x);
if (theta1 < 0) theta1 = 2 * Math.PI + theta1;
theta2 = Math.Atan2(dict[j].Value.y - dict[i].Value.y, dict[j].Value.x - dict[i].Value.x);
if (theta2 <= 0) theta2 = 2 * Math.PI + theta2;
if (theta2 - theta1 < 0) {
dict.RemoveAt(i);
i--;
}
}
//戻り値用の配列に格納
Vec2D[] result = new Vec2D[dict.Count];
for (int i = 0; i < dict.Count; i++) {
result[i] = dict[i].Value;
}
return result;
}


