对于平面中任意三点的点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_points
为1
。同样,如果P1,P2,P3和P4具有三角形凸包,我们可以重新排列,使P1,P2和P3去逆时针围成的三角形边界和P4是三角形内,而在这种情况下,ccw
标志是1
,1
,-1
和1
。