1. 程式人生 > >維諾圖(Voronoi Diagram)分析與實現

維諾圖(Voronoi Diagram)分析與實現

一、問題描述

1.Voronoi圖的定義

又叫泰森多邊形或Dirichlet圖,它是由一組由連線兩鄰點直線的垂直平分線組成的連續多邊形組成。

2.Voronoi圖的特點

(1)每個V多邊形內有一個生成元; 
(2)每個V多邊形內點到該生成元距離短於到其它生成元距離; 
(3)多邊形邊界上的點到生成此邊界的生成元距離相等; 
(4)鄰接圖形的Voronoi多邊形界線以原鄰接界線作為子集。

3.Voronoi的應用

在計算幾何學科中的重要地位,由於其根據點集劃分的區域到點的距離最近的特點,其在地理學、氣象學、結晶學、航天、核物理學、機器人等領域具有廣泛的應用。如在障礙物點集中,規避障礙尋找最佳路徑。

二、演算法分析與設計

Voronoi圖有著按距離劃分鄰近區域的普遍特性,應用範圍廣。生成V圖的方法很多,常見的有分治法、掃描線演算法和Delaunay三角剖分演算法。

1.建立Voronoi圖方法和步驟

本次實驗採用的是Delaunay三角剖分演算法。主要是指生成Voronoi圖時先生成其對偶元Delaunay三角網,再找出三角網每一三角形的外接圓圓心,最後連線相鄰三角形的外接圓圓心,形成以每一三角形頂點為生成元的多邊形網。如下圖所示。

這裡寫圖片描述

建立Voronoi圖演算法的關鍵是對離散資料點合理地連成三角網,即構建Delaunay三角網。 
建立Voronoi圖的步驟為: 
(1)離散點自動構建三角網,即構建Delaunay三角網。對離散點和形成的三角形編號,記錄每個三角形是由哪三個離散點構成的。 
(2)計算每個三角形的外接圓圓心,並記錄之。 
(3)遍歷三角形連結串列,尋找與當前三角形pTri三邊共邊的相鄰三角形TriA,TriB和TriC。 
(4)如果找到,則把尋找到的三角形的外心與pTri的外心連線,存入維諾邊連結串列中。如果找不到,則求出最外邊的中垂線射線存入維諾邊連結串列中。 
(5)遍歷結束,所有維諾邊被找到,根據邊畫出維諾圖。

2. Delaunay三角網的生成

建立Voronoi圖的關鍵是Delaunay三角網的生成。Delaunay三角網的特性: 
(1)空圓性,任一三角形外接圓內部不包含其他點。 
(2)最接近:以最近臨的三點形成三角形,且各線段(三角形的邊)皆不相交。 
(3)唯一性:不論從區域何處開始構建,最終都將得到一致的結果。 
(4)最優性:任意兩個相鄰三角形形成的凸四邊形的對角線如果可以互換的話,那麼兩個三角形六個內角中最小的角度不會變大。 
(5)最規則:如果將三角網中的每個三角形的最小角進行升序排列,則Delaunay三角網的排列得到的數值最大。 
(6)區域性:新增、刪除、移動某一個頂點時只會影響臨近的三角形。 
(7)具有凸多邊形的外殼:三角網最外層的邊界形成一個凸多邊形的外殼。 
Delaunay剖分是一種三角剖分的標準,實現它有多種演算法。本次採用Bowyer-Watson演算法,演算法的基本步驟是: 
(1)構造一個超級三角形,包含所有散點,放入三角形連結串列。 
(2)將點集中的散點依次插入,在三角形連結串列中找出其外接圓包含 
插入點的三角形(稱為該點的影響三角形),刪除影響三角形的公共邊,將插入點同影響三角形的全部頂點連線起來,從而完成一個點在Delaunay三角形連結串列中的插入。 
(3)根據優化準則對區域性新形成的三角形進行優化。將形成的三角形放入Delaunay三角形連結串列。 
(4)迴圈執行上述第2步,直到所有散點插入完畢。

關鍵步驟2如下圖所示: 
這裡寫圖片描述

步驟3的區域性優化的準則指的是: 
1.對新形成的三角形進行優化,將兩個具有共同邊的三角形合成一個多邊形。 
2.以最大空圓準則作檢查,看其第四個頂點是否在三角形的外接圓之內。 
3.如果在,修正對角線即將對角線對調,即完成區域性優化過程的處理。

LOP (Local Optimization Procedure)處理過程如下圖所示: 
這裡寫圖片描述

