射线法(Ray Casting Algorithm)是计算几何中判断点与多边形位置关系的经典算法。它的核心思想是从待测点P(x,y)向任意方向发射一条射线(通常选择水平向右),统计该射线与多边形边的交点数量:
这个原理基于约当曲线定理(Jordan Curve Theorem)——任何简单闭合曲线将平面分成内部和外部两个区域,从内部到外部的路径必须穿过曲线奇数次。
实际应用中需要特别注意三种边界情况:
点在顶点上:
射线经过顶点:
python复制if pt[1] > min(y1, y2) and pt[1] <= max(y1, y2): # 严格大于最小值,小于等于最大值
水平边处理:
提示:实际工程中建议将"在边上"的情况单独返回枚举值(INSIDE/OUTSIDE/BOUNDARY),而不是简单归为内部。
交点的x坐标计算使用直线方程的两点式推导:
给定边AB(A(x1,y1)到B(x2,y2))和测试点P(x,y),当P的y坐标在y1和y2之间时,交点x坐标计算公式为:
code复制x_intersect = (y - y1) * (x2 - x1) / (y2 - y1) + x1
这个推导过程是:
原始代码可以进行多处优化:
顶点预处理:
python复制# 将多边形顶点复制一份方便循环处理
vertices = polygon + [polygon[0]]
向量化计算:
python复制for i in range(len(polygon)):
x1, y1 = polygon[i]
x2, y2 = polygon[(i+1)%len(polygon)]
# 后续计算...
提前终止判断:
python复制if x < min(x1, x2): # 点在边左侧,不可能有交点
continue
使用numpy批量处理:
python复制import numpy as np
def point_in_polygon(points, polygon):
x, y = points.T
inside = np.zeros(len(points), dtype=bool)
# 向量化计算...
return inside
python复制def is_point_in_polygon(point, polygon):
"""判断点是否在多边形内(含边界)
参数:
point: 待测点 (x,y)
polygon: 多边形顶点列表 [(x1,y1), (x2,y2),...]
返回:
True: 点在多边形内或边界上
False: 点在多边形外
"""
x, y = point
n = len(polygon)
inside = False
# 方便处理最后一条边
p1x, p1y = polygon[0]
for i in range(n+1):
p2x, p2y = polygon[i % n]
# 检查点是否在边上
if (x == p1x and y == p1y) or (x == p2x and y == p2y):
return True
# 检查水平射线与边的交点
if y > min(p1y, p2y):
if y <= max(p1y, p2y):
if x <= max(p1x, p2x):
if p1y != p2y: # 排除水平边
xinters = (y-p1y)*(p2x-p1x)/(p2y-p1y)+p1x
if p1x == p2x or x <= xinters:
inside = not inside
p1x, p1y = p2x, p2y
return inside
良好的测试应覆盖各种边界情况:
python复制# 测试正方形
square = [(1,1), (5,1), (5,5), (1,5)]
test_cases = [
((3,3), True), # 内部
((0,0), False), # 外部
((1,1), True), # 顶点
((3,1), True), # 边
((3,5), True), # 上边
((5,3), True), # 右边
((1,3), True), # 左边
((3,0), False), # 下方外部
((3,6), False), # 上方外部
((6,3), False) # 右侧外部
]
for point, expected in test_cases:
assert is_point_in_polygon(point, square) == expected
对于需要处理大量点的情况,可以考虑:
空间索引优化:
python复制min_x = min(p[0] for p in polygon)
max_x = max(p[0] for p in polygon)
min_y = min(p[1] for p in polygon)
max_y = max(p[1] for p in polygon)
if x < min_x or x > max_x or y < min_y or y > max_y:
return False
并行计算:
python复制from multiprocessing import Pool
def batch_check(points, polygon):
with Pool() as p:
return p.starmap(is_point_in_polygon, [(pt, polygon) for pt in points])
顶点顺序问题:
浮点精度问题:
python复制def is_close(a, b, rel_tol=1e-9):
return abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), rel_tol)
自相交多边形:
地理信息系统(GIS):
计算机图形学:
游戏开发:
三维空间判断:
带洞多边形处理:
其他判断算法:
在实际项目中,我通常会根据具体场景选择算法。对于简单多边形,射线法已经足够高效;对于需要更高精度的场景,可以考虑结合多种算法。一个实用的建议是:先进行快速包围盒检测排除明显不在多边形内的点,再使用射线法进行精确判断,这样可以显著提升性能。