半平面交,求解多邊形內核


關於求多邊形內核的算法

什么是多邊形的內核?

它是平面簡單多邊形的核是該多邊形內部的一個點集,該點集中任意一點與多邊形邊界上一點的連線都處於這個多邊形內部。就是一個在一個房子里面放一個攝像 頭,能將所有的地方監視到的放攝像頭的地點的集合即為多邊形的核。

 

         

如上圖,第一個圖是有內核的,比如那個黑點,而第二個圖就不存在內核了,無論點在哪里,總有地區是看不到的。

 

那么,如何求得這個內核區間呢?通常的算法是用兩點的直線去不斷切割多邊形,切割到最后剩下的,就是內核區間了。

我們都知道一條直線可以將平面切割成兩個區域,假設直線方程為

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;
}

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM