平面 $ax+by+cz+d=0$ 穿过凸多面体当且仅当其所有顶点代入方程后结果符号不全相同,即存在正值、负值或零,这是基于凸集被超平面分割的充要条件。

如何快速判断平面是否穿过凸多面体内部
直接结论:平面 ax + by + cz + d = 0 穿过凸多面体当且仅当其所有顶点代入该平面方程后,结果符号不全相同(即存在正、负值,或含零)。这是最可靠且高效的判定方式。
原理很简单:凸多面体是凸集,平面将空间严格分为正半空间(ax+by+cz+d > 0)、负半空间()和面上(<code>= 0)。若所有顶点都在同一侧或面上,整个多面体就完全位于该侧;只要至少一个顶点在正侧、另一个在负侧,线段(进而整个凸包)必然横跨平面——即被“切为两半”。
注意:被切割为两半在几何上不要求体积均等,只要求交集非空且补集也非空(即平面既不远离也不贴合/包含整个多面体)。
为什么不能只检查顶点到平面的距离符号
可以检查,但必须用带符号的有向距离(即直接计算 ax+by+cz+d),而非绝对值距离。常见错误包括:
立即学习“C++免费学习笔记(深入)”;
- 误用
abs(ax+by+cz+d)—— 符号信息丢失,无法区分两侧 - 未归一化法向量就比较数值大小 —— 距离值无物理意义,但符号永远有效
- 对浮点数用
== 0判断是否在平面上 —— 应改用fabs(value) ,其中 <code>eps取1e-9或根据坐标量级调整
关键点:平面方程系数未归一化不影响符号判断,所以无需提前除以 sqrt(a²+b²+c²),省掉开方运算更高效。
代码里怎么写才不容易出错
核心逻辑几行即可,重点在鲁棒性处理:
bool isCutByPlane(const std::vector<Vec3>& vertices,
double a, double b, double c, double d,
double eps = 1e-9) {
bool has_positive = false;
bool has_negative = false;
for (const auto& v : vertices) {
double val = a * v.x + b * v.y + c * v.z + d;
if (val > eps) has_positive = true;
else if (val < -eps) has_negative = true;
// 忽略 |val| <= eps 的情况(顶点近似在平面上,不影响穿越判定)
if (has_positive && has_negative) return true;
}
return false;
}说明:
- 用
eps处理浮点误差,避免把接近平面的顶点误判为严格分侧 - 提前返回(
if (has_positive && has_negative) return true;)提升平均性能 - 若所有顶点都满足
|val| ,说明整个多面体退化到平面附近(如扁平化或共面),此时严格来说未被“切割为两半”,函数返回 <code>false符合直觉
哪些特殊情况需要额外留意
实际工程中容易忽略但影响逻辑正确性的点:
- 平面法向量为零向量(
a==b==c==0)—— 此时方程无效,必须在调用前校验,否则val恒为d,导致误判 - 凸多面体顶点数极少(如四面体只有 4 个点)—— 仍适用上述方法,无需特例处理
- 多面体含共面顶点或退化面 —— 不影响顶点符号法,因为凸包定义只依赖顶点集合
- 需要进一步求交(而不仅是判断)时,此方法只是前置快筛;真正剖分需调用 ClipConvexPolyhedron 或 Sutherland–Hodgman 类算法
最常被跳过的一步:没验证输入平面是否合法。一旦 a,b,c 全为零,后续所有符号判断都失去几何意义——这个检查比你想的更关键。


















