判断点在多边形内还是外部

来源:互联网 发布:手机软件无法连接网络 编辑:程序博客网 时间:2024/05/01 11:03

改进的弧长法:摘自网上


有关"弧长法"的介绍:"弧长法要求多边形是有向多边形,一般规定沿多边形的正向,边的左侧为多边形的内侧域.以被测点为圆心作单位圆,将全部有向边向单位圆作径向投影,并计算其中单位圆上弧长的代数和,若代数和为0,则点在多边形外部;若代数和为2π,则点在多边形内部;若代数和为π,则点在多边形上."
根据上面的介绍,其实弧长法就是转角法,但它的改进方法比较比较厉害:将坐标原点平移到被测点P,这个新坐标系将平面划分为4个象限,对每个多边形顶点P,只考虑其所在的象限,然后按邻接顺序访问多边形的各个顶点P,分析P和P[i+1],有下列三种情况:
(1) P[i+1]在P的下一象限,此时弧长和加π/2;
(2)P[i+1]在P的上一象限,此时弧长和减π/2;
(3)P[i+1]在P的相对象限,首先计算f=p[i+1].y*p.x-p[i+1].x*p.y(叉积),若f=0,则点在多边形上;若f<0,弧长和减π;若f>0,弧长和加π.最后对算出的代数和和上述的情况一样判断即可.
实现的时候还有两点要注意:
1> 若P的某个坐标为0时,一律当正号处理;
2> 若被测点和多边形的顶点重合时要特殊处理.
还有一个问题那就是当多边形的某条边在坐标轴上而且两个顶点分别在原点的两侧时会出错,如边(3,0)……>(-3,0),按以上的处理,象限分别是第一和第二,这样会使代数和加π/2,有可能导致最后结果是被测点在多边形外,而实际上被测点是在多边形上(该边穿过该点).
对于这点,处理办法是:每次计算P和P[i+1]时,就计算叉积和点积,判断该点是否在该边上,是则判断结束,否则继续上述过程,这样牺牲了时间,但保证了正确性.
具体实现的时候,由于只需知道当前点和上一点的象限位置,所以附加空间只需O(1).实现的时候可以把上述的"π/2"改成1,"π"改成2,这样便可以完全使用整数进行计算,不必考虑顶点的顺序,逆时针和顺时针都可以处理,只是最后的代数和符号不同而已.


优点:实现简单,具有很高的精度,只做乘法和减法。


#include <stdio.h>#include <iostream>using namespace std;#define M 105struct node {     double x, y; }p[M];int inpolygon(node t, int n){    int i, t1, t2, sum, f;    for(i = 0; i <= n; i ++) p[i].x -= t.x, p[i].y -= t.y;    t1 = p[0].x>=0 ?(p[0].y>=0?0:3) :(p[0].y>=0?1:2);       for(sum = 0, i = 1; i <= n; i ++)    {        if(!p[i].x && !p[i].y) break;                    f = p[i].y * p[i-1].x - p[i].x * p[i-1].y;           if(!f && p[i-1].x*p[i].x <= 0 && p[i-1].y*p[i].y <= 0) break;          t2 = p[i].x>=0 ?(p[i].y>=0?0:3) :(p[i].y>=0?1:2);           if(t2 ==(t1 + 1) % 4) sum += 1;                 else if(t2 ==(t1 + 3) % 4) sum -= 1;           else if(t2 ==(t1 + 2) % 4)                      {                                                         if(f > 0) sum += 2;             else sum -= 2;        }        t1 = t2;    }    if(i<=n || sum) return 1;     return 0;}int main(){    int n, i;    node t;    while(scanf("%d", &n) && n)    {        for(i = 0; i < n; i ++)            scanf("%lf%lf", &p[i].x, &p[i].y);        p[n] = p[0];        scanf("%lf%lf", &t.x, &t.y);        if(inpolygon(t, n)) printf("inside\n");        else printf("outside\n");    }    return 0;}


原创粉丝点击