X、y、幅、高さ(ピクセル)、回転値(度)の2つの長方形がある場合、それらの輪郭の相互の最も近い距離を計算するにはどうすればよいですか?
背景:Luaで書かれたゲームでは、ランダムにマップを生成していますが、特定の長方形が互いに近すぎないようにしたいのですが、長方形が特定の近距離位置に入るとマップが解決できなくなるため、これが必要です。ボールはそれらの間を通過する必要があります。長方形があまりなく、マップはレベルごとに1回だけ生成されるため、速度は大きな問題ではありません。 StackOverflowで見つけた以前のリンクは this と this です
よろしくお願いします!
Luaにはありませんが、M Katzの提案に基づくPythonコード:
def rect_distance((x1, y1, x1b, y1b), (x2, y2, x2b, y2b)):
left = x2b < x1
right = x1b < x2
bottom = y2b < y1
top = y1b < y2
if top and left:
return dist((x1, y1b), (x2b, y2))
Elif left and bottom:
return dist((x1, y1), (x2b, y2b))
Elif bottom and right:
return dist((x1b, y1), (x2, y2b))
Elif right and top:
return dist((x1b, y1b), (x2, y2))
Elif left:
return x1 - x2b
Elif right:
return x2 - x1b
Elif bottom:
return y1 - y2b
Elif top:
return y2 - y1b
else: # rectangles intersect
return 0.
どこ
dist
は、点間のユークリッド距離です。(x1, y1)
および(x1b, y1b)
(x2, y2)
および(x2b, y2b)
編集:OKが指摘しているように、このソリューションはすべての長方形が直立していることを前提としています。 OPが要求するように回転した長方形で機能させるには、各長方形の角から他の長方形の最も近い辺までの距離も計算する必要があります。ただし、ポイントがラインセグメントの両方のエンドポイントの上または下にあり、両方のラインセグメントの左または右にある場合(電話の位置1、3、7、または9)、ほとんどの場合、この計算を回避できます。線分)。
Agniusの答えは、DistanceBetweenLineSegments()関数に依存しています。これは、以下を行わないケース分析です。
(1) Check if the rects intersect. If so, the distance between them is 0.
(2) If not, think of r2 as the center of a telephone key pad, #5.
(3) r1 may be fully in one of the extreme quadrants (#1, #3, #7, or #9). If so
the distance is the distance from one rect corner to another (e.g., if r1 is
in quadrant #1, the distance is the distance from the lower-right corner of
r1 to the upper-left corner of r2).
(4) Otherwise r1 is to the left, right, above, or below r2 and the distance is
the distance between the relevant sides (e.g., if r1 is above, the distance
is the distance between r1's low y and r2's high y).
擬似コード:
distance_between_rectangles = some_scary_big_number;
Rectangle1の各Edge1について:
Rectangle2の各Edge2について:
距離=最短を計算 Edge1とEdge2の間の距離
if(distance <distance_between_rectangles)
distance_between_rectangles =距離
実際には、高速な数学的解決策があります。
_Length(Max((0, 0), Abs(Center - otherCenter) - (Extent + otherExtent)))
_
ここで、Center = ((Maximum - Minimum) / 2) + Minimum
およびExtent = (Maximum - Minimum) / 2
。基本的に、ゼロの軸より上のコードはオーバーラップしているため、距離は常に正しいです。
多くの状況で望ましいので、長方形をこの形式のままにしておくことをお勧めします(つまり、回転がはるかに簡単です)。
これを解決するための多くのアルゴリズムがあり、Agniusアルゴリズムは正常に機能します。ただし、以下の方が直感的であり(1枚の紙で行うことができます)、線の間の最小距離を見つけることに依存せず、点と線の間の距離を見つけることに依存しているため、以下の方が好きです。
難しいのは、数学関数を実装して、線と点の間の距離を見つけ、点が線に面しているかどうかを見つけることです。ただし、単純な三角法でこれらすべてを解決できます。これを行うための方法論を以下に示します。
任意の角度のポリゴン(三角形、長方形、六角形など)の場合
これらのアルゴリズムは、形状の任意の2つのエッジが180度を超える角度を作成しない限り機能します。その理由は、何かが180度を超える場合、それは星のようにいくつかの角が内側で膨らんでいることを意味するからです。
エッジとポイントの間の最小距離
Area = 12⋅base⋅height
を使用して高さを計算します。base
はエッジの長さです。ポイントがエッジに面しているかどうかを確認します
前と同じように、エッジとポイントから三角形を作成します。 余弦定理 を使用すると、エッジ距離を知るだけですべての角度を見つけることができます。エッジからポイントまでの各角度が90度未満である限り、ポイントはエッジに面しています。
興味があれば、Python for all this here に実装しています。
Javaの場合はこれを確認してください。すべての長方形が平行であるという制約があり、交差するすべての長方形に対して0を返します。
public static double findClosest(Rectangle rec1, Rectangle rec2) {
double x1, x2, y1, y2;
double w, h;
if (rec1.x > rec2.x) {
x1 = rec2.x; w = rec2.width; x2 = rec1.x;
} else {
x1 = rec1.x; w = rec1.width; x2 = rec2.x;
}
if (rec1.y > rec2.y) {
y1 = rec2.y; h = rec2.height; y2 = rec1.y;
} else {
y1 = rec1.y; h = rec1.height; y2 = rec2.y;
}
double a = Math.max(0, x2 - x1 - w);
double b = Math.max(0, y2 - y1 - h);
return Math.sqrt(a*a+b*b);
}
この質問は、どのような距離に依存します。中心の距離、エッジの距離、または最も近いコーナーの距離が必要ですか?
私はあなたが最後のものを意味すると思います。 X値とY値が長方形の中心を示している場合は、このトリックを適用することで各コーナーを見つけることができます
//Pseudo code
Vector2 BottomLeftCorner = new Vector2(width / 2, heigth / 2);
BottomLeftCorner = BottomLeftCorner * Matrix.CreateRotation(MathHelper.ToRadians(degrees));
//If LUA has no built in Vector/Matrix calculus search for "rotate Vector" on the web.
//this helps: http://www.kirupa.com/forum/archive/index.php/t-12181.html
BottomLeftCorner += new Vector2(X, Y); //add the Origin so that we have to world position.
すべての長方形のすべての角に対してこれを行い、次にすべての角をループして距離を計算します(abs(v1-v2)のみ)。
これがお役に立てば幸いです