Skip to content

半平面交(求凸多边形的交集) ​

cpp
struct Line{//存储凸包上每条线段的起点和终点
    Point st,ed;
    Line(Point _st=Point(),Point _ed=Point()):st(_st),ed(_ed){}
};

double line_get_angle(const Line& a){
    return atan2(a.ed.y-a.st.y,a.ed.x-a.st.x);
}

bool line_cmp(const Line& a, const Line& b){
    double A=line_get_angle(a),B=line_get_angle(b);
    if (!cmp(A,B)) return area(a.st,a.ed,b.ed)<0;
    return A<B;
}

Point get_line_intersection(Line a,Line b){
    return get_line_intersection(a.st,a.ed-a.st,b.st,b.ed-b.st);
}

bool on_right(Line& a, Line& b, Line& c){//判断bc的交点是否在a的右侧
    auto o = get_line_intersection(b,c);
    return sign(area(a.st,a.ed,o))<=0;
    //需要保留经过半平面交的所有直线的时候,将该处等号去掉
}
int dq[N];// 双端队列
double half_plane_intersection(vector<Line> &line){
    sort(line.begin(),line.end(),line_cmp);//对每条直线按照极坐标进行排序
    int hh=0,tt=-1;//定义双端队列对头和队尾元素
    for(int i=0;i<line.size();i++)//遍历每一条直线
    {
        if (i&&!cmp(line_get_angle(line[i]),line_get_angle(line[i-1]))) continue;//当两直线极坐标角度相同
        while(hh+1<=tt&&on_right(line[i],line[dq[tt-1]],line[dq[tt]])) tt--;
        //如果line[l[tt-1]]和line[l[tt]]的交点在line[i]的右侧,删除队尾元素
        while(hh+1<=tt&&on_right(line[i],line[dq[hh]],line[dq[hh+1]])) hh++;
        //如果line[l[hh]]和line[l[hh+1]]的交点在line[i]的右侧,删除队头元素
        dq[++tt]=i;//将该直线加入队尾
    }
    //判断一下第一条直线与最后一条直线
    while (hh+1<=tt&&on_right(line[dq[hh]],line[dq[tt-1]],line[dq[tt]])) tt--;//队尾更新队头
    while (hh+1<=tt&&on_right(line[dq[tt]],line[dq[hh]],line[dq[hh+1]])) hh++;//队头更新队尾

    dq[++tt]=dq[hh];//将第一条直线再次加入双端队列末尾

    //计算半平面交交点
    vector<Point> ans;
    for (int i=hh;i<tt;i++){ //对于半平面交上的直线两两之间求出交点
        ans.push_back(get_line_intersection(line[dq[i]],line[dq[i+1]]));
    }
    //计算半平面交面积
    return polygon_area(ans);
}