射线法判定点在多边形内:从点向右引水平射线,统计与边的严格交点数,奇数则在内部;需处理浮点精度、顶点共线、水平边及边界情况,EPS应动态计算,分母为零须检查,交点比较需加容差。

射线法核心逻辑与边界情况处理
射线法本质是:从待测点向右(或任意固定方向)引一条水平射线,统计它与多边形边界的交点数;奇数次相交 → 点在内部,偶数次(含 0)→ 在外部。但直接套用会踩坑:std::vector 存顶点顺序必须是首尾闭合(即最后一个点到第一个点隐含一条边),且所有边不能退化为点(p[i] == p[i+1])。
关键难点不在算法本身,而在浮点精度和共线/顶点重合等边界判断。比如射线恰好穿过顶点、擦过水平边、或点落在边上——这些情况若不做统一约定,结果会随实现抖动。
- 统一规定:射线方向为正 x 轴(
y = py,x > px),只考虑「严格上方交点」和「下方交点」,忽略水平边(y1 == y2) - 对每个边
p[i] → p[i+1],先检查是否跨过当前点的 y 坐标:即(y1 > py) != (y2 > py) - 再计算交点 x 坐标:
x_intersect = x1 + (py - y1) * (x2 - x1) / (y2 - y1);仅当x_intersect > px才计数 - 顶点落在射线上(
y1 == py)时,只计入「上端点」(即y1 > y2的那个顶点),避免重复或漏计
C++ 实现中必须注意的浮点比较问题
用 double 或 float 做坐标运算时,直接写 y1 == py 极不可靠。实际应引入小量 EPS,但 EPS 不能一刀切设为 1e-9 —— 若坐标值在百万级,这个精度反而会导致误判。
推荐按比例动态容差:const double EPS = 1e-10 * std::max({1.0, fabs(px), fabs(py), fabs(x1), fabs(y1), fabs(x2), fabs(y2)});
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 判断「点在边上」需单独处理(如要求包含边界),此时要用点到线段距离 ≤ EPS,而非依赖射线交点
- 除法前必须检查分母
fabs(y2 - y1) > EPS,否则触发 NaN 或无穷大 - 比较
x_intersect > px时也应写成x_intersect > px + EPS,防止因舍入误差把本该计入的交点丢掉
完整可粘贴的 C++ 函数示例(含注释)
以下函数假设多边形顶点按逆时针或顺时针顺序存于 std::vector<:pair double>></:pair> 中,不自动闭合,因此循环时用取模处理:
bool pointInPolygon(const std::vector<std::pair<double, double>>& poly, double px, double py) {
int n = poly.size();
if (n < 3) return false;
int cnt = 0;
double EPS = 1e-10;
for (int i = 0; i < n; ++i) {
double x1 = poly[i].first, y1 = poly[i].second;
double x2 = poly[(i + 1) % n].first, y2 = poly[(i + 1) % n].second;
if (fabs(y1 - y2) < EPS) continue; // 水平边跳过
if ((y1 > py) != (y2 > py)) {
double x_intersect = x1 + (py - y1) * (x2 - x1) / (y2 - y1);
if (x_intersect > px + EPS) cnt++;
}
}
return cnt & 1;
}
注意:此版本默认「点在边上返回 false」。若需包含边界,应在循环外加一次 pointOnSegment 检查。
性能与实操建议:什么时候不该用射线法
射线法时间复杂度是 O(n),对单点单多边形没问题;但若要批量判断(如渲染光栅化、碰撞检测),它比预处理的 bounding box + spatial partitioning(如四叉树)慢得多。
- 多边形固定且查询频繁?先构建
std::vector<AABB>(轴对齐包围盒)做粗筛,再对候选多边形调用射线法 - 多边形含大量凹陷或自相交?射线法仍可用,但语义可能不符合直觉(例如自交区域奇偶翻转),此时应先调用
poly2tri或clipper2库做裁剪归一化 - 嵌入式或无 STL 环境?把
std::vector换成裸数组 + 长度参数,std::pair换成结构体,其余逻辑不变
真正容易被忽略的是:射线法对「多边形定义」有隐含假设——顶点必须构成简单闭合环。如果输入数据来自用户手绘或 GIS 导出,务必先做拓扑校验,否则 cnt & 1 的结果毫无意义。

















