我有一組點。我想將它們分成2個不同的集合。爲此,我選擇兩個點(a和b)並在它們之間繪製一條虛線。現在我想把這一行中的所有點都放在一個集合中,並且這些行中的所有點都放在另一個集合中。如何判斷一個點是否在一條線的右側或左側
我該如何判斷任何給定的點z無論是在左邊還是在右邊?我試圖計算a-z-b –之間的角度,右側的角度小於180°,左側的角度大於180°–但由於ArcCos的定義,計算的角度始終小於180°。是否有計算角度大於180°的公式(或任何其他公式可選擇右側或左側)?
我有一組點。我想將它們分成2個不同的集合。爲此,我選擇兩個點(a和b)並在它們之間繪製一條虛線。現在我想把這一行中的所有點都放在一個集合中,並且這些行中的所有點都放在另一個集合中。如何判斷一個點是否在一條線的右側或左側
我該如何判斷任何給定的點z無論是在左邊還是在右邊?我試圖計算a-z-b –之間的角度,右側的角度小於180°,左側的角度大於180°–但由於ArcCos的定義,計算的角度始終小於180°。是否有計算角度大於180°的公式(或任何其他公式可選擇右側或左側)?
使用矢量(AB,AM)
的行列式,其中M(X,Y)
是查詢點的符號:
position = sign((Bx - Ax) * (Y - Ay) - (By - Ay) * (X - Ax))
它是0
就行,和+1
在一側,-1
在另一側。
+1不錯,有一件事要注意:舍入錯誤可能是一個問題,當點很接近線。 *大多數情況下不會造成問題,但它會不時地咬人。 – 2009-10-13 14:18:53
太棒了!這是一個很好的,沒有竇,arccos等:) 舍入誤差在這裏不是一個問題,因爲我使用算法中的分離來計算點集的凸包並在它們周圍繪製折線。如果一個點位於船體外面,這不是什麼大問題。 – Aaginor 2009-10-13 14:53:37
如果您發現自己處於這個測試的舍入錯誤導致您遇到問題的情況,您將需要查看Jon Shewchuk的「用於計算幾何的快速魯棒謂詞」。 – 2009-10-13 15:13:45
使用equation of the lineab,獲取與要排序的點在同一個y座標線上的x座標。
這是錯誤的,因爲正如你可以從Aaginor對第一個答案的評論中看到的那樣,我們不想知道該點是在DIRECTED線AB的左側還是右側,即如果你站在A上,朝着B方向看,是在你的左邊還是在右邊? – dionyziz 2011-09-04 12:18:43
@dionyziz - 咦?我的回答並沒有通過AB給線路分配「方向」。我的答案假設「左」是corrdinate系統的-x方向。接受的答案選擇定義一個*向量* AB,並使用交叉乘積定義左邊。原始問題沒有說明「左」是什麼意思。 – mbeckish 2011-09-06 19:29:46
注意:如果您使用這種方法(而不是交叉產品被批准爲答案),請注意在線接近水平線時存在缺陷。數學錯誤增加,並且如果完全水平,則命中無窮。解決方案是使用兩個點之間具有較大三角形的軸。 (或者也許更小的三角洲..這是我的頭頂。) – ToolmakerSteve 2013-07-10 05:20:33
你看看
| x2-x1 x3-x1 |
| y2-y1 y3-y1 |
行列式這將利好點一側的(上線本身分零)的標誌,而在其他陰性。
矢量(y1 - y2, x2 - x1)
與線垂直,並且始終指向右側(或者如果平面方向與我的平面方向不同,則總是指向左側)。
然後,您可以計算該向量的點積和(x3 - x1, y3 - y1)
,以確定該點是否與垂直向量(點積>0
)位於該線的同一側。
嘗試這個代碼,其利用一個cross product的:
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;
}
凡一個 =行點1; b =第2行; c =指向檢查。
如果公式等於0,則這些點是共線的。
如果該行是水平的,那麼如果該點位於該行之上,則返回true。
首先檢查,如果你有一條垂直線:
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
。
現在插入(x3, y3)
爲x
和y
。下面是一些僞細節會發生什麼:
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
失敗:垂直線的斜率計算無效。無盡的如果/其他的東西。不知道這是OP的左/右意味着什麼 - 如果這樣看着它旋轉90度將削減一半的代碼,因爲「上方」是正確的或左邊的。 – phkahler 2010-08-11 18:57:23
這個答案有幾個問題。垂直線導致除以零。更糟糕的是,它失敗了,因爲它不擔心線的斜率是正值還是負值。 – 2010-08-12 03:36:34
@phkahler,修復了垂直線問題。絕對不是一個忘記一個測試用例的失敗,但是要感謝這些客氣的話。 「如果/其他」是解釋數學理論; OP的問題中沒有提到編程。 @woodchips,修復了垂直線問題。斜率是變量m;我確實檢查它是正面還是負面。 – maksim 2010-08-12 22:46:13
基本上,我認爲有一個解決方案,它是非常容易和簡單的,對於任何給定的多邊形,可以說包括四個頂點(P1,P2,P3 ,p4),在多邊形中找到兩個極端相對的頂點,換句話說,找到例如最左上角的頂點(可以說是p1)以及位於最右下角(可以說)的相反頂點。因此,考慮到您的測試點C(x,y),現在您必須在C與p1和C以及p4之間進行雙重檢查:如果cx> p1x AND cy> p1y ==>意味着C較低並且到的P1 下 右如果CX < P2X和cy < P2Y ==>表示C是上和向左P4
結論的,C是在矩形內。
謝謝:)
(1)回答與問題不同的問題嗎?聽起來像是「邊界框」測試,當一個矩形與兩個軸對齊時。 (2)更詳細地:對4點之間的可能關係進行假設。例如,取一個矩形,並將其旋轉45度,以便獲得一顆鑽石。鑽石中沒有「左上角的點」。最左邊的點不是最頂部或最底部。當然,4分可以形成更奇怪的形狀。例如,3個點可能在一個方向上很遠,而另一個方向上的4個點可能很遠。繼續嘗試! – ToolmakerSteve 2013-07-10 05:28:11
我在Java中實現這一點,並(下面源)跑一個單元測試。上述解決方案均不起作用。這段代碼通過了單元測試。如果有人發現未通過單元測試,請告訴我。
代碼:NOTE: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));
}
假設點(AX,AY)(BX,BY)和(CX,CY),你需要計算:
( Bx-Ax)*(Cy-Ay) - (By-Ay)*(Cx-Ax)
如果點C位於由點A和點B形成的線上並且將具有不同簽署取決於一方。這是哪一邊取決於(x,y)座標的方向,但可以將A,B和C的測試值插入此公式中,以確定負值是左側還是右側。
這基本上是接受的答案的副本? – bummzack 2013-03-19 13:53:31
非常感謝您的迴應......這是非常有益的..保持偉大的工作:) - 1投票從我:D – 2013-06-25 08:10:16
@ 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)
最後,與其他跨產品基礎的解決方案,該解決方案返回一個布爾值,並且不會給出共線性的第三個結果。但它會給出一個有意義的結果,例如:
(is-left? [[1 1] [3 1]] [10 1])
false
右或左定義如何? A)從P1到P2或B)在平面上的線的左側或右側。 – phkahler 2010-08-11 18:58:59
爲了說明問題的第二部分,您可以使用atan2()而不是acos()來計算正確的角度。但是,Eric Bainville指出,使用交叉產品是最好的解決方案。 – dionyziz 2011-09-04 12:20:39
下面的許多解決方案都不起作用,因爲如果您交換點a和b(我們用來定義我們的線的點),它們會給出相反的答案。我在Clojure中給出了一個解決方案,在將它們與第三點進行比較之前,首先按照字典順序排列這兩點。 – Purplejacket 2015-02-23 00:28:29