半平面交,求解多边形内核

关于求多边形内核的算法

什么是多边形的内核?

它是平面简单多边形的核是该多边形内部的一个点集,该点集中任意一点与多边形边界上一点的连线都处于这个多边形内部。就是一个在一个房子里面放一个摄像 头,能将所有的地方监视到的放摄像头的地点的集合即为多边形的核。

 

         

如上图,第一个图是有内核的,比如那个黑点,而第二个图就不存在内核了,无论点在哪里,总有地区是看不到的。

 

那么,如何求得这个内核区间呢?通常的算法是用两点的直线去不断切割多边形,切割到最后剩下的,就是内核区间了。

我们都知道一条直线可以将平面切割成两个区域,假设直线方程为

ax+by+c==0,那么,两个平面可分别表示成ax+by+c>=0 和 ax+by+c<0

 

具体如何用程序实现直线对多边形的切割呢?

流程是这样的:

1、          用一个顺时针或者逆时针的顺序,将最初的多边形的点集储存起来。

2、          按顺序取连续的两个点组成一条直线,用这条直线来切割原先的多边形

我首先假设点是顺时针储存的,如图:

此时,多边形的点集是{1,2,3,4,5,6,7,8,9,10}

 

取点1,和点2组成直线ax+by+c==0,这时候,将点集中的点一次带入方程ax+by+c,得到的值都将会是大于等于0的,说明所有的点都在该直线的同一侧,继续保持点集不变

 

取点2和点3组成直线,同样,将点集中的点依次带入方程ax+by+c中,此时,4和5两个点的结果是小于0的,而其他的点的值依旧是大于等于0,这时候说明4和5两个点被切割出了该多边形,于是现在点集只剩下{1,2,3,6,7,8,9,10,X},(X是直线23和直线56的交点)

 

依次类推,一直执行到点10和点1,那么内核的集合就得到了。

 

值得说明的是,这个例子的图形比较特殊,全是直角,如果图形比较随意,那么当某一个点被断定在多边形区间之外的时候,我们还应该考虑它和它相邻的两个点各自组成的直线和ax+by+c有没有交点,有交点的话,更新的点集中还应该加上这些交点,比如例子中执行完点2和点3组成的直线后,点集是{1,2,3,6,7,8,X},其中3和X就是这样的结果

 

还有,为什么将所有的点依次执行一遍,然后取剩下的某一边的点构成新的点集就够了呢?答案是,点是顺时针或者逆时针给出的~~~

 

下面给一个实例,poj 3335

 

View Code
#include <iostream>
#include <algorithm>
#include <cmath>
#include <stdio.h>
using namespace std;
#define exp 1e-10

struct node
{
double x;
double y;
};

node point[105];//记录最开始的多边形
node q[105]; //临时保存新切割的多边形
node p[105]; //保存新切割出的多边形
int n,m;//n的原先的点数,m是新切割出的多边形的点数
double a,b,c;

void getline(node x,node y) //获取直线ax+by+c==0
{
a=y.y-x.y;
b=x.x-y.x;
c=y.x*x.y-x.x*y.y;
}

node intersect(node x,node y) //获取直线ax+by+c==0 和点x和y所连直线的交点
{
double u=fabs(a*x.x+b*x.y+c);
double v=fabs(a*y.x+b*y.y+c);
node ans;
ans.x=(x.x*v+y.x*u)/(u+v);
ans.y=(x.y*v+y.y*u)/(u+v);
return ans;
}

void cut() //用直线ax+by+c==0切割多边形
{
int cutm=0,i;
for(i=1;i<=m;i++)
{
if(a*p[i].x+b*p[i].y+c>=0) //题目是顺时钟给出点的
{ //所以一个点在直线右边的话,那么带入值就会大于等于0
q[++cutm]=p[i]; //说明这个点还在切割后的多边形内,将其保留
}
else
{
if(a*p[i-1].x+b*p[i-1].y+c>0) //该点不在多边形内,但是它和它相邻的点构成直线与
{ //ax+by+c==0所构成的交点可能在新切割出的多边形内,
q[++cutm]=intersect(p[i-1],p[i]); //所以保留交点
}
if(a*p[i+1].x+b*p[i+1].y+c>0)
{
q[++cutm]=intersect(p[i+1],p[i]);
}
}
}
for(i=1;i<=cutm;i++)
{
p[i]=q[i];
}
p[cutm+1]=q[1];
p[0]=q[cutm];
m=cutm;
}

void solve()
{
int i;
for(i=1;i<=n;i++)
{
p[i]=point[i];
}
point[n+1]=point[1];
p[n+1]=p[1];
p[0]=p[n];
m=n;
for(i=1;i<=n;i++)
{
getline(point[i],point[i+1]); //根据point[i]和point[i+1]确定直线ax+by+c==0
cut(); //用直线ax+by+c==0切割多边形
}
}

int main()
{
int cas,i;
freopen("D:\\in.txt","r",stdin);
scanf("%d",&cas);
while(cas--)
{
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%lf%lf",&point[i].x,&point[i].y);
}
solve();
if(m==0)
{
printf("NO\n");
}
else
{
printf("YES\n");
}
}
return 0;
}

 

 

转载于:https://www.cnblogs.com/ka200812/archive/2012/01/20/2328316.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值