[解決済み] 時計回りに並べると?
質問
x,y 点の配列がある場合、この配列の点を時計回りに(全体の平均中心点を中心に)並べ替えるにはどうすればよいでしょうか。私の目標は、線を作成する関数にポイントを渡して、最終的に線が交差しないできるだけ凸の、むしろ "solid" のように見えるものを作成することです。
参考までに、私はLuaを使っていますが、どんな疑似コードでもありがたいです。
更新してください。 参考までに、これはCiamejの素晴らしい回答に基づいたLuaのコードです(私の"app"の接頭辞は無視してください)。
function appSortPointsClockwise(points)
local centerPoint = appGetCenterPointOfPoints(points)
app.pointsCenterPoint = centerPoint
table.sort(points, appGetIsLess)
return points
end
function appGetIsLess(a, b)
local center = app.pointsCenterPoint
if a.x >= 0 and b.x < 0 then return true
elseif a.x == 0 and b.x == 0 then return a.y > b.y
end
local det = (a.x - center.x) * (b.y - center.y) - (b.x - center.x) * (a.y - center.y)
if det < 0 then return true
elseif det > 0 then return false
end
local d1 = (a.x - center.x) * (a.x - center.x) + (a.y - center.y) * (a.y - center.y)
local d2 = (b.x - center.x) * (b.x - center.x) + (b.y - center.y) * (b.y - center.y)
return d1 > d2
end
function appGetCenterPointOfPoints(points)
local pointsSum = {x = 0, y = 0}
for i = 1, #points do pointsSum.x = pointsSum.x + points[i].x; pointsSum.y = pointsSum.y + points[i].y end
return {x = pointsSum.x / #points, y = pointsSum.y / #points}
end
解決方法は?
まず、中心点を計算します。 次に、好きな並べ替えアルゴリズムを使って点を並べ替えます。ただし、ある点が他の点より小さいかどうかを判断するために、特別な比較ルーチンを使用します。
このように簡単な計算で、ある点(a)が中心に対して他の点(b)の左側にあるか右側にあるかを確認することができるのです。
det = (a.x - center.x) * (b.y - center.y) - (b.x - center.x) * (a.y - center.y)
の結果が0なら中心から同じ線上にあり、正または負ならどちらか一方にあるので、一方の点が他方に先行することになります。 これを使うと、点を比較するための小なり関係を構築し、ソートされた配列に現れるべき順序を決定することができます。しかし、その順序の始まりはどこなのか、つまりどの角度から始まるのか(例えばX軸の正の半分)を定義する必要があります。
比較関数のコードは次のようになります。
bool less(point a, point b)
{
if (a.x - center.x >= 0 && b.x - center.x < 0)
return true;
if (a.x - center.x < 0 && b.x - center.x >= 0)
return false;
if (a.x - center.x == 0 && b.x - center.x == 0) {
if (a.y - center.y >= 0 || b.y - center.y >= 0)
return a.y > b.y;
return b.y > a.y;
}
// compute the cross product of vectors (center -> a) x (center -> b)
int det = (a.x - center.x) * (b.y - center.y) - (b.x - center.x) * (a.y - center.y);
if (det < 0)
return true;
if (det > 0)
return false;
// points a and b are on the same line from the center
// check which point is closer to the center
int d1 = (a.x - center.x) * (a.x - center.x) + (a.y - center.y) * (a.y - center.y);
int d2 = (b.x - center.x) * (b.x - center.x) + (b.y - center.y) * (b.y - center.y);
return d1 > d2;
}
12時を起点に時計回りにポイントを並べます。同じ時間帯のポイントは、中心から遠いものから順番に並びます。
整数型(Luaにはあまり存在しない)を使用する場合、変数det、d1、d2が計算結果を保持できる型であることを確認する必要があります。
見た目がしっかりしたもの、できるだけ凸のものを実現したいのであれば、やはり 凸型船体 . を使用して計算することができます。 グラハムスキャン . このアルゴリズムでは、特別なピボットポイントを起点として、時計回り(または反時計回り)に点を並べ替える必要があります。そして、凸包に新しい点を追加するために左回りか右回りかをチェックするたびに、単純なループステップを繰り返します。このチェックは、上記の比較関数と同様に、クロスプロダクトに基づいて行われます。
編集する
ifステートメントを1つ追加しました。
if (a.y - center.y >= 0 || b.y - center.y >=0)
x=0 と負の y を持つ点が、中心から遠いものからソートされることを確認するためです。もし、同じ「時間」上の点の順序を気にしないのであれば、この if 文を省略して、常に
a.y > b.y
.
最初のif文を修正し
-center.x
と
-center.y
.
2つ目のif文の追加
(a.x - center.x < 0 && b.x - center.x >= 0)
. それがないのは明らかな見落としでした。いくつかのチェックは冗長なので、今すぐif文を再編成することができます。例えば、最初のif文の最初の条件がfalseであれば、2番目のifの最初の条件はtrueでなければならない。しかし、私はコードを単純化するために、そのままにしておくことにした。どうせコンパイラが最適化して同じ結果を出す可能性は十分にある。
関連
-
[解決済み] 2つの緯度経度点間の距離を計算する?(ハバーシンの公式)
-
[解決済み] 32ビット整数のセットビットの数を数えるには?
-
[解決済み] ヒープの構築はどうして時間計算量O(n)になるのですか?
-
[解決済み] なぜクイックソートはマージソートより優れているのですか?
-
[解決済み】ポリゴンの点のリストが時計回りに並んでいるかどうかを判断する方法は?
-
[解決済み】このゲームの数学的/計算原理は何ですか?
-
[解決済み] 任意の二分木における2つのノードの最小公倍数の先祖を見つけるには?
-
[解決済み] 円形のデータの集合の平均はどのように計算するのですか?[クローズド]
-
[解決済み] 行または列に 0 が含まれる場合、行列のすべてのセルに 0 を設定します。
-
[解決済み] バックトラックと深さ優先探索の違いは何ですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] マージソートの時間および空間の複雑さ
-
[解決済み] 任意の二分木における2つのノードの最小公倍数の先祖を見つけるには?
-
[解決済み] Diff Algorithm? [クローズド]
-
[解決済み] 時計回りに並べると?
-
[解決済み] 3つ以上の数値の最小公倍数
-
[解決済み] 行または列に 0 が含まれる場合、行列のすべてのセルに 0 を設定します。
-
[解決済み] 2つのキューを使用したスタックの実装
-
[解決済み] 2つの矩形の交差を検出するアルゴリズム?
-
[解決済み] 短い文字列のための効率的な圧縮アルゴリズム[closed]。
-
[解決済み] ある数字が回文であるかどうかを調べるには?