凸多边形
问题
给定一个点o和平面中三个点a、b、c,判断o是否在三角形内部
解决
可以通过面积算,即判断o点与另外两个点构成的三角形面积之和是否等于三角形abc的面积。
面积可以通过海伦公式计算,即假设三边长分别为a、b、c,
s
=
p
(
p
−
a
)
(
p
−
b
)
(
p
−
c
)
,
p
=
a
+
b
+
c
2
s=\sqrt{p(p-a)(p-b)(p-c)}, p=\frac{a+b+c}{2}
s=p(p−a)(p−b)(p−c),p=2a+b+c
但是这种方法可能因为浮点数精度问题导致误差,所以最好使用叉乘法。
有图可知,按逆时针顺序得到的向量,点o在他们的左侧即表示在三角形内部。AB*AO>0则表示o在AB左侧,叉乘公式为
(
x
1
∗
y
2
−
x
2
∗
y
1
)
(x_1*y_2-x_2*y_1)
(x1∗y2−x2∗y1)。
代码:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def product(p1, p2, p3):
# 叉乘
return (p2.x - p1.x) * (p3.y - p1.y) - \
(p2.y - p1.y) * (p3.x - p1.x)
def solution(p1, p2, p3, o):
# 确保逆时针,即p3在p1p2的左侧,否则调换位置
if product(p1, p2, p3) < 0:
return solution(p1, p3, p2, o)
# o在三角形内部,则在逆时针每个向量的左侧,均>0
if product(p1, p2, o) > 0 and \
product(p2, p3, o) > 0 and \
product(p3, p1, o) > 0:
return True
return False
也有可能要判断三个点能否构成三角形,只要任意两边斜率不相等即可。
非凸多边形
非凸多边形就不能用叉乘的方式,但如果一个点在多边形内部,以其为点向任意方向做射线,则必然存在奇数个交点,在多边形外则一定偶数个交点(0也是偶数)。
import collections
# polygon: list of points
def IsPointInPolygon(point, polygon):
lines = collections.defaultdict()
for i in range(len(polygon) - 1):
for j in range(i+1, len(polygon)):
c = 0
if polygon[i].x - polygon[j].x == 0:
a = polygon[i].x
c = 1
else:
a = (polygon[i].y - polygon[j].y) / (polygon[i].x - polygon[j].x)
b = polygon[i].y - a * polygon[i].x
lines[i, j] = [a, b, c]
a = 1
b = point.y - point.x
cnt = 0
for i, j in lines.keys():
c, d, e = lines[i, j][0], lines[i, j][1], lines[i, j][2]
if e:
x = c
else:
x = (d - b) / (a - c)
y = a * x + b
if (y - polygon[i].y) * (y - polygon[j].y) < 0 and (y - point.y) / (x - point.x) > 0:
cnt += 1
if cnt % 2:
return True
return False