多邊形面積交/並
阿新 • • 發佈:2018-12-11
求多邊形面積交,邊逆時針方向給出(否則要逆過來)
求多邊形面積並,用面積和減去交即可
#include <bits/stdc++.h> using namespace std; const int maxn = 300; const double eps = 1e-8; int dcmp(double x){ //精度誤差比較 if(x > eps) return 1; return x < -eps ? -1 : 0; } struct Point{double x, y;}; //點結構體 double cross(Point a,Point b,Point c){return (a.x-c.x)*(b.y-c.y)-(b.x-c.x)*(a.y-c.y);}//叉積 Point intersection(Point a,Point b,Point c,Point d){//傳入四點即兩直線,輸出交點 Point p = a; double t =((a.x-c.x)*(c.y-d.y)-(a.y-c.y)*(c.x-d.x))/((a.x-b.x)*(c.y-d.y)-(a.y-b.y)*(c.x-d.x)); p.x +=(b.x-a.x)*t; p.y +=(b.y-a.y)*t; return p;//輸出交點,證明用數學方法易證 } double PolygonArea(Point p[], int n){//計算多邊形面積,三角剖分 if(n < 3) return 0.0; double s = p[0].y * (p[n - 1].x - p[1].x); p[n] = p[0]; for(int i = 1; i < n; ++ i) s += p[i].y * (p[i - 1].x - p[i + 1].x); return fabs(s * 0.5);//叉乘出來是平行四邊形面積故/2,且順逆方向不定,故取ABS變正 } double CPIA(Point a[], Point b[], int na, int nb){//傳入兩個三角形,求相交部分的凸包 Point p[20], tmp[20]; //複製點集與臨時點集(P其實可以用B來做 int tn, sflag, eflag; //每輪相交凸包的點,叉乘符號 a[na] = a[0], b[nb] = b[0]; //末點用初點複製方便首末點連邊 memcpy(p,b,sizeof(Point)*(nb + 1)); //把B複製到P for(int i=0;i<na&&nb>2;i++){ //掃一次A sflag=dcmp(cross(a[i+1],p[0],a[i])); //取A兩點與B第一點求叉乘符號 for(int j=tn=0;j<nb;j++,sflag=eflag){ //掃一次B,更新TMP,TN是點數 if(sflag>=0)tmp[tn++]=p[j]; //叉乘為正就是B陣列的那個點壓入 eflag=dcmp(cross(a[i+1],p[j+1],a[i]));//求叉乘符號 if((sflag^eflag)==-2) //1異或-1等於-2 tmp[tn++]=intersection(a[i],a[i+1],p[j],p[j+1]);//求交點 } memcpy(p, tmp, sizeof(Point) * tn); //把TMP複製到P nb = tn, p[nb] = p[0];//TN即TMP點數記到NB }//其實該是NP表示P陣列個數,這裡省了個變數就用NB表示,下面第二行做引數而已 if(nb < 3) return 0.0; //相交部分凸包不夠三個點,面積就是0 return PolygonArea(p, nb); //求出相交凸包部分的面積 } double SPIA(Point a[], Point b[], int na, int nb){//傳入兩個多邊形的點 int i,j; //迴圈變數 Point t1[4],t2[4]; //其實T13與T23沒用上 double res=0,num1,num2; //答案初始化,及叉乘符號 a[na]=t1[0]=a[0],b[nb]=t2[0]=b[0]; //初始化T1,T2和ANA,BNB for(i=2;i<na;i++){ //掃一次第一個多邊形全部點 t1[1]=a[i-1],t1[2]=a[i]; //每次在第一個多邊形取兩個點賦給T11,T12 num1=dcmp(cross(t1[1],t1[2],t1[0]));//求出叉乘符號 if(num1<0)swap(t1[1],t1[2]); //小於0則改變T11,T12可使叉乘符號變正,實即改變T1三個點的順逆 for(j=2;j<nb;j++){ //掃一次第二個多邊形全部點 t2[1]=b[j-1],t2[2]=b[j]; //然後再在第二個多邊形取兩個點賦給T21,T22 num2=dcmp(cross(t2[1],t2[2],t2[0]));//求出叉乘符號 if(num2<0)swap(t2[1],t2[2]);//小於0則改變T11,T12可使叉乘符號變正,實即改變T1三個點的順逆 res+=CPIA(t1,t2,3,3); //累加相交部分面積 } } return res; } Point p1[maxn], p2[maxn];//兩個陣列存讀入點集 int main(){ int n1, n2; //定義 while(cin>>n1>>n2){ //輸入兩個多邊形的點數 for(int i=0;i<n1;i++)scanf("%lf%lf",&p1[i].x,&p1[i].y);//輸入點數 for(int i=0;i<n2;i++)scanf("%lf%lf",&p2[i].x,&p2[i].y);//輸入點數 cout<<SPIA(p1,p2,n1,n2)<<endl;//輸出面積交 }//如果要求面積並,則先用三角剖分分別求兩個多邊形的面積S1,S2,然後S1+S2-面積交即可 return 0; } /* in 4 4 0 0 1 0 1 1 0 1 0.5 0.5 -1 0.5 -1 -1 0.5 -1 out 0.25 int 4 4 0 0 5 0 5 5 0 5 -1 -1 4 -1 4 4 -1 4 out 16 */