ポイントが長方形の内側にあるかどうかを調べる


84

点が長方形の内側にあるかどうかを調べたいのですが。長方形は任意の方向に向けることができ、軸を揃える必要はありません。

私が考えることができた1つの方法は、長方形と点の座標を回転させて長方形の軸を揃え、次に点の座標が長方形の座標内にあるかどうかをテストすることでした。

上記の方法では、回転が必要であるため、浮動小数点演算が必要です。これを行う他の効率的な方法はありますか?


ポイントが回転した長方形の直交する境界ボックスにあるかどうかを簡単に確認でき、そうでない場合はすぐに失敗します。(はい、それは答えの半分にすぎません(うーん、コーナーポイントによって形成できる3つの直交ボックスがあります...そしてそれは遅いです(概念的なジオメトリが最初に行く)))。
msw 2010年

しかし、私は最初に右に回転する必要がありますか?
avd 2010年

1
長方形がどのように定義されているかを教えてくれるまで、答えにはあまり実用的な価値はありません。整数座標で作業している場合、アルゴリズムを選択する際には、形状を表すために使用される方法が非常に重要です。
AnT 2010年

回答:


81

長方形はどのように表されますか?3点?4つのポイント?ポイント、サイド、アングル?2点と1面?他に何かありますか?それを知らずに、あなたの質問に答えようとする試みは、純粋に学術的な価値しかありません。

いずれの場合も、多角形(長方形を含む)のテストは非常に簡単です。各エッジが反時計回りの方向を向いていると仮定して、ポリゴンの各エッジをチェックし、ポイントがエッジの左側(左側)にあるかどうかをテストします。-ハンドハーフプレーン)。すべてのエッジがテストに合格した場合、ポイントは内側にあります。少なくとも1つが失敗した場合、ポイントは外側にあります。

ポイント(xp, yp)がエッジの左側にあるかどうかをテストするには、(x1, y1) - (x2, y2)計算する必要があります

D = (x2 - x1) * (yp - y1) - (xp - x1) * (y2 - y1)

の場合D > 0、ポイントは左側にあります。の場合D < 0、ポイントは右側にあります。の場合D = 0、ポイントは線上にあります。


この回答の以前のバージョンは、左側のテストの見かけ上異なるバージョンを説明していました(以下を参照)。しかし、同じ値を計算していることは簡単にわかります。

...ポイント(xp, yp)がエッジの左側にあるかどうかをテストするには、エッジ(x1, y1) - (x2, y2)を含む線の線方程式を作成する必要があります。方程式は次のとおりです

A * x + B * y + C = 0

どこ

A = -(y2 - y1)
B = x2 - x1
C = -(A * x1 + B * y1)

今あなたがする必要があるのは計算することだけです

D = A * xp + B * yp + C

の場合D > 0、ポイントは左側にあります。の場合D < 0、ポイントは右側にあります。の場合D = 0、ポイントは線上にあります。

ただし、このテストも凸多角形に対して機能します。つまり、長方形には一般的すぎる可能性があります。長方形は、値の単純なテスト...たとえば、矩形で(または他の任意の平行四辺形で)可能かもしれないABテストを簡素化するために利用することができる(すなわち、平行)の縁を、対向するために同じ大きさであるが異なる符号を有します。


これは、数学者の座標セットにのみ当てはまります。PC画面とGPSの場合、軸の方向が異なり、正しい不等式のセットがあるかどうかを確認できません。または、そうではないと確信することもできます。私の答えはより良いです:-)。
Gangnus 2014

AndreyT @Gangnus、迅速な精度、方程式の符号がPに関連する凸形状のすべての点で同じであることを確認するだけで済みます。これにより、座標系や凸形状の方向を気にする必要がなくなります。定義済み;))
Jason Rogers

2
これには、処理を高速化するための拡張機能がいくつかあります。1.長方形の2つの反対側が平行であるため、それらのA、B係数は同じにすることができます。他の2辺は、これらに垂直な2ため、それらの係数A'とはB'で与えられるA'=BB'=-A。3.A xp + B yp両方のエッジを計算する意味がないため、それらを1つのテストに結合します。次に、長方形内にあるためのテストはになり(v_min < A xp + B yp < v_max) && ( w_min < B xp - A yp < w_max )ます。
Michael Anderson

