大家好,我是前端丁修,今天我们来学习如何判断一个点是否在凸多边形内。这是图形处理中经常遇到的问题,也是面试中的常见问题。
首先,我们讨论的是凸多边形。什么是凸多边形呢?简单来说,就是所有内角都小于 180 度的多边形,看起来没有"凹进去"的部分。虽然本文标题是关于凸多边形的判断,但其中的引射线法实际上对任意多边形(包括凹多边形)都适用。面积法和夹角法则主要适用于凸多边形。
接下来,我会介绍几种常用的方法,包括面积和判别法、夹角和判别法以及引射线法。
第一种方法是用面积来判断:如果点在多边形内部,那么这个点和多边形的每条边组成的小三角形的面积之和,应该等于整个多边形的面积。
三角形面积可以通过向量叉积计算:

计算多边形的总面积。可以通过以下方式计算:
计算点与多边形各顶点形成的三角形面积之和。
比较总面积与三角形面积之和,如果相等,则点在多边形内,这里要考虑容差。
typescript// 计算三角形面积的函数
function triangleArea(p1: [number, number], p2: [number, number], p3: [number, number]): number {
const [x1, y1] = p1;
const [x2, y2] = p2;
const [x3, y3] = p3;
return Math.abs((x2 - x1) * (y3 - y1) - (x3 - x1) * (y2 - y1)) / 2;
}
function isPointInsidePolygonArea(polygon: [number, number][], point: [number, number]): boolean {
let totalArea = 0;
let sumAreas = 0;
const n = polygon.length;
// 计算多边形总面积
for (let i = 0; i < n; i++) {
const j = (i + 1) % n;
totalArea += triangleArea(polygon[0], polygon[i], polygon[j]);
}
// 计算点与各边构成的三角形面积和
for (let i = 0; i < n; i++) {
const j = (i + 1) % n;
sumAreas += triangleArea(point, polygon[i], polygon[j]);
}
// 考虑JS浮点数精度问题
return Math.abs(totalArea - sumAreas) < 1e-10;
}
第二种方法是看夹角。如果一个点在多边形内部,那么这个点与多边形各顶点的向量之间的夹角之和应该等于 360 度。这就是夹角和判别法的核心思想。

typescript// 计算两个向量之间的夹角
// v1, v2: 两个二维向量
// 返回值: 两个向量的夹角(弧度)
function vectorAngle(v1: [number, number], v2: [number, number]): number {
// 计算点积
const dotProduct = v1[0] * v2[0] + v1[1] * v2[1];
// 计算叉积
const crossProduct = v1[0] * v2[1] - v1[1] * v2[0];
// 使用atan2计算夹角,范围为[-π,π]
return Math.atan2(crossProduct, dotProduct);
}
// 使用夹角和判别法判断点是否在多边形内
// polygon: 多边形顶点数组
// point: 待判断的点
// 返回值: 点是否在多边形内
function isPointInsidePolygonAngle(polygon: [number, number][], point: [number, number]): boolean {
let angleSum = 0; // 夹角和
const n = polygon.length; // 多边形顶点数
// 遍历多边形的每条边
for (let i = 0; i < n; i++) {
// 计算point到当前顶点的向量
const v1: [number, number] = [polygon[i][0] - point[0], polygon[i][1] - point[1]];
// 计算point到下一个顶点的向量
const v2: [number, number] = [
polygon[(i + 1) % n][0] - point[0],
polygon[(i + 1) % n][1] - point[1],
];
// 累加两个向量的夹角
angleSum += vectorAngle(v1, v2);
}
// 判断夹角和是否接近2π(考虑浮点数误差)
return Math.abs(Math.abs(angleSum) - 2 * Math.PI) < 1e-10;
}
最后一种方法原理是这样的:从点画一条射线线,看这条线和多边形边界相交多少次。如果是奇数次,说明点在内部;偶数次说明在外部。

typescript// 使用射线法判断点是否在多边形内
// polygon: 多边形顶点数组
// point: 待判断的点[x,y]
// 返回值: 点是否在多边形内
function isPointInsidePolygonRay(polygon: [number, number][], point: [number, number]): boolean {
// 获取待判断点的坐标
const [x, y] = point;
// inside表示点是否在多边形内
let inside = false;
// 遍历多边形的每条边
// j表示当前边的起点索引,i表示终点索引
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
// 获取边的两个端点坐标
const [xi, yi] = polygon[i];
const [xj, yj] = polygon[j];
// 判断射线是否与当前边相交
// 条件1:点的y坐标在边的两个端点y坐标之间
// 条件2:点的x坐标小于射线与边的交点的x坐标
if (yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi) {
// 如果相交则改变inside的值
inside = !inside;
}
}
return inside;
}
这三种方法各有优缺点:
在实际开发中,要注意这些坑:
以上介绍了三种判断点是否在凸多边形内的方法:面积和判别法、夹角和判别法以及引射线法。每种方法都有其优缺点,大家可以根据实际需求选择合适的方法。希望这篇文章对你有所帮助!
本文作者:丁修
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!