[JavaScript] 凸包のプログラムを書いてみる

JavaScript

凸包とは?

まずはじめに凸包について、凸包というのは「与えられた点をすべて包含する最小の凸多角形のこと」です。イメージとしては以下の画像ですね。

凸包の描画

こんなの何に使うの?って思うかもしれませんが、多角形と点の当たり判定(例えば、多角形ボタン UI とのタッチ判定)に使用出来たり、グラフィックスを扱うプログラムでは意外と応用範囲が広いです。今回は、この凸包を計算するプログラムを書いてみます。

凸包のアルゴリズム:ギフト包装法

凸包を求めるアルゴリズムの中で、最も直感的でわかりやすいのが、ギフト包装法(Gift wrapping algorithm)です。このアルゴリズムの考え方としては、

  1. 最も y 座標が小さく、その中で x も最も小さいものを求めて注目点とする
  2. 次の点を求めるために、注目点ほか全ての点との偏角を求めて、最も左にあるものを選ぶ
  3. 一周するまで繰り返す

というものです。具体的なソースコードをみていきましょう。

最も y 座標が小さく、その中で x も最も小さいものを求める

ここでは points という配列に、点情報(x, y) が入っているものとします。まずは、配列の先頭の点情報を注目点と仮に定め、あとはその点と他のすべての点を比較して、注目点を更新していきます。

let basep = points[0]; 
points.forEach(point => {
  if (basep.y > point.y || ((basep.y == point.y) && basep.x > point.x)) {
    basep = point;
  }
});

上記のようなコードでループが完了すれば、注目点が basep に格納されます。

次の点を求めるために、注目点ほか全ての点との偏角を求めて、最も左にあるものを選ぶ

最も左にある点を求めるには、外積の計算を行います。

チェック対象の点(A)と、現時点で最も左にある点(B)、比較対象の点(C)とした場合、ベクトル AB と ベクトル AC の外積を計算します。この計算結果が正なら直線 AB に対して点 C は左側にあると判定できます。

let next = function(points, checkp) {
  let nextp = points[0];
  for (let i = 1, max = points.length; i < max; ++i) {
    let point = points[i];
    if (checkp == nextp) {
      nextp = point;
    } else {
      let v = (checkp.x - nextp.x) * (checkp.y - point.y) - (checkp.x - point.x) * (checkp.y - nextp.y);
      let ab = (checkp.x - nextp.x) * (checkp.x - nextp.x) + (checkp.y - nextp.y) * (checkp.y - nextp.y);
      let ac = (checkp.x - point.x) * (checkp.x - point.x) + (checkp.y - point.y) * (checkp.y - point.y);
      if (v > 0 || (v == 0 && Math.abs(ac) > Math.abs(ab))) {
        nextp = point;
      }
    }
  }
  return nextp;
};

こちらのコードでは、すべての点情報が格納された配列 points と チェック対象の点 checkp を渡すと、points に含まれる点情報から、checkp の最も左側にある点情報を返す next 関数として定義しました。

一周するまで繰り返す

最後はすべての点に対して計算を繰り返し、点が基準点に戻ってきたら処理は完了です。

let result = [];
let lastp = basep;
do {
  result.push(lastp);
  lastp = next(points, lastp);
} while(basep != lastp);
result.push(basep);

こちらのコードでは、result 配列に凸包を構成する点情報が格納されていきます。

実行サンプル

クリックすると点が作成され、それらの点をすべて包含する最小の凸多角形を描画します。

ソースコード

今回書いたソースコードの全文は以下にあります。Unlicense として公開していますので、どなたでも自由にお使いいただけます。

コメント

タイトルとURLをコピーしました