如何判断一个点是否在多边形内

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博客

 

posted @   taohuaxiaochunfeng  阅读(2192)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示