C# 詭異的Graphics.FillPolygon

最近研究界址座標繪圖,圖形不規則且內部有空洞。
用到GDI繪圖,使用Graphics.FillPolygon函數來實現,使用過程卻發現一些詭異的事情,查閱MSDN文檔解釋,示例也是模糊不清,亦或者我的理解能力有限。[查看MSDN Graphics.FillPolygon的解釋](https://docs.microsoft.com/zh-cn/dotnet/api/system.drawing.graphics.fillpolygon?redirectedfrom=MSDN&view=netframework-4.8)
實現功能:繪製如下挖空洞圖形:


代碼測試:在WinForm下的Paint進行。
注意到Graphics.FillPolygon函數的可選參數填充模式[FillMode](https://docs.microsoft.com/zh-cn/dotnet/api/system.drawing.drawing2d.fillmode?view=netframework-4.8)
MSDN上的機器翻譯把我給整暈了。切換至英文的[FillMode](https://docs.microsoft.com/en-us/dotnet/api/system.drawing.drawing2d.fillmode?view=netframework-4.8)
靜下心來看英文解釋,大體明白了
FillMode有兩個模式
| Fields    |Value  |
|Alternate|0         |    Specifies the alternate fill mode.
| Winding |1        |    Specifies the winding fill mode.

Alternate,缺省的默認模式。看註解:
        請從路徑中的任意起始點繪製一條線條,使其在路徑外明顯地指向某個點。 如果行與奇數個路徑段相交,則起始點在閉合區域內,因而是填充或剪輯區域的一部分。 偶數個交叉點表示該點不在要填充或剪裁的區域中。 通過使用線條將最後一個點連接到圖形的第一個點,對其進行填充或剪裁。
        理解:通過計算點是否在填充區域的內部來確定是否挖空不填充。如何判定是否在內部?以點爲始點作射線,射線與填充的外框線相交點爲奇數個則爲內部點,偶數個爲外部點,(這是幾何證明,初中知識,略過)
        按理解,輸入代碼試試是否與自己理解的一樣。
<1>

            Point[] points = new Point[] {
                //外邊框
                new Point(10,10),
                new Point(200,10),
                new Point(200,200),
                new Point(10,200),
                new Point(10,10),             

                //內挖空邊框1
                new Point(120,30),
                new Point(120,90),
                new Point(160,90),
                new Point(160,30),
                new Point(120,30),

                //內挖空邊框2
                new Point(20,20),
                new Point(20,100),
                new Point(100,100),
                new Point(100,20),
                new Point(20,20),

            };
            e.Graphics.FillPolygon(Brushes.Blue, points, System.Drawing.Drawing2D.FillMode.Alternate);

產生圖形:

<2>、產生的圖形與意料中不相符!刪除內邊框2點座標再試試

            Point[] points = new Point[] {
                //外邊框
                new Point(10,10),
                new Point(200,10),
                new Point(200,200),
                new Point(10,200),
                new Point(10,10),             

                //內挖空邊框1
                new Point(120,30),
                new Point(120,90),
                new Point(160,90),
                new Point(160,30),
                new Point(120,30)
            };
            e.Graphics.FillPolygon(Brushes.Blue, points, System.Drawing.Drawing2D.FillMode.Alternate);

產生圖形:

產生的圖形符合意料中。

爲何<1>中多出一個內框就會繪製錯誤? 再回去細讀英文MSDN的解釋https://docs.microsoft.com/en-us/dotnet/api/system.drawing.graphics.fillpolygon?redirectedfrom=MSDN&view=netframework-4.8

Remarks

Every two consecutive points in the array specify a side of the polygon. In addition, if the last point and the first point do not coincide, they specify the closing side of the polygon.

以及FillMode註解中的這句“通過使用線條將最後一個點連接到圖形的第一個點,對其進行填充或剪裁”

       原來理解“last point” 是閉合框中的始點。last point = first point = new Point(120,30), 實際上那只是閉合點,真正閉合框的最後一個點應該是pionts[0],亦即每個閉合框最後都應該添加一個點pionts[0]。

<3>嘗試代碼

            Point[] points = new Point[] {
                //外邊框
                new Point(10,10),
                new Point(200,10),
                new Point(200,200),
                new Point(10,200),
                new Point(10,10),             
                new Point(10,10),   //添加結束點point[0]
                //內邊框1
                new Point(120,30),
                new Point(120,90),
                new Point(160,90),
                new Point(160,30),
                new Point(120,30),
                new Point(10,10),   //添加結束點point[0]

                //內邊框2
                new Point(20,20),
                new Point(20,100),
                new Point(100,100),
                new Point(100,20),
                new Point(20,20),
                new Point(10,10)    //添加結束點point[0]
            };
            e.Graphics.FillPolygon(Brushes.Blue, points, System.Drawing.Drawing2D.FillMode.Alternate);

產生圖形

正確!

爲何第<2>步能產生意料的圖形?個人推測是當points的最後一個點與point[0]不相等時,函數自動以point[0]作爲last point。

 

Winding模式  看註解:

The Winding mode considers the direction of the path segments at each intersection. It adds one for every clockwise intersection, and subtracts one for every counterclockwise intersection. If the result is nonzero, the point is considered inside the fill or clip area. A zero count means that the point lies outside the fill or clip area.

A figure is considered clockwise or counterclockwise based on the order in which the segments of the figure are drawn.

理解:按點系列前進的方向,右手指向的是Polygon的內部,亦即Polygon的外框線是順時針走向,內框線(挖洞)是逆時針走向。這樣與ARCGIS的shape繪製是一致的。

嘗試代碼:

            Point[] points = new Point[] {
                //外邊框走向順時針
                new Point(10,10),
                new Point(200,10),
                new Point(200,200),
                new Point(10,200),
                new Point(10,10),             
                new Point(10,10),   //添加結束點point[0]
                
                //內邊框1走向順時針,無法形成空洞
                new Point(120,30),
                new Point(160,30),                
                new Point(160,90),
                new Point(120,90),
                new Point(120,30),
                new Point(10,10),   //添加結束點point[0]

                //內邊框2走向逆時針,形成空洞
                new Point(20,20),
                new Point(20,100),
                new Point(100,100),
                new Point(100,20),
                new Point(20,20),
                new Point(10,10),   //添加結束點point[0]
            };
            e.Graphics.FillPolygon(Brushes.Blue, points, System.Drawing.Drawing2D.FillMode.Winding);

運行產生圖形:

內邊框1順時針走向無法形成空洞,這在shapefile中將產生一個“髒的”Polygon,導致疊加出現。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章