判断点在凸多边形内需所有叉积同号(≥0或≤0),前提是顶点严格顺/逆时针有序;叉积为0仅说明共线,须额外判断是否在线段范围内,且须用eps处理浮点误差。

用叉积符号判断点与每条边的相对位置
核心思路是:对凸多边形的每条有向边 edge[i] → edge[i+1],计算点 P 相对于该边的叉积符号。若所有叉积同号(全 ≥ 0 或全 ≤ 0,取决于顶点顺序),则点在内部或边上;否则在外。
关键前提是顶点必须按顺时针或逆时针顺序排列(不能乱序)。常见错误是传入无序顶点数组,导致叉积符号杂乱,结果完全不可靠。
实操建议:
- 先用
std::is_sorted或手动检查顶点是否构成单调环绕(如用atan2算极角排序),但更稳妥的是预处理时确保输入是合法凸包(例如用convex_hull_graham生成) - 叉积公式为:
(v1.x * v2.y - v1.y * v2.x),其中v1 = edge[i+1] - edge[i],v2 = P - edge[i] - 使用
double计算叉积,避免整数溢出;比较时用eps = 1e-9处理浮点误差,不要直接写== 0
处理边界情况:点恰好落在边上或顶点上
叉积为 0 表示点共线于某条边,但不等于就在多边形内——还需进一步判断是否在线段范围内。这是最容易漏掉的坑。
立即学习“C++免费学习笔记(深入)”;
实操建议:
- 当某次叉积绝对值 eps 时,不能直接返回 false,要额外做线段包含检测:
dot(P - edge[i], P - edge[i+1]) (即向量夹角 ≥ 90°,说明投影落在端点之间) - 若点与某个顶点距离 eps,可提前返回 true;但不要用欧氏距离平方开方,改用
(P.x - v.x)*(P.x - v.x) + (P.y - v.y)*(P.y - v.y) - 若多边形退化(三点共线、边长为 0),需在调用前用
std::unique去重并跳过零长边,否则叉积恒为 0 导致误判
性能优化:提前退出 + 缓存边向量
最坏情况要算 n 次叉积,但实际中多数点都在外部,越早发现异号叉积越快返回。同时重复构造向量会拖慢速度。
实操建议:
- 循环中一旦发现当前叉积符号与首个非零叉积符号相反,立即 return false;不要等全部算完
- 把边向量
edge[i+1] - edge[i]预存在std::vector<:pair>></:pair>中,避免每次循环重复减法运算 - 若需高频调用(如光栅化、碰撞检测),可将多边形顶点转为
std::array固定大小存储,并用constexpr预计算边向量(C++20 起支持)
完整函数骨架与典型错误
一个健壮的实现必须同时覆盖顶点顺序、浮点误差、退化边、空/单点输入。下面这个片段能跑通大多数场景,但注意它不处理自相交多边形(那已不是凸多边形):
bool pointInConvexPolygon(const std::vector<std::pair<double,double>>& poly,
double px, double py, double eps = 1e-9) {
if (poly.size() < 3) return false;
int n = poly.size();
bool has_pos = false, has_neg = false;
for (int i = 0; i < n; ++i) {
auto& a = poly[i];
auto& b = poly[(i+1)%n];
double cross = (b.first - a.first)*(py - a.second) - (b.second - a.second)*(px - a.first);
if (cross > eps) has_pos = true;
else if (cross < -eps) has_neg = true;
if (has_pos && has_neg) return false; // 异号,必在外
}
return true; // 同号或全零(含边界)
}
典型错误包括:忘记取模 (i+1)%n 导致最后一条边丢失;用 int 存坐标引发截断;未检查 poly.empty() 导致 poly[0] 越界;把 cross > 0 写成 >= 0 导致边界点被排除。
真正难的不是算法本身,而是如何让这段逻辑在嵌入式设备上不崩、在并发场景下不因浮点环境差异翻车、以及当美术导出的“凸多边形”其实带锯齿顶点时怎么优雅降级——这些细节比叉积公式重要得多。


