@MichaelAndersonそして、何v_minですか?
匿名

v_minは、A x + B y長方形の内部にあるすべての値の最小値です(これは、コーナーで評価されたときの最小値です)。v_max対応する最大値です。w_?値は同じですが、ためBx - A y
マイケルアンダーソン

43

長方形が3つの点A、B、Cで表され、ABとBCが垂直であると仮定すると、ABとBC上のクエリ点Mの投影を確認するだけで済みます。

0 <= dot(AB,AM) <= dot(AB,AB) &&
0 <= dot(BC,BM) <= dot(BC,BC)

ABは、座標(Bx-Ax、By-Ay)を持つベクトルABであり、dot(U,V)ベクトルUとVの内積ですUx*Vx+Uy*Vy

更新します。これを説明する例を見てみましょう:A(5,0)B(0,2)C(1,5)およびD(6,3)。点の座標から、AB =(-5,2)、BC =(1,3)、dot(AB、AB)= 29、dot(BC、BC)= 10が得られます。

クエリポイントM(4,2)の場合、AM =(-1,2)、BM =(4,0)、dot(AB、AM)= 9、dot(BC、BM)= 4になります。Mは長方形の内側にあります。

クエリポイントP(6,1)の場合、AP =(1,1)、BP =(6、-1)、dot(AB、AP)=-3、dot(BC、BP)= 3になります。Pは、辺ABの投影がセグメントABの内側にないため、長方形の内側にはありません。


1
0,2-10,2-10,10-2,10は長方形ではありません。
Eric Bainville 2013

2
ポイントをプロットし、最初のコメントの正確さを検証することを検討してください。
Eric Bainville 2013

3
これがベストアンサーです!
tahlil 2017年

1
この答えが簡潔で、操作の数をここの他の良い答えとほぼ同じくらい低く保つだけでなく、非常に直感的で視覚化できるという利点もあるのが好きです。
匿名

22

私はエリックベインビルの答えから借りました:

0 <= dot(AB,AM) <= dot(AB,AB) && 0 <= dot(BC,BM) <= dot(BC,BC)

javascriptでは次のようになります。

function pointInRectangle(m, r) {
    var AB = vector(r.A, r.B);
    var AM = vector(r.A, m);
    var BC = vector(r.B, r.C);
    var BM = vector(r.B, m);
    var dotABAM = dot(AB, AM);
    var dotABAB = dot(AB, AB);
    var dotBCBM = dot(BC, BM);
    var dotBCBC = dot(BC, BC);
    return 0 <= dotABAM && dotABAM <= dotABAB && 0 <= dotBCBM && dotBCBM <= dotBCBC;
}

function vector(p1, p2) {
    return {
            x: (p2.x - p1.x),
            y: (p2.y - p1.y)
    };
}

function dot(u, v) {
    return u.x * v.x + u.y * v.y; 
}

例えば:

var r = {
    A: {x: 50, y: 0},
    B: {x: 0, y: 20},
    C: {x: 10, y: 50},
    D: {x: 60, y: 30}
};

var m = {x: 40, y: 20};

その後:

pointInRectangle(m, r); // returns true.

これが視覚的なテストとして出力を描画するためのコードペンです:) http://codepen.io/mattburns/pen/jrrprN

ここに画像の説明を入力してください


こんにちは@mattburns私はあなたの方法を使用して、それを私のテストプロジェクトに入れました:jsfiddle.net/caymanbruce/06wjp2sk/6しかし、私はそれを機能させることができませんでした。回転せずに元の長方形内のポイントをテストしている理由がわかりません。mouseoverプロジェクトでイベントを使用しているので、長方形の内側にあるはずのポイントの上にマウスを置くと、マウスの周りに黒い円のドットが表示され、長方形の外側には何も表示されません。私はそれを機能させるために助けが必要ですが、私はとても混乱しています。
newguy 2016

