如何判断一个点是否在多边形内
1、概述
判断一个点是否在多边形内有几种不同的思路,相应的方法有很多:
- 射线法:从判断点向某个统一方向作射线,依交点个数的奇偶判断;
- 转角法:按照多边形顶点逆时针顺序,根据顶点和判断点连线的方向正负(设定角度逆时针为正)求和判断;
- 夹角和法:求判断点与所有边的夹角和,等于360度则在多边形内部。
- 面积和法:求判断点与多边形边组成的三角形面积和,等于多边形面积则点在多边形内部
因此,本文是通过射线法来进行判断点是否在多边形内的,主要用于无人机判断有没有进入准飞行区和禁飞区。
2、射线法的实现
射线法就是以判断点开始,向右(或向左)的水平方向作一射线,计算该射线与多边形每条边的交点个数,如果交点个数为奇数,则点位于多边形内,偶数则在多边形外。
网上大部分的射线法都是以向右或者向左作一射线的方法,但是都存在一个没有考虑的情况(类似下图,如果判断点所作的射线与多边形一条边界重合了,该怎么判断):
为此,在使用C++编写代码时,对于射线的方向进行了改进,不再采用单一的射线方向,而是给它一个数组的形式来存放不同斜率的射线方向。
整体代码如下:(可直接运行)
1 #include <iostream> 2 #include <string> 3 #include <cmath> 4 5 #define PI 3.1415926 6 #define EXP 1e-6 // 截取精度 7 #define EXP1 1e-4 // 校验精度 8 9 10 11 int main() { 12 /* start_point表示判断点,point_all代表多边形点的二维数组 */ 13 double start_point[] = { 0.0, 0.0 }; 14 15 double point_all[7][2] = { 16 { 1.0, 1.0 }, 17 { 0.0, 3.0 }, 18 { 1.0, 2.0 }, 19 { 1.5, 3.0 }, 20 { 2.0, 2.0 }, 21 { 3.0, 4.0 }, 22 { 3.0, 0.0 } 23 }; 24 25 const int n = 7;// +1; 26 const int double_n = 2 * n; 27 double All_point_xy[double_n + 2] = {}; 28 29 for (int i = 0; i < n; i++) { 30 for (int j = 0; j < 2; j++) { 31 All_point_xy[2 * i + j] = { point_all[i][j] }; // 想将接收的多边形点放到一个一维数组中 32 } 33 } 34 35 All_point_xy[double_n] = All_point_xy[0]; 36 All_point_xy[double_n + 1] = All_point_xy[1]; // 为方便多边形边界判断,再次将开头点放到末尾 37 38 printf("点元素集合(末尾重复开头):\n"); // 验证 39 for (int i = 0; i < double_n + 2; i++) { 40 printf("%f , ", All_point_xy[i]); 41 } 42 43 44 int counter = 0; 45 double point_x; // 交点x坐标 46 double point_y; // 交点y坐标 47 double theta = 45; 48 double theta_arr[11] = { 45, 60, 256, 128, 64, 32, 16, 8, 4, 2, 1 }; // 不同角度的射线方向 49 double t, b; 50 double Deg2rad = 180 / PI; // C++ 中三角函数计算,运用的是弧度 51 double tan_theta = tan(theta / Deg2rad); // 转换至弧度,进行正切值计算 52 printf("\n验证正切值:%lf", tan_theta); // 验证转换关系,以及求解是否正确 53 54 bool Coincidence_judgment = false; // 重合判段标识 55 bool Coincidence_judgment02 = false; // 为更好地跳出循环,进行了两次重合判断 56 double slope_vertex_start[n+1] = {}; 57 58 for (int i = 0; i < (std::end(theta_arr) - std::begin(theta_arr)); i++) { // 获得数组的长度方法 59 int now_theta = theta_arr[i]; 60 double slope_vertex = tan(now_theta / Deg2rad); // 记录顶点所处斜率 61 slope_vertex_start[0] = tan(now_theta / Deg2rad); 62 for (int j = 0; j <= (n - 1); j++) { 63 double point_start_x = All_point_xy[2 * j]; 64 double point_start_y = All_point_xy[2 * j + 1]; 65 double point_end_x = All_point_xy[2 * j + 2]; 66 double point_end_y = All_point_xy[2 * j + 3]; 67 double err_x = point_end_x - point_start_x; 68 if (err_x == 0) { 69 err_x = EXP; 70 } 71 72 double slope_half_line = tan(theta_arr[i] / Deg2rad); // 射线斜率 73 double slope_line_segment = (point_end_y - point_start_y) / err_x; // 两点形成直线的斜率 74 double err_0 = abs(slope_half_line - slope_line_segment); // 由于无法绝对相等,设计了精度差 75 76 slope_vertex_start[j+1] = slope_line_segment; 77 //if (j == 0) { 78 // slope_vertex_start = slope_line_segment; // 存储首个顺序边界的斜率,防止被刷掉 79 //} 80 81 82 printf("\n%d", j); 83 if (err_0 < EXP) { // 斜率相等,表示射线与边界平行或重合,再次求解两点与判断点之间的斜率,如果两个相等,则表示重合 84 double slope2start = (point_start_y - start_point[1]) / (point_start_x - start_point[0]); 85 double slope2end = (point_end_y - start_point[1]) / (point_end_x - start_point[0]); 86 double err_1 = abs(slope2end - slope2start); 87 if (err_1 < EXP) { 88 Coincidence_judgment = true; // 重合 89 Coincidence_judgment02 = true; 90 } 91 } 92 if (Coincidence_judgment == true) { 93 Coincidence_judgment = false; 94 break; // 重合则表示该斜率不可用,跳出整个循环 95 } 96 // 如果不重合,则利用两个直线求交点的公式求解交点坐标 97 b = start_point[1] - slope_half_line * start_point[0]; 98 t = (slope_half_line * point_start_x + b - point_start_y) / (point_end_y - point_start_y - slope_half_line * (point_end_x - point_start_x)); 99 point_x = (point_end_x - point_start_x) * t + point_start_x; 100 point_y = (point_end_y - point_start_y) * t + point_start_y; 101 102 printf("\n%d", now_theta); 103 printf("\n求解交点坐标:%lf, %lf ", point_x, point_y); 104 105 double slope_vertex = slope_vertex_start[j]; 106 107 if (((abs(point_x - point_start_x) < EXP) && (abs(point_y - point_start_y) < EXP)) || ((abs(point_x - point_end_x) < EXP) && (abs(point_y - point_end_y)) < EXP)) { 108 109 110 111 if (((slope_half_line < slope_vertex ) && (slope_half_line < slope_line_segment)) || ((slope_half_line > slope_vertex) && (slope_half_line > slope_line_segment))) { 112 113 continue; 114 } 115 116 117 if (j == n - 1) { 118 if (((slope_half_line < slope_vertex_start[1]) && (slope_half_line > slope_line_segment)) || ((slope_half_line > slope_vertex_start[1]) && (slope_half_line < slope_line_segment))) { 119 continue; 120 } 121 } 122 if ((abs(point_x - point_start_x) < EXP)) { 123 if (start_point[0] < point_x) { // 由于上面求解交点坐标方式是以直线形式进行的,将其转换至一个方向表示射线形式 124 printf("\n可用交点:%d,%lf, %lf", j, point_x, point_y); 125 counter++; 126 } 127 } 128 129 } 130 else if ((point_x > point_start_x) && (point_x > point_end_x)) { 131 printf("x坐标上限均大于两端点,故舍弃并跳出\n "); 132 continue; 133 } 134 else if ((point_x < point_start_x) && (point_x < point_end_x)) { 135 printf("x坐标上限均小于两端点,故舍弃并跳出\n "); 136 continue; 137 } 138 else if ((point_y > point_start_y) && (point_y > point_end_y)) { 139 printf("y坐标上限均大于两端点,故舍弃并跳出\n "); 140 continue; 141 } 142 else if ((point_y < point_start_y) && (point_y < point_end_y)) { 143 printf("y坐标上限均小于两端点,故舍弃并跳出\n "); 144 continue; 145 } 146 else { 147 148 printf("正常值,触发 counter++;\n "); 149 if (start_point[0] < point_x) { // 由于上面求解交点坐标方式是以直线形式进行的,将其转换至一个方向表示射线形式 150 printf("\n可用交点:%d,%lf, %lf", j, point_x, point_y); 151 counter++; 152 } 153 154 } 155 156 157 } 158 if (Coincidence_judgment02 == true) { 159 Coincidence_judgment02 = false; 160 counter = 0; 161 continue; 162 } 163 else { 164 break; 165 } 166 167 } 168 169 printf("\n交点个数 = %d", counter); 170 171 if (counter % 2 == 1) { 172 printf("\n点在多边形的内部"); 173 } 174 else { 175 printf("\n点在多边形的外部"); 176 } 177 return 0; 178 }
3、补充知识点
C++实现求两条直线的交点
C++实现求两条直线的交点,以及已知直线外一点求垂足_c++求两直线交点_只道寻常zero的博客-CSDN博客
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)