如何获取平面3D多边形内部确定点?质心方法存在缺陷
平面3D凹多边形内部点的获取方法
你当前采用的顶点坐标平均法,对凸多边形能稳定得到内部点,但凹多边形的顶点平均点(甚至质心)确实可能落在多边形外部,这里给你几个实用的解决思路:
思路1:三角剖分加权平均法
这是最稳妥且易实现的方案,核心是把凹多边形拆成多个凸三角形,再通过子区域的加权平均得到内部点:
- 步骤1:多边形三角剖分:用耳切法(Ear Clipping)把凹多边形分割成若干个无重叠的三角形,所有三角形的并集就是原多边形区域。每个三角形都是凸的,其顶点平均点或质心必然在内部。
- 步骤2:加权计算整体中心点:对每个三角形计算质心(三个顶点坐标的平均值),再以三角形的面积为权重,对所有质心做加权平均,最终得到的点既在原多边形内部,又能接近几何中心。
对应的JS伪代码示例:
// 三角剖分(耳切法核心逻辑) function triangulatePolygon(boundary) { const triangles = []; const vertices = [...boundary]; // 循环直到剩下3个顶点组成最后一个三角形 while (vertices.length > 3) { let earFound = false; for (let i = 0; i < vertices.length; i++) { const prev = vertices[(i - 1 + vertices.length) % vertices.length]; const curr = vertices[i]; const next = vertices[(i + 1) % vertices.length]; // 判断当前顶点是否为凸点 const isConvex = isConvexVertex(prev.position, curr.position, next.position); if (!isConvex) continue; // 判断三角形内是否包含其他顶点 let hasInnerPoint = false; for (const v of vertices) { if (v === prev || v === curr || v === next) continue; if (pointInTriangle(v.position, prev.position, curr.position, next.position)) { hasInnerPoint = true; break; } } if (!hasInnerPoint) { triangles.push([prev, curr, next]); vertices.splice(i, 1); earFound = true; break; } } if (!earFound) break; // 处理退化情况 } // 添加最后一个三角形 if (vertices.length === 3) triangles.push(vertices); return triangles; } // 计算加权平均的内部点 function getWeightedInnerPoint(triangles) { let totalArea = 0; let sumX = 0, sumY = 0, sumZ = 0; triangles.forEach(tri => { const [a, b, c] = tri.map(v => v.position); // 计算三角形面积(平面多边形的面积用叉积模长的一半) const cross = [ b[1] * c[2] - b[2] * c[1], b[2] * c[0] - b[0] * c[2], b[0] * c[1] - b[1] * c[0] ]; const area = Math.hypot(...cross) / 2; // 三角形质心为三个顶点的平均 const centroid = [(a[0]+b[0]+c[0])/3, (a[1]+b[1]+c[1])/3, (a[2]+b[2]+c[2])/3]; // 加权累加 sumX += centroid[0] * area; sumY += centroid[1] * area; sumZ += centroid[2] * area; totalArea += area; }); return [sumX / totalArea, sumY / totalArea, sumZ / totalArea]; } // 辅助函数:判断顶点是否为凸点(右手坐标系下,叉积z分量为正表示凸) function isConvexVertex(p1, p2, p3) { const v1 = [p2[0]-p1[0], p2[1]-p1[1], p2[2]-p1[2]]; const v2 = [p3[0]-p2[0], p3[1]-p2[1], p3[2]-p2[2]]; const crossZ = v1[0]*v2[1] - v1[1]*v2[0]; // 平面内的叉积z分量 return crossZ > 0; // 可根据多边形顶点顺序调整符号 } // 辅助函数:判断点是否在三角形内部( barycentric坐标法) function pointInTriangle(p, a, b, c) { const v0 = [c[0]-a[0], c[1]-a[1], c[2]-a[2]]; const v1 = [b[0]-a[0], b[1]-a[1], b[2]-a[2]]; const v2 = [p[0]-a[0], p[1]-a[1], p[2]-a[2]]; const dot00 = v0[0]*v0[0] + v0[1]*v0[1] + v0[2]*v0[2]; const dot01 = v0[0]*v1[0] + v0[1]*v1[1] + v0[2]*v1[2]; const dot02 = v0[0]*v2[0] + v0[1]*v2[1] + v0[2]*v2[2]; const dot11 = v1[0]*v1[0] + v1[1]*v1[1] + v1[2]*v1[2]; const dot12 = v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2]; const denom = dot00 * dot11 - dot01 * dot01; const u = (dot11 * dot02 - dot01 * dot12) / denom; const v = (dot00 * dot12 - dot01 * dot02) / denom; return (u >= 0) && (v >= 0) && (u + v <= 1); }
思路2:射线法修正+中心优化
如果不想做复杂的三角剖分,可以用这种轻量方案:
- 步骤1:判断初始点是否在内部:用射线法,从顶点平均点出发引一条任意方向的射线,统计射线与多边形边的交点数,奇数则在内部,偶数则在外部。
- 步骤2:将外部点拉回内部:如果初始点在外部,沿着多边形某条边的内法线方向移动,直到射线法判断为内部点,得到一个初始内部点。
- 步骤3:优化到中间位置:计算该点到所有多边形边的距离,找到最小距离(即到最近边的距离),然后往远离该边的方向移动一段距离(比如最小距离的一半),重复几次后就能得到更靠近中间的点。
思路3:内核区域中心点法
凹多边形的内核是所有能看到多边形全部边界的点的集合,内核内的点必然在多边形内部:
- 用Sutherland-Hodgman算法,依次对多边形的每条边对应的半平面求交集,最终得到内核区域(可能是凸多边形或空集,空集说明是自交多边形)。
- 取内核区域的顶点平均点,这个点绝对在原多边形内部,适合对内部点有严格要求的场景。
内容的提问来源于stack exchange,提问作者pcace
相关产品推荐
相关产品推荐