mouseovermousemove、ただのタイプミスである必要があります。
newguy 2016


原則として、あなたの方法は正しいですが、あなたの長方形はあなたの例の長方形ではありません。私はここであなたの改良版を作成しました。ここでは、元の数式と命名スキームに固執し、入力は実際には真の長方形です。
JohannesB

15
# Pseudo code
# Corners in ax,ay,bx,by,dx,dy
# Point in x, y

bax = bx - ax
bay = by - ay
dax = dx - ax
day = dy - ay

if ((x - ax) * bax + (y - ay) * bay < 0.0) return false
if ((x - bx) * bax + (y - by) * bay > 0.0) return false
if ((x - ax) * dax + (y - ay) * day < 0.0) return false
if ((x - dx) * dax + (y - dy) * day > 0.0) return false

return true

これを次のように読んでください:「点を長方形の3つの頂点に接続すると、それらのセグメントと辺の間の角度は鋭角になるはずです」
P Shved

3
このようなアプローチの問題は、理論的には機能することですが、実際には問題が発生する可能性があります。OPは、長方形がどのように表されるかについては述べていません。この回答は、3つの点- a、、bおよびで表されることを前提としていdます。理論的には3点が任意の長方形を表す有効な方法ですが、実際には、一般的な場合、インターガー座標で正確に行うことは不可能です。一般的な場合、平行四辺形になります。これは長方形に非常に近いですが、それでも長方形ではありません。
AnT 2010年

3
つまり、その形状の角度は正確に90度ではありません。このような状況で角度ベースのテストを行うときは、非常に注意する必要があります。繰り返しますが、それは、OPが不正確に表現された「長方形」の「内部」をどのように定義するかに依存します。そして、繰り返しますが、長方形がどのように表されるかについて。
AnT 2010年

両方のコメントに+1します。@avdだけが、これで十分かどうかを教えてくれます。
ジョナスElfström

私にとっては完璧に機能します...三角法と幾何学を頻繁に使用するので、一般的な問題を解決するための式を考え出す必要がないのは素晴らしいことです。ありがとう。
sq2 2012年

12

これは古いスレッドだと思いますが、純粋に数学的な観点からこれを見ることに興味がある人のために、数学スタック交換に関する優れたスレッドがここにあります。

/math/190111/how-to-check-if-a-point-is-inside-a-rectangle

編集:このスレッドに触発されて、私はあなたのポイントがどこにあるかをすばやく決定するための簡単なベクトルメソッドをまとめました。

p1 =(x1、y1)、p2 =(x2、y2)、p3 =(x3、y3)、およびp4 =(x4、y4)に点があり、時計回りに進む長方形があるとします。点p =(x、y)が長方形の内側にある場合、内積(p --p1)。(p2 --p1)は0と| p2 --p1 | ^ 2、および(p --p1)の間にあります。 (p4-p1)は0と| p4-p1 | ^ 2の間にあります。これは、p1を原点として、長方形の長さと幅に沿ってベクトルp --p1を射影するのと同じです。

同等のコードを表示すると、これはより理にかなっている可能性があります。

p21 = (x2 - x1, y2 - y1)
p41 = (x4 - x1, y4 - y1)

p21magnitude_squared = p21[0]^2 + p21[1]^2
p41magnitude_squared = p41[0]^2 + p41[1]^2

for x, y in list_of_points_to_test:

    p = (x - x1, y - y1)

    if 0 <= p[0] * p21[0] + p[1] * p21[1] <= p21magnitude_squared:
        if 0 <= p[0] * p41[0] + p[1] * p41[1]) <= p41magnitude_squared:
            return "Inside"
        else:
            return "Outside"
    else:
        return "Outside"

以上です。平行四辺形でも機能します。


ここまでの議論をまとめていただけますか?そうでなければ、これはおそらくコメントであり、答えではないはずです。もう少し担当者が増えると、コメントを投稿できるようになります
ネイサンタギー2015年

