核心思路
从点向 ± xy方向发射四条射线,从相交次数判断点与多边形关系,理论上只要有一条射线相交奇数次(n%2==1)即可判定点在多边形内,此方法可避免凹凸多边形造成的判断错误。
相交判断
根据参数方程判断相交(好像高三的知识,推到忘了23333…想起来了再补充)。
/**
// 判断两条线段是否相交
* (x1,y1,x2,y2)(x3,y3,x4,y4)
* @param {*} x1 //point1
* @param {*} y1 //point1
* @param {*} x2 //point2
* @param {*} y2 //point2
* @param {*} x3 //point3
* @param {*} y3 //point3
* @param {*} x4 //point4
* @param {*} y4 //point4
* @returns
*/
function segmentsIntersect(x1, y1, x2, y2, x3, y3, x4, y4) {
var ua, ub, denom, intersectX, intersectY;
denom = (x4 - x3) * (y2 - y1) - (y4 - y3) * (x2 - x1);
if (denom === 0) {
return false; // Lines are parallel
}
ua = ((y4 - y3) * (x1 - x3) - (x4 - x3) * (y1 - y3)) / denom;
ub = ((y2 - y1) * (x1 - x3) - (x2 - x1) * (y1 - y3)) / denom;
if (ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1) {
// Intersection point
// intersectX = x1 + ua * (x2 - x1);
// intersectY = y1 + ua * (y2 - y1);
// return [intersectX, intersectY];
return true
}
return false;
}
export default segmentsIntersect
计算是否再多边形内
使用滚筒循环遍历所有边 统计相交次数(边和射线看作两线段)
/**
* 这里 x , y 为点,captureVec3为一个Vector3({x,y,z})
* max min 分别为 {x:999999,y:999999}{x:0,y:0}
* 根据情况自行给定max,min边界范围
*/
// 计算范围内的点
const isInArea = (x, y, max, min, captureVec3) => {
let oddIntersections = [0, 0, 0, 0]; // 记录每个方向的交点个数→←↑↓
for (let i = 0; i < 4; i++) {
let [x1, y1] = [x, y]; // 起点为给定点
let [x2, y2] = getEndPoint(x, y, max, min, i); // 获取当前方向上的终点
// 遍历多边形的边,计算交点个数
for (let k = 0, j = captureVec3.length - 1; k < captureVec3.length; j = k++) {
let x3 = captureVec3[k].x, y3 = captureVec3[k].y
let x4 = captureVec3[(j) % captureVec3.length].x,
y4 = captureVec3[(j) % captureVec3.length].y
let isIntersect = segmentsIntersect(x1, y1, x2, y2, x3, y3, x4, y4)
if (isIntersect) oddIntersections[i]++
}
}
for (let i = 0; i < 4; i++) {
let v = oddIntersections[i]
if (v % 2 == 0) {
return false
}
}
return true
}
// 获取边界值
const getEndPoint = (x, y, max, min, i) => {
let _x = x, _min_x = min.x, _max_x = max.x
let _y = y, _min_y = min.y, _max_y = max.y
let result = null
switch (i) {
case 0:
result = [_max_x, _y]
break;
case 1:
result = [_min_x, _y]
break;
case 2:
result = [_x, _max_y]
break;
case 3:
result = [_x, _min_y]
break;
}
return result
}
改进
其实只要有一条射线交点是奇数就够了,但我实际操作时出现了问题,暂时还没找到在哪…🤣,这个方法重复了四遍。