2009-10-13 254 views
94

我有一組點。我想將它們分成2個不同的集合。爲此,我選擇兩個點(ab)並在它們之間繪製一條虛線。現在我想把這一行中的所有點都放在一個集合中,並且這些行中的所有點都放在另一個集合中。如何判斷一個點是否在一條線的右側或左側

我該如何判斷任何給定的點z無論是在左邊還是在右邊?我試圖計算a-z-b –之間的角度,右側的角度小於180°,左側的角度大於180°–但由於ArcCos的定義,計算的角度始終小於180°。是否有計算角度大於180°的公式(或任何其他公式可選擇右側或左側)?

+0

右或左定義如何? A)從P1到P2或B)在平面上的線的左側或右側。 – phkahler 2010-08-11 18:58:59

+2

爲了說明問題的第二部分,您可以使用atan2()而不是acos()來計算正確的角度。但是,Eric Bainville指出,使用交叉產品是最好的解決方案。 – dionyziz 2011-09-04 12:20:39

+0

下面的許多解決方案都不起作用,因爲如果您交換點a和b(我們用來定義我們的線的點),它們會給出相反的答案。我在Clojure中給出了一個解決方案,在將它們與第三點進行比較之前,首先按照字典順序排列這兩點。 – Purplejacket 2015-02-23 00:28:29

回答

148

使用矢量(AB,AM)的行列式,其中M(X,Y)是查詢點的符號:

position = sign((Bx - Ax) * (Y - Ay) - (By - Ay) * (X - Ax)) 

它是0就行,和+1在一側,-1在另一側。

+7

+1不錯,有一件事要注意:舍入錯誤可能是一個問題,當點很接近線。 *大多數情況下不會造成問題,但它會不時地咬人。 – 2009-10-13 14:18:53

+0

太棒了!這是一個很好的,沒有竇,arccos等:) 舍入誤差在這裏不是一個問題,因爲我使用算法中的分離來計算點集的凸包並在它們周圍繪製折線。如果一個點位於船體外面,這不是什麼大問題。 – Aaginor 2009-10-13 14:53:37

+13

如果您發現自己處於這個測試的舍入錯誤導致您遇到問題的情況,您將需要查看Jon Shewchuk的「用於計算幾何的快速魯棒謂詞」。 – 2009-10-13 15:13:45

3

使用equation of the lineab,獲取與要排序的點在同一個y座標線上的x座標。

  • 如果point的x> line的x,則該點位於該行的右側。
  • 如果點的 x <行的x,該點在該行的左側。
  • 如果點的x ==線的x,該點在線上。
+0

這是錯誤的,因爲正如你可以從Aaginor對第一個答案的評論中看到的那樣,我們不想知道該點是在DIRECTED線AB的左側還是右側,即如果你站在A上,朝着B方向看,是在你的左邊還是在右邊? – dionyziz 2011-09-04 12:18:43

+0

@dionyziz - 咦?我的回答並沒有通過AB給線路分配「方向」。我的答案假設「左」是corrdinate系統的-x方向。接受的答案選擇定義一個*向量* AB,並使用交叉乘積定義左邊。原始問題沒有說明「左」是什麼意思。 – mbeckish 2011-09-06 19:29:46

+3

注意:如果您使用這種方法(而不是交叉產品被批准爲答案),請注意在線接近水平線時存在缺陷。數學錯誤增加,並且如果完全水平,則命中無窮。解決方案是使用兩個點之間具有較大三角形的軸。 (或者也許更小的三角洲..這是我的頭頂。) – ToolmakerSteve 2013-07-10 05:20:33

38

你看看

| x2-x1 x3-x1 | 
| y2-y1 y3-y1 | 

行列式這將利好點一側的(上線本身分零)的標誌,而在其他陰性。

7

矢量(y1 - y2, x2 - x1)與線垂直,並且始終指向右側(或者如果平面方向與我的平面方向不同,則總是指向左側)。

然後,您可以計算該向量的點積和(x3 - x1, y3 - y1),以確定該點是否與垂直向量(點積>0)位於該線的同一側。

186

嘗試這個代碼,其利用一個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。

+6

如果線是垂直的呢? – Sameer 2012-02-29 10:31:46

+9

你的意思是點積? – 2012-05-16 23:01:33

+12

@lzprgmr:不,這是一個叉積,等價於二維矩陣的行列式。 考慮由行(a,b)和(c,d)定義的2D矩陣。行列式是ad - bc。 (a,b),然後使用PointA和PointC定義* another *向量得到(c,d): (a,b)=(PointB .x - PointA.x,PointB.y - PointA.y (c,d)=(PointC.x - 該職位。 – AndyG 2013-06-05 18:59:14

3

首先檢查,如果你有一條垂直線:

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)xy。下面是一些僞細節會發生什麼:

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 
+3

失敗:垂直線的斜率計算無效。無盡的如果/其他的東西。不知道這是OP的左/右意味着什麼 - 如果這樣看着它旋轉90度將削減一半的代碼,因爲「上方」是正確的或左邊的。 – phkahler 2010-08-11 18:57:23

+1

這個答案有幾個問題。垂直線導致除以零。更糟糕的是,它失敗了,因爲它不擔心線的斜率是正值還是負值。 – 2010-08-12 03:36:34

+2

@phkahler,修復了垂直線問題。絕對不是一個忘記一個測試用例的失敗,但是要感謝這些客氣的話。 「如果/其他」是解釋數學理論; OP的問題中沒有提到編程。 @woodchips,修復了垂直線問題。斜率是變量m;我確實檢查它是正面還是負面。 – maksim 2010-08-12 22:46:13

1

基本上,我認爲有一個解決方案,它是非常容易和簡單的,對於任何給定的多邊形,可以說包括四個頂點(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是在矩形內。

謝謝:)

+0

(1)回答與問題不同的問題嗎?聽起來像是「邊界框」測試,當一個矩形與兩個軸對齊時。 (2)更詳細地:對4點之間的可能關係進行假設。例如,取一個矩形,並將其旋轉45度,以便獲得一顆鑽石。鑽石中沒有「左上角的點」。最左邊的點不是最頂部或最底部。當然,4分可以形成更奇怪的形狀。例如,3個點可能在一個方向上很遠,而另一個方向上的4個點可能很遠。繼續嘗試! – ToolmakerSteve 2013-07-10 05:28:11

5

我在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)); 
} 
+0

你近似等於幾何的單位是多少? – spy 2016-01-04 03:35:57

+0

我找不到接近平等的代碼:( – Renjith 2016-08-13 20:14:50

1

假設點(AX,AY)(BX,BY)和(CX,CY),你需要計算:

( Bx-Ax)*(Cy-Ay) - (By-Ay)*(Cx-Ax)

如果點C位於由點A和點B形成的線上並且將具有不同簽署取決於一方。這是哪一邊取決於(x,y)座標的方向,但可以將A,B和C的測試值插入此公式中,以確定負值是左側還是右側。

+2

這基本上是接受的答案的副本? – bummzack 2013-03-19 13:53:31

+0

非常感謝您的迴應......這是非常有益的..保持偉大的工作:) - 1投票從我:D – 2013-06-25 08:10:16

1

@ AVB的答案紅寶石

det = Matrix[ 
    [(x2 - x1), (x3 - x1)], 
    [(y2 - y1), (y3 - y1)] 
].determinant 

如果det爲正其上面,如果爲負其下方。如果是0,那就行了。

1

這是一個使用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 
相關問題