7
bool pointInRectangle(Point A, Point B, Point C, Point D, Point m ) {
    Point AB = vect2d(A, B);  float C1 = -1 * (AB.y*A.x + AB.x*A.y); float  D1 = (AB.y*m.x + AB.x*m.y) + C1;
    Point AD = vect2d(A, D);  float C2 = -1 * (AD.y*A.x + AD.x*A.y); float D2 = (AD.y*m.x + AD.x*m.y) + C2;
    Point BC = vect2d(B, C);  float C3 = -1 * (BC.y*B.x + BC.x*B.y); float D3 = (BC.y*m.x + BC.x*m.y) + C3;
    Point CD = vect2d(C, D);  float C4 = -1 * (CD.y*C.x + CD.x*C.y); float D4 = (CD.y*m.x + CD.x*m.y) + C4;
    return     0 >= D1 && 0 >= D4 && 0 <= D2 && 0 >= D3;}





Point vect2d(Point p1, Point p2) {
    Point temp;
    temp.x = (p2.x - p1.x);
    temp.y = -1 * (p2.y - p1.y);
    return temp;}

ポリゴン内のポイント

C ++を使用してAnTの回答を実装しました。このコードを使用して、ピクセルの調整(X、Y)がシェイプの内側にあるかどうかを確認しました。


ここで何をしているのかについての説明は本当に役に立ちます。
ブラッド

ただ感謝を言いたかった。Unity Shaderで作業する必要があるものを変換し、4ポイントではなく3ポイントに減らしました。うまくいきました。乾杯。
ダスティンジェンセン2017年

これは、ユニティDOTSのために作られたC#Iで実装され、ここで、私の仕事:gist.github.com/rgoupil/04b59be8ddb56c992f25e1489c61b310
JamesDev

6

長方形の問題を解決できない場合は、問題をより簡単な問題に分割してみてください。長方形を2つの三角形に分割し、ここで説明されているように、ポイントがそれらのいずれかの内側にあるかどうかを確認します

基本的に、ポイントから2組の線ごとにエッジを循環します。次に、外積を使用して、外積を使用してポイントが2つの線の間にあるかどうかを確認します。3つのポイントすべてについて検証された場合、そのポイントは三角形の内側にあります。この方法の良いところは、角度をチェックした場合に発生する浮動小数点エラーが発生しないことです。


それは正しいですが、ひどく効果のないアルゴリズムです。
Gangnus 2017

4

ポイントが長方形の内側にある場合。飛行機の中で。数学者または測地学(GPS)座標の場合

  • 長方形を頂点A、B、C、Dで設定します。点はPです。座標は長方形です:x、y。
  • 長方形の辺を長くしましょう。したがって、4本の直線l AB、l BC、l CD、l DA、または、短くするために、l 1、l 2、l 3、L 4
  • すべてのlの方程式を作成します私を。方程式の種類:

    f i(P)= 0。

Pはポイントです。ポイントの場合、Lのに属するIは、方程式は真です。

  • 方程式の左側にある関数が必要です。彼らはFであり1 F、2、F 3、F 4
  • l iの一方の側からのすべての点について、関数f iは0より大きいことに注意してください。もう一方の側からの点については、iが0より小さいです。
  • したがって、Pが長方形内にあることを確認する場合、pが4本の線すべての正しい側にある必要があるだけです。したがって、4つの関数の兆候を確認する必要があります。
  • しかし、長方形が属する線のどちら側が正しいのでしょうか?線に属さない長方形の頂点が存在する側です。チェックには、頂点に属していない2つの頂点のいずれかを選択できます。
  • したがって、これを確認する必要があります。

    f AB(P)f AB(C)> = 0

    f BC(P)f BC(D)> = 0

    f CD(P)f CD(A)> = 0

    f DA(P)f DA(B)> = 0

