Cooooding!!

Unity(C#)を使ったゲーム開発関連Tipsなど

4つの頂点座標からRectを求める【C#】

概要

与えられた複数の頂点(Vector2)が矩形(Rect)になるかどうかを判定して処理を分岐させたかったので、与えられた頂点からRectを求める関数を実装してみました。

4頂点から矩形を求める

実装

Rectを求める関数の実装:

using System.Collections.Generic;
using UnityEngine;

public static class RectUtility
{
    /// <summary>
    /// 頂点が矩形を表すならtrueを返し、その情報をresultに格納する
    /// </summary>
    public static bool ToRect(
        out Rect result,
        IList<Vector2> vertices,
        float allowedError = 0      // 許容できる誤差
    )
    {
        result = default;
        if (vertices == null || vertices.Count != 4)
        {
            return false;
        }
        // 0番目の頂点から見て左 or 右にある頂点を特定
        var h = GetHorizontalVertexIndex(vertices, 0, allowedError);
        if (h <= 0) { return false; }
        // 0番目の頂点から見て上 or 下にある頂点を特定
        var v = GetVerticalVertexIndex(vertices, 0, allowedError);
        if (v <= 0) { return false; }
        // 0番目の頂点から見て斜めの位置にある頂点を特定
        var d = GetDiagonalVertexIndex(vertices, 0, allowedError);
        if (d <= 0) { return false; }

        // 位置関係に問題がないかを確認
        if (!(
            Equals(vertices[h].x, vertices[d].x, allowedError) &&
            Equals(vertices[v].y, vertices[d].y, allowedError)
        ))
        {
            return false;
        }
        // 位置やサイズを設定
        result = new Rect(
            Mathf.Min(vertices[h].x, vertices[0].x),
            Mathf.Min(vertices[v].y, vertices[0].y),
            Mathf.Abs(vertices[h].x - vertices[0].x),
            Mathf.Abs(vertices[v].y - vertices[0].y)
        );
        return true;
    }

    /// <summary>
    /// selfIndex番目の頂点から斜めの位置にある頂点のIndexを取得する
    /// </summary>
    private static int GetDiagonalVertexIndex(
        IList<Vector2> vertices,
        int selfIndex,
        float allowedError
    )
    {
        var self = vertices[selfIndex];
        for (int i = 0; i < vertices.Count; ++i)
        {
            if (i == selfIndex) { continue; }

            if (
                !Equals(vertices[i].x, self.x, allowedError) &&
                !Equals(vertices[i].y, self.y, allowedError)
            )
            {
                return i;
            }
        }
        return -1;
    }

    /// <summary>
    /// selfIndex番目の頂点から上 or 下の位置にある頂点のIndexを取得する
    /// </summary>
    private static int GetVerticalVertexIndex(
        IList<Vector2> vertices,
        int selfIndex,
        float allowedError
    )
    {
        var self = vertices[selfIndex];
        for (int i = 0; i < vertices.Count; ++i)
        {
            if (i == selfIndex) { continue; }

            if (Equals(vertices[i].x, self.x, allowedError))
            {
                return i;
            }
        }
        return -1;
    }

    /// <summary>
    /// selfIndex番目の頂点から左 or 右の位置にある頂点のIndexを取得する
    /// </summary>
    private static int GetHorizontalVertexIndex(
        IList<Vector2> vertices,
        int selfIndex,
        float allowedError
    )
    {
        var self = vertices[selfIndex];
        for (int i = 0; i < vertices.Count; ++i)
        {
            if (i == selfIndex) { continue; }

            if (Equals(vertices[i].y, self.y, allowedError))
            {
                return i;
            }
        }
        return -1;
    }

    /// <summary>
    /// 誤差を許容するfloatの比較
    /// </summary>
    private static bool Equals(float a, float b, float allowedError)
    {
        return Mathf.Abs(a - b) <= allowedError;
    }
}

使用例:

var list = new Vector2[]
{
    new Vector2(1, -1),
    new Vector2(1, 0),
    new Vector2(3, -1),
    new Vector2(3, 0)
};
if (RectUtility.ToRect(out Rect rect, list))
{
    Debug.Log(rect);
}

ToRectの第三引数を指定すれば誤差を許容することもできます。

出力結果:

(x:1.00, y:-1.00, width:2.00, height:1.00)

※ 回転した状態の矩形には対応していません

環境

  • Unity 2018.4.2f1
  • VisualStudio 2019