3.資料結構的設計

本程式的實現採用C#面嚮物件語言實現,故資料結構的設計採用類的形式,具體有:

點:
    public class Site
    {
        public double x, y;
        public Site()
        { }
        public Site(double x, double y)
        {
            this.x = x;
            this.y = y;
        }
    }
邊:
    public class Edge
    {
        public Site a, b;
        public Edge(Site a, Site b)
        {
            this.a = a;
            this.b = b;
        }
    }
三角形:
    public class DelaunayTriangle
    {
        Voronoi voronoi = new Voronoi();
        public Site site1, site2, site3;//三角形三點
        public Site centerPoint;//外界圓圓心
        public double radius;//外接圓半徑
        public List<DelaunayTriangle> adjoinTriangle;//鄰接三角形 

        public DelaunayTriangle(Site site1,Site site2,Site site3)
        {
            centerPoint = new Site();
            this.site1 = site1;
            this.site2 = site2;
            this.site3 = site3;
            //構造外接圓圓心以及半徑
            voronoi.circle_center(centerPoint, site1, site2,site3,ref radius);
        }
    }

4. 演算法複雜度分析

時間複雜度: 
Delaunay三角網的生成的時間複雜度: 
步驟一:構造一個超級三角形,O(1); 
步驟二:產找影響的三角形,構造新的三角形,O(1+2+…+n)=O(n2)O(1+2+…+n)=O(n2) 
步驟三:對新形成的三角形進行優化區域性優化:O(n)。 
因此,整體時間複雜度是:O(1)+O(n2)+O(n)=O(n2)O(1)+O(n2)+O(n)=O(n2)。

從Delaunay三角網生成Voronoi圖的時間複雜度: 
步驟一:構造構建Delaunay三角網,O(n2)O(n2); 
步驟二:計算三角形外接圓圓心,O(n); 
步驟三:尋找三角形三邊相鄰三角形:3O(n); 
步驟四:找到的維諾邊存入連結串列中,畫出維諾圖:O(n)。 
因此,整體時間複雜度是O(n2)+O(n)+3O(n)+O(n)=O(n2)O(n2)+O(n)+3O(n)+O(n)=O(n2)。

三、實驗結果

隨機生成點:

這裡寫圖片描述 

生成Delaunay三角形網:

這裡寫圖片描述 

生成Voronoi圖:

這裡寫圖片描述

生成Voronoi圖的可執行程式和原始碼工程檔案見here

下面附上相關函式申明(詳細程式碼見原始碼工程檔案)。

//根據點集構造Delaunay三角形網
public void setDelaunayTriangle(List<DelaunayTriangle> allTriangle, List<Site> sites);

//根據Delaunay三角形網構造Voronoi圖的邊
public List<Edge> returnVoronoiEdgesFromDelaunayTriangles(List<DelaunayTriangle> allTriangle, List<Edge> voronoiRayEdgeList);

//根據三角形連結串列返回三角形所有的邊
public List<Edge> returnEdgesofTriangleList(List<DelaunayTriangle> allTriangle);

//對新形成的三角形進行區域性優化
public List<DelaunayTriangle> LOP(List<DelaunayTriangle> newTriList);

//判斷邊是否屬於三角形
public bool isEdgeOnTriangle(DelaunayTriangle triangel,Edge edge);

//判斷點是否屬於三角形
public bool isPointOnTriangle(DelaunayTriangle triangle, Site site);

//將點與受影響的三角形三點連線,形成新的三個三角形新增到三角形鏈中
public void addNewDelaunayTriangle(List<DelaunayTriangle> allTriangles,DelaunayTriangle influenedTri,Site point);

//找出受影響的三角形的公共邊
public List<Edge> findCommonEdges(List<DelaunayTriangle> influenedTriangles);

//找出兩個三角形的公共邊
public Edge findCommonEdge(DelaunayTriangle chgTri1, DelaunayTriangle chgTri2);

//判斷插入點是否在三角形邊上
public Site[] isOnEdges(DelaunayTriangle triangle,Site site);  

//判斷點是否在三角形外接圓的內部
public bool isInCircle(DelaunayTriangle triangle, Site site) ;

//求三角形的外接圓心
public void circle_center(Site center, Site sites0, Site sites1, Site sites2, ref double radius) ;

//求兩點之間距離
public double distance2Point(Site p,Site p2);

PS:由於時間和水平有限,博文難免有不足甚至錯誤之處,僅供參考,歡迎批評指正。

參考文獻