二维几何-平面区域

当平面上有很多线段时,组成的图形往往不止一个多边形,而是一个平面直线图(PSLG),它代表一个平面区域划分,其中一个区域是一个多边形。

如果只有点和边的信息,如何找出所有区域呢?为方便起见,我们把每条边u-v拆成两条半边 u-v 和 v-u.并且每条半边只与它左边的面相邻。接下来,我们从一条半边出发遍历,每次像卷包裹算法那样找一个逆时针转的尽量多的边作为下一条边,直到回到出发的那条半边。

程序实现上可以把边表扩大一倍,让编号为i的半边的反向边的编号为i^1.

首先看一下边的存储结构定义:

//边
struct Edge
{
    int from, to; //起点,终点
    double ang;   //极角
};

平面直线图:

//平面直线图。
struct PSLG
{
    int n, m;               //顶点数 边数
    int face_cnt;           //面数
    double x[maxn];         //顶点
    double y[maxn];
    vector<Edge> edges;
    vector<int> G[maxn];
    bool vis[maxn*2];      // 每条边是否被访问过
    int left[maxn*2];       //左面的编号
    int prev[maxn*2];       //相同起点的上一条边,即顺时针旋转碰到的下一条边的编号
    vector<Polygon> faces;  //面
    double areas[maxn];     //每个polygon的面积

    void init(int n)
    {
        this->n = n;
        edges.clear();
        faces.clear();
        for(int i = 0; i < n; i++)
            G[i].clear();
    }

    //有向线段from-to的极角
    double getAngle(int from, int to)
    {
        return atan2(y[to]-y[from], x[to]-x[from]);
    }

    void AddEdge(int from, int to)
    {
        edges.push_back((Edge){from, to, getAngle(from, to)});
        edges.push_back((Edge){to, from, getAngle(to, from)});
        m = edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }

    //找出faces并计算面积
    void Build()
    {
        int u, i, j, d, from, e;
        //给从u开始个各条边按照极角排序
        for(u = 0; u < n; u++)
        {
            d = G[u].size();
            for(i = 0; i < d; i++)
                for(j = i+1; j < d; j++)   //这里假设从u出发的线段不会太多
                    if(edges[G[u][i]].ang > edges[G[u][j]].ang)
                         swap(G[u][i], G[u][j]);
            for(i = 0; i < d; i++)
                prev[G[u][(i+1)%d]] = G[u][i];
        }

        face_cnt = 0;
        memset(vis, false, sizeof(vis));
        for(u = 0; u < n; u++)
            for(i = 0; i < G[u].size(); i++)
            {
                e = G[u][i];
                if(!vis[e])                               //逆时针找圈
                {
                    face_cnt++;                           //找到一个新的面
                    Polygon poly;
                    for(;;)
                    {
                        vis[e] = true;
                        left[e] = face_cnt;
                        from = edges[e].from;
                        poly.push_back(Point(x[from], y[from]));  //把新的点加入面中
                        e = prev[e ^ 1];                          //e^1为反向边,然后prev就是需要走的下一条边
                        if(e == G[u][i])                          //回到了起始边
                            break;
                    }
                    faces.push_back(poly);
                }
            }
        //对于连通图,惟一一个面积小于零的面是无限面
        //对于内部区域来说,无限面多边形的各个顶点是顺时针的
        for(i = 0; i < faces.size(); i++)                  //计算各个面的面积
            areas[i] = PolygonArea(faces[i]);
    }
};

 

    原文作者:算法
    原文地址: https://www.twblogs.net/a/5bd3a0592b717778ac209bf4
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