2012-08-10 91 views
13

如何編寫此函數?任何示例讚賞如何檢查一個點是否位於另外兩個點之間的線上

function isPointBetweenPoints(currPoint, point1, point2):Boolean { 

    var currX = currPoint.x; 
    var currY = currPoint.y; 

    var p1X = point1.x; 
    var p1y = point1.y; 

    var p2X = point2.x; 
    var p2y = point2.y; 

    //here I'm stuck 
} 
+1

下面有一些很好的答案,但我想我會指出你應該注意的浮動點精度問題。無論您使用哪種方法,例如,在測試兩個不同的斜率是否相同時,您可能必須允許少量的錯誤。 – 2012-08-10 20:45:26

+0

@Adrian McCarthy:這是基於斜率的方法的主要問題。斜率隨着角度變化不均勻:線越接近垂直,斜率越快增長(甚至沒有提到垂直線和幾乎垂直線的特殊情況)。沒有好的斜坡策略。我會不惜一切代價避免以斜率爲基礎的方法。 – AnT 2012-08-11 05:27:50

回答

34

假設point1point2是不同的,首先檢查點是否在線上。爲此,您只需要一個載體point1 -> currPointpoint1 -> point2的「交叉產品」。

dxc = currPoint.x - point1.x; 
dyc = currPoint.y - point1.y; 

dxl = point2.x - point1.x; 
dyl = point2.y - point1.y; 

cross = dxc * dyl - dyc * dxl; 

你點位於當且僅當cross等於零行。

if (cross != 0) 
    return false; 

現在,你知道點也騙就行了,現在是時候來檢查它是否位於原始點之間。這可以通過比較所述x座標來容易地完成,如果線是「比垂直更水平的」,或以其他方式y座標

if (abs(dxl) >= abs(dyl)) 
    return dxl > 0 ? 
    point1.x <= currPoint.x && currPoint.x <= point2.x : 
    point2.x <= currPoint.x && currPoint.x <= point1.x; 
else 
    return dyl > 0 ? 
    point1.y <= currPoint.y && currPoint.y <= point2.y : 
    point2.y <= currPoint.y && currPoint.y <= point1.y; 

注意上面的算法,如果如果輸入數據是積分完全積分,即它整數輸入不需要浮點計算。雖然計算cross時要小心潛在的溢出。

P.S.這個算法絕對精確,這意味着它會拒絕非常接近線但不在線上的點。有時候這不是我們所需要的。但這是一個不同的故事。

+5

通過在交叉乘積的驗證中實現閾值,可以降低算法精度因此,如果交叉乘積幾乎爲零,那麼該點幾乎在線上'threshold = 0.1; if(abs(cross)> threshold)返回false;'。 – Matej 2015-04-01 08:56:38

+0

我們可以簡化這個嗎?既然我們知道它是在線上,爲什麼我們在乎它是否比水平更加水平?對於線上的任何給定x,只能有一個y值,所以如果'currPoint.x'介於'point1.x'和'point2.x'之間,它怎麼可能在線以外的任何地方? – mkirk 2016-06-04 02:00:05

+1

@mkirk:「對於線上的任何給定x,只能有一個y值」 - 對於垂直線不是這樣。如果段嚴格垂直,那麼範圍檢查'x'不會產生有意義的答案。是的,人們總是可以檢查'x'範圍,但是對於嚴格垂直的線段,必須檢查'y'範圍。我用「更多水平」/「更垂直」的方法只是一種平衡的泛化。 – AnT 2016-06-04 02:01:46

3

這是獨立於Javascript。試試下面的算法,用點P1 =點1和p2 =點2,和你的第三個點的存在P3 = currPoint:

v1 = p2 - p1 
v2 = p3 - p1 
v3 = p3 - p2 
if (dot(v2,v1)>0 and dot(v3,v1)<0) return between 
else return not between 

如果你想確保它在P1和P2之間的線段以及:

v1 = normalize(p2 - p1) 
v2 = normalize(p3 - p1) 
v3 = p3 - p2 
if (fabs(dot(v2,v1)-1.0)<EPS and dot(v3,v1)<0) return between 
else return not between 
3

您要檢查從point1currPoint斜率是否相同斜率從currPointpoint2,所以:

m1 = (currY - p1Y)/(currX - p1X); 
m2 = (p2Y - currY)/(p2X - currX); 

你還需要檢查是否currPoint是由其他兩個創建的盒子裏面,所以:

return (m1 == m2) && (p1Y <= currY && currY <= p2Y) && (p1X <= currX && currX <= p2X); 

編輯:這是不是一個很好的方法;看看maxim1000's solution更正確的方法。

+0

是的,我剛剛意識到我的錯誤。我想我可以添加另一個約束來解決這個問題。 – 2012-08-10 19:27:38

+0

但輸入數據並沒有聲明'p1X <= p2X'和'p1Y <= p2Y'(此外,在任何情況下都不是這樣)。然而,只有滿足這些條件,您的最終檢查才能正常工作。最重要的是,您的算法依賴於浮點值'm1'和'm2'的直接精確比較。不用說,由於浮點計算的不精確性,這根本無法工作。 – AnT 2012-08-10 22:37:23

+0

...甚至沒有提到基於斜率的方法不能處理垂直線。除以零除什麼? – AnT 2012-08-11 05:28:36

19
Distance(point1,currPoint)+Distance(currPoint,point2)==Distance(point1,point2) 

但要小心,如果你有浮點值,事情對他們不同的...

+4

非常好,浮點值(如JavaScript),你可以做類似'return distanceAC + distanceBC - distanceAB mikhail 2014-01-14 21:42:56

+1

這個方法比AnT的交叉值更穩定。 – CodePlumber 2017-01-28 16:38:15

+0

但它需要更多的計算能力(3倍平方根)。 – Chrisstar 2017-11-08 20:07:06

相關問題