[英]How to let the program know if a point is at the right or the left of a line?
[英]How to tell whether a point is to the right or left side of a line
我有一組要點。 我想將它們分成兩個不同的集合。 為此,我選擇了兩個點( a和b )並在它們之間畫了一條假想線。 現在我想把這條線左邊的所有點都放在一組里,把這條線右邊的點放在另一組里。
我如何判斷任何給定點z是在左側還是右側? 我試圖計算a-zb之間的角度——小於 180 的角度在右側,大於 180 的角度在左側——但是由於 ArcCos 的定義,計算出的角度總是小於 180°。 是否有計算大於 180° 的角度的公式(或選擇右側或左側的任何其他公式)?
試試這個使用叉積的代碼:
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。
使用向量(AB,AM)
行列式的符號,其中M(X,Y)
是查詢點:
position = sign((Bx - Ax) * (Y - Ay) - (By - Ay) * (X - Ax))
線上為0
,一側為+1
,另一側為-1
。
你看行列式的符號
| x2-x1 x3-x1 |
| y2-y1 y3-y1 |
一側的點為正,另一側為負(直線本身的點為零)。
矢量(y1 - y2, x2 - x1)
垂直於線,並且始終指向右側(或始終指向左側,如果您的平面方向與我的不同)。
然后,您可以計算該向量與(x3 - x1, y3 - y1)
的點積,以確定該點是否與垂直向量(點積 > 0
)位於直線的同一側。
首先檢查是否有垂直線:
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
我在 java 中實現了這個並運行了一個單元測試(來源如下)。 上述解決方案均無效。 這段代碼通過了單元測試。 如果有人發現未通過的單元測試,請告訴我。
代碼: 注意:如果兩個數字非常接近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));
}
我想提供一個受物理學啟發的解決方案。
想象一下沿線施加的力,您正在測量該點周圍的力的扭矩。 如果扭矩為正(逆時針),則該點位於直線的“左側”,但如果扭矩為負,則該點位於直線的“右側”。
所以如果力矢量等於定義線的兩點的跨度
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
假設點是 (Ax,Ay) (Bx,By) 和 (Cx,Cy),您需要計算:
(Bx - Ax) * (Cy - Ay) - (By - Ay) * (Cx - Ax)
如果點 C 位於由點 A 和 B 形成的線上,則該值將為零,並且根據邊的不同將具有不同的符號。 這是哪一側取決於您的 (x,y) 坐標的方向,但您可以將 A、B 和 C 的測試值插入此公式以確定負值是向左還是向右。
這是一個版本,再次使用交叉產品邏輯,用 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
@AVB 在 ruby 中的回答
det = Matrix[
[(x2 - x1), (x3 - x1)],
[(y2 - y1), (y3 - y1)]
].determinant
如果det
為正則其上方,如果負則其下方。 如果為0,它就行了。
基本上,我認為有一個更簡單直接的解決方案,對於任何給定的多邊形,假設由四個頂點(p1,p2,p3,p4)組成,在多邊形中找到兩個極端相反的頂點,在另一個話,找到例如最左上角的頂點(比方說 p1)和位於最右下角的相反頂點(比方說)。 因此,鑒於您的測試點 C(x,y),現在您必須在 C 和 p1 以及 C 和 p4 之間進行雙重檢查:
if cx > p1x AND cy > p1y ==> 表示 C 位於 p1 的下方和右側 if cx < p2x AND cy < p2y ==> 表示 C 位於 p4 的上方和左側
結論,C在矩形內。
謝謝 :)
雖然我發現 Eric Bainville 的回答是正確的,但我發現它完全不足以理解:
sign
? position = sign((Bx - Ax) * (Y - Ay) - (By - Ay) * (X - Ax))
Bx
?Y
? Y
不是一個向量,而不是一個標量嗎?此外,我的用例涉及復雜的曲線而不是簡單的直線,因此需要進行一些重新調整:
Point a = new Point3d(ax, ay, az); // point on line
Point b = new Point3d(bx, by, bz); // point on line
如果您想查看您的點是否高於/低於曲線,那么您需要獲得您感興趣的特定曲線的一階導數 - 也稱為曲線上點的切線。 如果你能這樣做,那么你就可以突出你的興趣點。 當然,如果你的曲線是一條線,那么你只需要沒有切線的興趣點。 切線是直線。
Vector3d lineVector = curve.GetFirstDerivative(a); // where "a" is a point on the curve. You may derive point b with a simple displacement calculation:
Point3d b = new Point3d(a.X, a.Y, a.Z).TransformBy(
Matrix3d.Displacement(curve.GetFirstDerivative(a))
);
Point m = new Point3d(mx, my, mz) // the point you are interested in.
return (b.X - a.X) * (m.Y - a.Y) - (b.Y - a.Y) * (m.X - a.X) < 0; // the answer
為我工作! 請參閱上圖中的證明。 青磚滿足條件,但外面的磚被過濾掉了。 在我的用例中 - 我只想要接觸圓圈的磚塊。
我會回來解釋這個。 總有一天。 不知何故...
了解 netters 提供的解決方案的另一種方法是了解一些幾何含義。
讓pqr =[P,Q,R] 是形成一個平面的點,該平面被線[P,R]分成 2 邊。 我們要找出pqr平面上的兩個點 A、B 是否在同一側。
pqr 平面上的任何點T都可以用 2 個向量表示: v = PQ 和u = RQ,如下所示:
T' = TQ = 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
所以我們在平面上得到了 2 個點,A,B:
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 * RB = b1 * P + b2 * Q + b3 * R
和 A,B 在平面 [P,Q,R] 的同一側,如果
sign(a1+a2+a3-1) = sign(b1+b2+b3-1)
直線方程是 y-y1 = m(x-x1)
這里 m 是 y2-y1 / x2-x1
現在將 m 放入方程並將條件放在 y < m(x-x1) + y1 上,然后它是左側點
例如。
for i in rows:
for j in cols:
if j>m(i-a)+b:
image[i][j]=0
A(x1,y1) B(x2,y2) 長度為 L=sqrt( (y2-y1)^2 + (x2-x1)^2 ) 的線段
和一個點 M(x,y)
進行坐標變換,以便成為新的起點 A 和新 X 軸的點 B
我們有了點 M 的新坐標
這是 newX = ((x-x1) (x2-x1)+(y-y1) (y2-y1)) / L
從 (x-x1)*cos(t)+(y-y1)*sin(t) 其中 cos(t)=(x2-x1)/L, sin(t)=(y2-y1)/L
newY = ((y-y1) (x2-x1)-(x-x1) (y2-y1)) / L
從 (y-y1)*cos(t)-(x-x1)*sin(t)
因為“左”是 Y 為正的 X 軸的一側,如果 newY(即 M 與 AB 的距離)為正,則它在 AB 的左側(新的 X 軸)您可以省略除以 L(始終為正),如果您只想要符號
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.