点が境界上にある場合、それも長方形に属するため、不等式は厳密ではありません。境界にポイントが必要ない場合は、厳密なものの不等式を変更できます。ただし、浮動小数点演算で作業している間は、選択は関係ありません。

  • 長方形内の点については、4つの不等式すべてが真です。すべての凸多角形でも機能することに注意してください。線/方程式の数だけが異なります。
  • 残っているのは、2点を通る直線の方程式を取得することだけです。これはよく知られている線形方程式です。線ABと点Pについて書きましょう。

    f AB(P)≡(x A -x B)(y P -y B)-(y A -y B)(x P -x B

チェックは単純化できます-長方形に沿って時計回りに進みましょう-A、B、C、D、A。そうすると、すべての正しい辺が線の右側になります。したがって、別の頂点がある側と比較する必要はありません。そして、より短い不等式のセットをチェックする必要があります。

f AB(P)> = 0

f BC(P)> = 0

f CD(P)> = 0

f DA(P)> = 0

しかし、これは通常の数学者(学校の数学から)の座標のセットには正しいです。ここで、Xは右に、Yは上にあります。また、GPSで使用されている測地座標では、Xが上、Yが右であるため、不等式を回転させる必要があります。

f AB(P)<= 0

f BC(P)<= 0

f CD(P)<= 0

f DA(P)<= 0

軸の方向がわからない場合は、この簡略化されたチェックに注意してください。正しい不等式を選択した場合は、既知の配置で1つのポイントをチェックしてください。


「Xが上に、Yが右にある場合、不等式を回す必要があります。」これは、「時計回り」をどのように決定するかによって異なります。座標を数学的な座標と見なすと、時計回りは自動的に反時計回りになり、最初のまったく同じ不等式のセットを引き続き使用できます。言い換えれば、座標をすべての面で数学的なものと見なすだけで、この混乱がなくてもうまくいく可能性があります。座標を逆にしたり交換したりしても、この述語には影響しません。
パロ

@PaloMathematics自体には方向性がありません。はい。しかし、アルゴリズムにはいくつかのポイントがあり、テストできるようにするには、その時点で理解可能で賢明な(実際の)結果を得る方がはるかに優れています。それがないと、2番目の文の終わりまで、空間の想像力を使用して、不等式を正しく解いているかどうかを確認することはほとんどできません。
Gangnus 2017

0

私が考えた最も簡単な方法は、点を長方形の軸に投影することでした。説明させてください:

長方形の中心から上端または下端、および左端または右端までのベクトルを取得できる場合。また、長方形の中心からポイントまでのベクトルがあり、そのポイントを幅と高さのベクトルに投影できます。

P =点ベクトル、H =高さベクトル、W =幅ベクトル

ベクトルを大きさで割って単位ベクトルW '、H'を取得します

proj_P、H = P-(P.H ')H' proj_P、W = P-(P.W ')W'

私が間違っているとは思わないが、私が間違っているとは思わないが、高さベクトルへの点の投影の大きさが高さベクトルの大きさよりも小さい場合(長方形の高さの半分)と幅ベクトルへの点の投影の大きさはであり、長方形の内側に点があります。

ユニバーサル座標系を使用している場合は、ベクトル減算を使用して高さ/幅/点のベクトルを計算する必要がある場合があります。ベクトルの成分分解は素晴らしいです!それを覚えておいてください。


0

続けてマットが答えます。それを機能させるには、https://math.stackexchange.com/questions/190111/how-to-check-if-a-point-is-inside-a-rectangle/190373#190373 ソリューションを使用する必要があり ます

以下は機能しません
0 <= dot(AB、AM)<= dot(AB、AB)&& 0 <= dot(BC、BM)<= dot(BC、BC)

以下の作品
0 <= dot(AB、AM)<= dot(AB、AB)&& 0 <= dot(AM、AC)<= dot(AC、AC)

以下をjavascriptコンソールに貼り付けて確認します// Javascriptソリューションと同じ

            var screenWidth = 320;
            var screenHeight = 568;
            var appHeaderWidth = 320;
            var AppHeaderHeight = 65;
            var regionWidth = 200;
            var regionHeight = 200;

            this.topLeftBoundary = {
                A: {x: 0, y: AppHeaderHeight},
                B: {x: regionWidth, y: AppHeaderHeight},
                C: {x: 0, y: regionHeight + AppHeaderHeight},
                D: {x: regionWidth, y: regionHeight + AppHeaderHeight}
            }

            this.topRightBoundary = {
                A: {x: screenWidth, y: AppHeaderHeight},
                B: {x: screenWidth - regionWidth, y: AppHeaderHeight},
                C: {x: screenWidth, y: regionHeight + AppHeaderHeight},
                D: {x: screenWidth - regionWidth, y: regionHeight + AppHeaderHeight}
            }

            this.bottomRightBoundary = {
                A: {x: screenWidth, y: screenHeight},
                B: {x: screenWidth - regionWidth, y: screenHeight},
                C: {x: screenWidth, y: screenHeight - regionHeight},
                D: {x: screenWidth - regionWidth, y: screenHeight - regionHeight}
            }

            this.bottomLeftBoundary = {
                A: {x: 0, y: screenHeight},
                B: {x: regionWidth, y: screenHeight},
                C: {x: 0, y: screenHeight - regionHeight},
                D: {x: regionWidth, y: screenHeight - regionHeight}
            }
            console.log(this.topLeftBoundary);
            console.log(this.topRightBoundary);
            console.log(this.bottomRightBoundary);
            console.log(this.bottomLeftBoundary);

            checkIfTapFallsInBoundary = function (region, point) {
                console.log("region " + JSON.stringify(region));
                console.log("point" + JSON.stringify(point));

                var r = region;
                var m = point;

                function vector(p1, p2) {
                    return {
                        x: (p2.x - p1.x),
                        y: (p2.y - p1.y)
                    };
                }

                function dot(u, v) {
                    console.log("DOT " + (u.x * v.x + u.y * v.y));
                    return u.x * v.x + u.y * v.y;
                }

                function pointInRectangle(m, r) {
                    var AB = vector(r.A, r.B);
                    var AM = vector(r.A, m);
                    var AC = vector(r.A, r.C);
                    var BC = vector(r.B, r.C);
                    var BM = vector(r.B, m);

                    console.log("AB " + JSON.stringify(AB));
                    console.log("AM " + JSON.stringify(AM));
                    console.log("AM " + JSON.stringify(AC));
                    console.log("BC " + JSON.stringify(BC));
                    console.log("BM " + JSON.stringify(BM));

                    var dotABAM = dot(AB, AM);
                    var dotABAB = dot(AB, AB);
                    var dotBCBM = dot(BC, BM);
                    var dotBCBC = dot(BC, BC);
                    var dotAMAC = dot(AM, AC);
                    var dotACAC = dot(AC, AC);

                    console.log("ABAM " + JSON.stringify(dotABAM));
                    console.log("ABAB " + JSON.stringify(dotABAB));
                    console.log("BCBM " + JSON.stringify(dotBCBM));
                    console.log("BCBC " + JSON.stringify(dotBCBC));
                    console.log("AMAC " + JSON.stringify(dotAMAC));
                    console.log("ACAC" + JSON.stringify(dotACAC));

                    var check = ((0 <= dotABAM && dotABAM <= dotABAB) && (0 <= dotBCBM && dotBCBM <= dotBCBC));
                    console.log(" first check" + check);
                    var check = ((0 <= dotABAM && dotABAM <= dotABAB) && (0 <= dotAMAC && dotAMAC <= dotACAC));
                    console.log("second check" + check);
                    return check;
                }

                return pointInRectangle(m, r);
            }

        //var point = {x: 136, y: 342};

            checkIfTapFallsInBoundary(topLeftBoundary, {x: 136, y: 342});
            checkIfTapFallsInBoundary(topRightBoundary, {x: 136, y: 274});
            checkIfTapFallsInBoundary(bottomRightBoundary, {x: 141, y: 475});
            checkIfTapFallsInBoundary(bottomRightBoundary, {x: 131, y: 272});
            checkIfTapFallsInBoundary(bottomLeftBoundary, {x: 131, y: 272});
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.