【PHP実践|実務向け】PHPで実現する多角形の幾何計算:重心と面積の算出

導入

Web開発において、地図データやグラフィカルなレポート生成、あるいはゲームロジックの実装などで、多角形の情報を扱う機会があります。特に「多角形の重心」や「面積」を算出するニーズは、特定領域のシステム開発で頻出します。今回は、PHPを用いて任意の多角形の重心と面積を計算する方法を解説します。

基礎知識

多角形の重心(Geometric Centroid)とは、その図形の「釣り合いの取れる点」を指します。計算には「靴紐公式(Shoelace formula)」を応用したアルゴリズムを用いるのが一般的です。
この計算の前提として、頂点の座標が「時計回り」または「反時計回り」の順序で配列に格納されている必要があります。頂点が交差するような複雑な図形ではなく、単純な凸多角形や凹多角形を想定した実装が実務では基本となります。

実装/解決策

多角形の面積と重心を求めるには、各辺を積分的に扱うことで算出可能です。実務的なアプローチとしては、頂点配列をループ処理し、隣り合う頂点間の計算値を累積していく手法をとります。
注意点として、座標系が数学的な直交座標系(上がプラス)なのか、Webの画面座標系(下がプラス)なのかによって、計算結果の符号が反転する場合があります。必要に応じて最後に調整を行ってください。

サンプルプログラム

以下のコードは、任意の多角形頂点から面積と重心を算出する実用的なスニペットです。

  • 多角形の重心と面積を計算する関数
  • @param array $vertexes 頂点の配列 [[x1, y1], [x2, y2], …]
  • @return array [‘area’ => 面積, ‘gx’ => 重心x, ‘gy’ => 重心y]
  • /
    function calculatePolygonProperties(array $vertexes) {
    $n = count($vertexes);
    $area = 0;
    $gx = 0;
    $gy = 0;

    for ($i = 0; $i < $n; $i++) { $pt1 = $vertexes[$i]; $pt2 = ($i + 1 >= $n) ? $vertexes[0] : $vertexes[$i + 1];

    // 外積を用いた面積計算の要素
    $crossProduct = ($pt1[0] $pt2[1]) – ($pt2[0] $pt1[1]);
    $area += $crossProduct;

    // 重心計算の各要素
    $gx += ($pt1[0] + $pt2[0]) $crossProduct;
    $gy += ($pt1[1] + $pt2[1]) $crossProduct;
    }

    $area = $area / 2;
    // 面積が0に近い場合は計算不能とする
    if (abs($area) < 0.0000001) { return ['area' => 0, ‘gx’ => 0, ‘gy’ => 0];
    }

    return [
    ‘area’ => abs($area),
    ‘gx’ => $gx / (3 $area),
    ‘gy’ => $gy / (3 $area)
    ];
    }

    // 使用例
    $points = [[0, 0], [10, 80], [100, 100], [120, 30]];
    $result = calculatePolygonProperties($points);

    echo “面積: ” . $result[‘area’] . PHP_EOL;
    echo “重心: (” . $result[‘gx’] . “, ” . $result[‘gy’] . “)” . PHP_EOL;
    ?>

    応用・注意点

    1. 頂点の順序
    頂点は必ず連続した順序で定義してください。順序がバラバラだと、意図しない図形として認識され、計算結果が不正になります。
    2. 浮動小数点誤差
    PHPの数値計算では、極めて小さな値の加算で浮動小数点誤差が発生することがあります。精度の高い計算が必要な場合は、bcmath拡張などを使用して文字列として計算を行うことを検討してください。
    3. 閉じた図形
    サンプルコードでは最後の頂点と最初の頂点を自動的に結ぶ処理を入れていますが、データソース側で重複する頂点が含まれている場合は、計算ロジックが二重カウントにならないよう注意が必要です。

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