射线法交点计数排除顶点和水平边,因顶点被两条边共享易致重复计数,水平边与射线平行无穿越语义;浮点比较需用eps避免精度误差破坏奇偶逻辑;std::vector实现应自动闭合、统一处理上端点、严格判断x_intersect > p.x。

射线法判断点在多边形内时,为什么交点计数要排除顶点和水平边?
因为射线法本质依赖「奇偶性」:从测试点向右(或任意固定方向)引一条射线,统计它与多边形边的**有效交点数**;奇数表示在内,偶数表示在外。但若不剔除退化情况,顶点重合、边共线会导致重复计数或漏计。
关键不是“能不能交”,而是“是否算作一次穿越”。顶点被两条边共享,若直接计数可能被算两次;水平边与射线平行,无穿越语义,必须跳过。
- 只处理
y坐标严格跨过测试点y值的边(即min(y1, y2) = py) - 当
py == y1 || py == y2时,仅当该端点是边中y值较大的那个才参与计算(避免顶点被两个邻边重复计入) - 完全水平的边(
y1 == y2)直接忽略
C++实现射线法时,浮点比较为什么要用 EPS 而不能直接用 ==?
浮点运算存在精度误差,比如两个理论上相等的交点 x 坐标,实际计算可能为 3.0000001 和 2.9999999。直接 == 判断会导致本应算作一个交点的被当成零个或两个,破坏奇偶逻辑。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
EPS 不是越小越好——太小会放大舍入噪声;太大又可能合并本应分离的交点。对 double 类型,1e-9 是较稳妥的起点。
- 所有涉及坐标的比较(如
y1 == y2、x_intersect == px)都应改写为fabs(a - b) - 射线方向选向右(
+x)时,交点x_intersect计算公式为:px + (py - y1) * (x2 - x1) / (y2 - y1),分母需先判fabs(y2 - y1) > EPS - 若使用整数坐标且确保无精度问题,可省略 EPS,但代码通用性下降
如何用 std::vector 实现一个健壮的 isPointInPolygon 函数?
标准做法是遍历每条有向边 poly[i] → poly[(i+1)%n],对每条边执行交点检测。注意多边形顶点顺序(顺/逆时针)不影响结果,但要求是简单多边形(无自交)。
struct Point { double x, y; };
bool isPointInPolygon(const std::vector<point>& poly, const Point& p) {
int n = poly.size();
if (n b.y) std::swap(a, b); // 保证 a.y = b.y) continue;
if (fabs(a.y - b.y) p.x) inside = !inside;
}
return inside;
}
</point>
- 使用
(i + 1) % n自动闭合多边形,无需额外添加首顶点到末尾 -
p.y >= b.y中的>=是为了统一处理上端点:仅当测试点 y 严格小于上端点 y 时才可能穿越,否则该边不贡献交点 - 交点只需比较
x_intersect > p.x,因射线向右,只关心是否“穿过右侧”
射线法在边界上的点会返回 true 还是 false?怎么控制?
标准射线法默认将边界视为“内部”,但实际行为取决于你对顶点和交点的判定逻辑。上面示例中,当 p.y == a.y 且 a 是下端点时,该边会被纳入计算,若交点恰在 p.x 处,则 x_intersect == p.x 导致 x_intersect > p.x 为假,不翻转 inside——也就是说,边界点大概率返回 false。
- 若需包含边界,可将交点判断改为
x_intersect >= p.x,并调整端点归属规则(例如:当p.y == a.y且a是下端点时,认为射线“擦过”该顶点,计入) - 更可靠的做法是单独加一步边界检测:用点到线段距离
distToSegment(p, a, b) 遍历所有边 - 多数图形库(如 CGAL)把“在边界上”定义为独立状态,不混入 in/out 布尔值,这点容易被忽略
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










