1. 程式人生 > >多邊形面積交/並

多邊形面積交/並

求多邊形面積交,邊逆時針方向給出(否則要逆過來)
求多邊形面積並,用面積和減去交即可

#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
*/