判断点是否在多边形内

有关"弧长法"的介绍:"弧长法要求多边形是有向多边形,一般规定沿多边形的正向,边的左侧为多边形的内侧域.以被测点为圆心作单位圆,将全部有向边向单位圆作径向投影,并计算其中单位圆上弧长的代数和,若代数和为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,这样便可以完全使用整数进行计算,不必考虑顶点的顺序,逆时针和顺时针都可以处理,只是最后的代数和符号不同而已.


以上是关于改进弧长法的算法,算法中有一点不明确,总结一下算法:

1、待检测点P作为坐标原点进行坐标变换。

2、按照顺(逆)时针判断每一条边的两个顶点所在相位区,

 (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,弧长和加π.最后对算出的代数和和上述的情况一样判断即可.

对于在同一相位区的两个顶点,则不作操作。在每次计算向量差乘时判断差乘结果是否等于原点到两个顶点距离的乘机的负数,若是,则p在边上。


贴上别人写的代码:

#include <stdio.h>
#include <iostream>
using namespace std;
#define M 105
struct 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;
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值