ポイントがあります。それらを2つの異なるセットに分けたいと思います。これを行うには、2つのポイント(aとb)を選択し、それらの間に想像上の線を引きます。ここで、1つのセットでこのラインから離れているすべてのポイントと、もう1つのセットでこのラインから離れているすべてのポイントが必要です。
特定のポイントzが左にあるか右にあるかをどのように判断できますか? azbの間の角度を計算しようとしました。180より小さい角度は右側にあり、180より大きい角度は左側にありますが、ArcCosの定義により、計算された角度は常に小さくなります180°より。 180°を超える角度を計算する式(または右側または左側を選択する他の式)はありますか?
ベクトルの行列式の符号(AB,AM)
を使用します。ここで、M(X,Y)
はクエリポイントです。
position = sign((Bx - Ax) * (Y - Ay) - (By - Ay) * (X - Ax))
行の0
と、一方の+1
、もう一方の-1
です。
クロス積 を利用するこのコードを試してください:
public bool isLeft(Point a, Point b, Point c){
return ((b.X - a.X)*(c.Y - a.Y) - (b.Y - a.Y)*(c.X - a.X)) > 0;
}
ここで、a=ラインポイント1; b=ラインポイント2; c=確認するポイント。
式が0に等しい場合、ポイントは同一直線上にあります。
線が水平の場合、ポイントが線の上にあれば、これはtrueを返します。
の決定因子のサインを見る
| x2-x1 x3-x1 |
| y2-y1 y3-y1 |
片側のポイントでは正で、もう一方の側ではマイナスになります(線自体のポイントではゼロになります)。
ベクトル(y1 - y2, x2 - x1)
は線に垂直であり、常に右を指します(飛行機の向きが私の方向と異なる場合は常に左を指します)。
次に、そのベクトルと(x3 - x1, y3 - y1)
のドット積を計算して、ポイントがラインの垂直ベクトルと同じ側にあるかどうかを判断できます(ドット積> 0
)。
これをJavaに実装し、単体テストを実行しました(以下のソース)。上記のソリューションはどれも機能しません。このコードは単体テストに合格します。誰かがパスしない単体テストを見つけたら、私に知らせてください。
コード:注:2つの数値が非常に近い場合、nearlyEqual(double,double)
はtrueを返します。
/*
* @return integer code for which side of the line ab c is on. 1 means
* left turn, -1 means right turn. Returns
* 0 if all three are on a line
*/
public static int findSide(
double ax, double ay,
double bx, double by,
double cx, double cy) {
if (nearlyEqual(bx-ax,0)) { // vertical line
if (cx < bx) {
return by > ay ? 1 : -1;
}
if (cx > bx) {
return by > ay ? -1 : 1;
}
return 0;
}
if (nearlyEqual(by-ay,0)) { // horizontal line
if (cy < by) {
return bx > ax ? -1 : 1;
}
if (cy > by) {
return bx > ax ? 1 : -1;
}
return 0;
}
double slope = (by - ay) / (bx - ax);
double yIntercept = ay - ax * slope;
double cSolution = (slope*cx) + yIntercept;
if (slope != 0) {
if (cy > cSolution) {
return bx > ax ? 1 : -1;
}
if (cy < cSolution) {
return bx > ax ? -1 : 1;
}
return 0;
}
return 0;
}
単体テストは次のとおりです。
@Test public void testFindSide() {
assertTrue("1", 1 == Utility.findSide(1, 0, 0, 0, -1, -1));
assertTrue("1.1", 1 == Utility.findSide(25, 0, 0, 0, -1, -14));
assertTrue("1.2", 1 == Utility.findSide(25, 20, 0, 20, -1, 6));
assertTrue("1.3", 1 == Utility.findSide(24, 20, -1, 20, -2, 6));
assertTrue("-1", -1 == Utility.findSide(1, 0, 0, 0, 1, 1));
assertTrue("-1.1", -1 == Utility.findSide(12, 0, 0, 0, 2, 1));
assertTrue("-1.2", -1 == Utility.findSide(-25, 0, 0, 0, -1, -14));
assertTrue("-1.3", -1 == Utility.findSide(1, 0.5, 0, 0, 1, 1));
assertTrue("2.1", -1 == Utility.findSide(0,5, 1,10, 10,20));
assertTrue("2.2", 1 == Utility.findSide(0,9.1, 1,10, 10,20));
assertTrue("2.3", -1 == Utility.findSide(0,5, 1,10, 20,10));
assertTrue("2.4", -1 == Utility.findSide(0,9.1, 1,10, 20,10));
assertTrue("vertical 1", 1 == Utility.findSide(1,1, 1,10, 0,0));
assertTrue("vertical 2", -1 == Utility.findSide(1,10, 1,1, 0,0));
assertTrue("vertical 3", -1 == Utility.findSide(1,1, 1,10, 5,0));
assertTrue("vertical 3", 1 == Utility.findSide(1,10, 1,1, 5,0));
assertTrue("horizontal 1", 1 == Utility.findSide(1,-1, 10,-1, 0,0));
assertTrue("horizontal 2", -1 == Utility.findSide(10,-1, 1,-1, 0,0));
assertTrue("horizontal 3", -1 == Utility.findSide(1,-1, 10,-1, 0,-9));
assertTrue("horizontal 4", 1 == Utility.findSide(10,-1, 1,-1, 0,-9));
assertTrue("positive slope 1", 1 == Utility.findSide(0,0, 10,10, 1,2));
assertTrue("positive slope 2", -1 == Utility.findSide(10,10, 0,0, 1,2));
assertTrue("positive slope 3", -1 == Utility.findSide(0,0, 10,10, 1,0));
assertTrue("positive slope 4", 1 == Utility.findSide(10,10, 0,0, 1,0));
assertTrue("negative slope 1", -1 == Utility.findSide(0,0, -10,10, 1,2));
assertTrue("negative slope 2", -1 == Utility.findSide(0,0, -10,10, 1,2));
assertTrue("negative slope 3", 1 == Utility.findSide(0,0, -10,10, -1,-2));
assertTrue("negative slope 4", -1 == Utility.findSide(-10,10, 0,0, -1,-2));
assertTrue("0", 0 == Utility.findSide(1, 0, 0, 0, -1, 0));
assertTrue("1", 0 == Utility.findSide(0,0, 0, 0, 0, 0));
assertTrue("2", 0 == Utility.findSide(0,0, 0,1, 0,2));
assertTrue("3", 0 == Utility.findSide(0,0, 2,0, 1,0));
assertTrue("4", 0 == Utility.findSide(1, -2, 0, 0, -1, 2));
}
最初に垂直線があるかどうかを確認します。
if (x2-x1) == 0
if x3 < x2
it's on the left
if x3 > x2
it's on the right
else
it's on the line
次に、勾配を計算します:m = (y2-y1)/(x2-x1)
次に、ポイントスロープ形式y - y1 = m*(x-x1) + y1
を使用して線の方程式を作成します。説明のために、それを勾配インターセプト形式(アルゴリズムでは不要)に簡略化します:y = mx+b
。
x
およびy
に(x3, y3)
を接続します。何が起こるかを詳細に説明する擬似コードを次に示します。
if m > 0
if y3 > m*x3 + b
it's on the left
else if y3 < m*x3 + b
it's on the right
else
it's on the line
else if m < 0
if y3 < m*x3 + b
it's on the left
if y3 > m*x3+b
it's on the right
else
it's on the line
else
horizontal line; up to you what you do
ラインの等式abを使用して、ポイントと同じy座標でライン上のx座標を取得しますソートされます。
基本的に、どのポリゴンでも、4つの頂点(p1、p2、p3、p4)で構成され、ポリゴンの反対側にある2つの頂点を見つけることができます。言葉、例えば、最も左上の頂点(p1と言う)と、最も右下に位置する反対の頂点(と言う)を見つけます。したがって、テストポイントC(x、y)を指定すると、Cとp1およびCとp4の間で二重チェックを行う必要があります。
if cx> p1x AND cy> p1y ==>は、Cが次にp1の右下にあることを意味しますcc <p2x AND cy <p2y ==>は、Cがp4の左上にあることを意味します
結論として、Cは長方形の中にあります。
ありがとう:)
Rubyでの@AVBの回答
det = Matrix[
[(x2 - x1), (x3 - x1)],
[(y2 - y1), (y3 - y1)]
].determinant
det
が正の場合は上、負の場合は下です。 0の場合、その行にあります。
こちらもClojureで書かれたクロス積ロジックを使用したバージョンです。
(defn is-left? [line point]
(let [[[x1 y1] [x2 y2]] (sort line)
[x-pt y-pt] point]
(> (* (- x2 x1) (- y-pt y1)) (* (- y2 y1) (- x-pt x1)))))
使用例:
(is-left? [[-3 -1] [3 1]] [0 10])
true
つまり、ポイント(0、10)は、(-3、-1)と(3、1)によって決定される線の左側にあります。
注:この実装は、(これまでのところ)他のどれも解決しない問題を解決します! 順序が重要ラインを決定するポイントを与えるとき。つまり、ある意味では「有向線」です。したがって、上記のコードでは、この呼び出しはtrue
の結果も生成します。
(is-left? [[3 1] [-3 -1]] [0 10])
true
これは、次のコードスニペットが原因です。
(sort line)
最後に、他のクロス積ベースのソリューションと同様に、このソリューションはブール値を返し、共線性の3番目の結果を返しません。しかし、それは理にかなっている結果を与えます、例えば:
(is-left? [[1 1] [3 1]] [10 1])
false
ポイントが(Ax、Ay)(Bx、By)および(Cx、Cy)であると仮定すると、計算する必要があります:
(Bx-Ax)*(Cy-Ay)-(By-Ay)*(Cx-Ax)
これは、ポイントCがポイントAとBによって形成される線上にある場合はゼロに等しく、側面に応じて異なる符号を持ちます。どちらの側が(x、y)座標の向きに依存しますが、A、B、Cのテスト値をこの式にプラグインして、負の値が左か右かを判断できます。
物理学に触発されたソリューションを提供したかった。
線に沿って加えられる力を想像してください。あなたは点の周りの力のトルクを測定しています。トルクが正(反時計回り)の場合、ポイントは線の「左」にありますが、トルクが負の場合、ポイントは線の「右」になります。
そのため、力ベクトルが線を定義する2点のスパンに等しい場合
fx = x_2 - x_1
fy = y_2 - y_1
次のテストの符号に基づいて、ポイントの側面(px,py)
をテストします
var torque = fx*(py-y_1)-fy*(px-x_1)
if torque>0 then
"point on left side"
else if torque <0 then
"point on right side"
else
"point on line"
end if
Nettersが提供するソリューションの感触を取得する別の方法は、ジオメトリの影響を少し理解することです。
pqr= [P、Q、R]が、線[P、R]によって2つの側面に分割される平面を形成する点であるとします。 pqr plane、A、Bの2点が同じ側にあるかどうかを調べます。
Pqr平面上の任意の点Tは、2つのベクトルで表すことができます:v = P-Qおよび = R-Q、次のように:
T '= T-Q = i * v + j * u
ジオメトリの意味:
i+j: <0 0 <1 =1 >1 ---------Q------[PR]--------- <== this is PQR plane ^ pr line
一般に、
iおよびjの他のジオメトリの重要性は次のとおりです(このソリューションとは関係ありません)。
i、jの値は、方程式を解くことで取得できます。
i*vx + j*ux = T'x
i*vy + j*uy = T'y
i*vz + j*uz = T'z
したがって、平面上のA、Bの2点が与えられます。
A = a1 * v + a2 * u B = b1 * v + b2 * u
A、Bが同じ側にある場合、これは真になります。
sign(a1+a2-1) = sign(b1+b2-1)
これは質問にも適用されることに注意してください:A、Bは平面[P、Q、R]の同じ側にあります、ここで:
T = i * P + j * Q + k * R
i + j + k = 1はTが平面[P、Q、R]上にあることを意味し、i + j + k-1の符号はその側面を意味します。これから、次のものが得られます。
A = a1 * P + a2 * Q + a3 * R B = b1 * P + b2 * Q + b3 * R
およびA、Bは、平面[P、Q、R]の同じ側にあります。
sign(a1+a2+a3-1) = sign(b1+b2+b3-1)