我有几点要点。我想将它们分为2个不同的集合。为此,我选择两个点(a和b)并在它们之间画一条假想线。现在,我要使一组中的该行剩余的点和另一组中的该行右边的点。
如何确定给定点z是在左侧还是右侧?我试图计算azb之间的角度-右侧小于180度,左侧大于180度-但是由于ArcCos的定义,计算出的角度始终小于180°。是否存在用于计算大于180°的角度的公式(或用于选择右侧或左侧的任何其他公式)?
我有几点要点。我想将它们分为2个不同的集合。为此,我选择两个点(a和b)并在它们之间画一条假想线。现在,我要使一组中的该行剩余的点和另一组中的该行右边的点。
如何确定给定点z是在左侧还是右侧?我试图计算azb之间的角度-右侧小于180度,左侧大于180度-但是由于ArcCos的定义,计算出的角度始终小于180°。是否存在用于计算大于180°的角度的公式(或用于选择右侧或左侧的任何其他公式)?
Answers:
使用vector的行列式符号(AB,AM)
,其中M(X,Y)
查询点为:
position = sign((Bx - Ax) * (Y - Ay) - (By - Ay) * (X - Ax))
它0
在线上,+1
在一侧,-1
在另一侧。
positions
?
尝试使用交叉产品的以下代码:
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。
return (b.x - a.x)*(c.y - a.y) > (b.y - a.y)*(c.x - a.x);
,但是编译器可能仍会对其进行优化。
你看一下行列式的符号
| 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
。
现在插入(x3, y3)
for 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
我在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
基本上,我认为对于任何给定的多边形,有一个简单易行的解决方案,可以说由四个顶点(p1,p2,p3,p4)组成,在多边形中找到两个极端相反的顶点,在另一个多边形中例如,找到最左上的顶点(让说p1)和位于最右下角的相反顶点(让说)。因此,给定测试点C(x,y),现在您必须在C和p1以及C和p4之间进行仔细检查:
如果cx> p1x AND cy> p1y ==>表示C较低且在p1的右边,如果cx <p2x AND cy <p2y ==>表示C较高且在p4的左边
结论,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
体验netter提供的解决方案的另一种方法是了解一些几何含义。
令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在同一侧,则为true:
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)