interface Point { x: number; y: number; } interface RectConfig { x: number; y: number; width: number; height: number; } /** * @param point {{x,y}} 需要判断的点 * @param coordinates {{x,y}[]} 多边形点坐标的数组,为保证图形能够闭合,起点和终点必须相等。 * 比如三角形需要四个点表示,第一个点和最后一个点必须相同。 * @param noneZeroMode 对不规则图形进行判断,默认启动none zero mode */ const isInPolyRect = (point: Point, coordinates, noneZeroMode = true) => { const { x } = point; const { y } = point; let crossNum = 0; // 点在线段的左侧数目 let leftCount = 0; // 点在线段的右侧数目 let rightCount = 0; for (let i = 0; i < coordinates.length - 1; i++) { const start = coordinates[i]; const end = coordinates[i + 1]; // 起点、终点斜率不存在的情况 if (start.x === end.x) { // 因为射线向右水平,此处说明不相交 if (x > start.x) continue; // 从左侧贯穿 if (end.y > start.y && y >= start.y && y <= end.y) { leftCount = leftCount + 1; crossNum = crossNum + 1; } // 从右侧贯穿 if (end.y < start.y && y >= end.y && y <= start.y) { rightCount = rightCount + 1; crossNum = crossNum + 1; } continue; } // 斜率存在的情况,计算斜率 const k = (end.y - start.y) / (end.x - start.x); // 交点的x坐标 const x0 = (y - start.y) / k + start.x; // 因为射线向右水平,此处说明不相交 if (x > x0) continue; if (end.x > start.x && x0 >= start.x && x0 <= end.x) { crossNum = crossNum + 1; if (k >= 0) leftCount = leftCount + 1; else rightCount = rightCount + 1; } if (end.x < start.x && x0 >= end.x && x0 <= start.x) { crossNum = crossNum + 1; if (k >= 0) rightCount = rightCount + 1; else leftCount = leftCount + 1; } } return noneZeroMode ? leftCount - rightCount !== 0 : crossNum % 2 === 1; }; /** * 面向数学坐标系编程 */ const isInRect = (point: Point, config: RectConfig) => { const { x, y, width, height } = config; if ( point.x >= x && point.x <= x + width && point.y >= y && point.y <= y + height ) { return true; } return false; }; /** * 调用时:面向直角坐标系编程(正方向为顺时针) * 方法内:面向web坐标系编程(正方向为逆时针) */ const isInSector = (point: Point, sectorArea) => { const { x } = point; const { y } = point; const { originX, originY, r0, r1, startAngle, endAngle } = sectorArea; // 计算直角坐标系角度 const xInCartesian = x - originX; const yInCartesian = y - originY; const rawAngle = Math.atan2(yInCartesian, xInCartesian); // 与x轴正方向的夹角(逆时针) // 直角坐标系 -> web坐标系 const angle = rawAngle > 0 ? 2 * Math.PI - rawAngle : -rawAngle; // 判断是否在扇形区域内 let isInAngle = false; if (startAngle * endAngle < 0) { // 起终角度,直角坐标系存在正负跨0区域,需要分为两个区间进行比对 if (startAngle < 0) { const angleRange1 = [startAngle + Math.PI * 2, Math.PI * 2]; const angleRange2 = [0, endAngle]; isInAngle = (angle >= angleRange1[0] && angle <= angleRange1[1]) || (angle >= angleRange2[0] && angle <= angleRange2[1]); } } else { isInAngle = angle >= startAngle && angle <= endAngle; } const length = Math.sqrt((y - originY) ** 2 + (x - originX) ** 2); const isInPolyRect = length >= r0 && length <= r1; return isInAngle && isInPolyRect; }; export { isInRect, isInPolyRect, isInSector };