平面上四个点的相对位置

gon*_*alo 3 algorithm geometry graph

平面中四个点有两个不同的相对位置:

点数

在位置1,这四个点可以形成一个凸四边形(即它们的凸包),在位置2,它们不能(在一个凸包中是一个三角形)。我的问题是:我该如何编写算法来找出这些点是在位置1还是位置2?(我知道所有四个点的坐标)。

Mar*_*son 5

对于平面中任意三点的点P,Q和R(不是共线的),可以通过查看量的符号来确定角度PQR是逆时针还是顺时针旋转:

(P[0] - R[0]) * (Q[1] - R[1]) - (P[1] - R[1]) * (Q[0] - R[0])
Run Code Online (Sandbox Code Playgroud)

其中P[0]和分别P[1]指代P的x坐标和y坐标,并且类似地指Q和R。

现在调用您的四个点P1,P2,P3和P4,并为四个三元组(P1,P2,P3),(P1,P2,P4),(P1,P3,P4)和(P2, P3,P4)(请注意:上面表达式中的点(P,Q,R)的顺序很重要)。如果所有符号都相等,或者有两个正负两个负号,则凸包为四边形。如果有三个正号和一个负号(或者相反),则四个点的凸包是一个三角形。或更简单地说,如果您的符号分别表示为+1和-1,请将四个符号相乘。如果乘积为+1,则表示四边形;否则为0。如果为-1,则表示为三角形。

上面假设四个点中没有三个是共线的。我让你来列举退化的案例。

由于这是StackOverflow,因此这里是一些代码(在Python中)。首先定义ccw(使用sign辅助函数)。

def sign(x):
    """ Return the sign of a finite number x. """
    if x > 0:
        return 1
    elif x < 0:
        return -1
    else:
        return 0

def ccw(P, Q, R):
    """ Return 1 if P-Q-R is a counterclockwise turn, -1 for clockwise,
        and 0 if the points are collinear (or not all distinct). """
    disc = (P[0] - R[0]) * (Q[1] - R[1]) - (P[1] - R[1]) * (Q[0] - R[0])
    return sign(disc)
Run Code Online (Sandbox Code Playgroud)

然后分类为四个点。

def classify_points(P, Q, R, S):
    """ Return 1 if the convex hull of P, Q, R and S is a quadrilateral,
        -1 if a triangle, and 0 if any three of P, Q, R and S are
        collinear (or if not all points are distinct). """
    return ccw(P, Q, R) * ccw(P, Q, S) * ccw(P, R, S) * ccw(Q, R, S)
Run Code Online (Sandbox Code Playgroud)

一个简单的测试:应将正方形与结果分类1

>>> # Test case 1: quadrilateral convex hull
>>> P = 0, 0
>>> Q = 0, 1
>>> R = 1, 0
>>> S = 1, 1
>>> classify_points(P, Q, R, S)
1
Run Code Online (Sandbox Code Playgroud)

结果是一个三角形-1

>>> # Test case 2: triangle.
>>> P = 0, 0
>>> Q = 0, 3
>>> R = 3, 0
>>> S = 1, 1
>>> classify_points(P, Q, R, S)
-1
Run Code Online (Sandbox Code Playgroud)

这是一个简并的情况(P,Q和S是共线的):

>>> P = 1, 1
>>> Q = 2, 2
>>> R = 5, 7
>>> S = 4, 4
>>> classify_points(P, Q, R, S)
0
Run Code Online (Sandbox Code Playgroud)

请注意,如果您使用的是不精确的浮点算法,则数字错误可能会导致将几乎退化的情况归类为退化的情况,反之亦然。

为证明上述理由:容易地检查是否互换ccw定义中的任何两个输入会反转结果的符号,并且互换classify_points定义中的任何两个输入是否会使乘积的符号保持不变。因此,我们可以随意对点进行重新排序以影响classify_points结果。

现在假设P1,P2,P3和P4具有四边形凸包。然后,通过上述观察,我们可以对点进行重新排序,以假定P1,P2,P3和P4以逆时针方向围绕该四边形的边界。然后每个ccw表达式为1,因此结果classify_points1。同样,如果P1,P2,P3和P4具有三角形凸包,我们可以重新排列,使P1,P2和P3去逆时针围成的三角形边界和P4是三角形内,而在这种情况下,ccw标志是11-11