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,导致叠加出现。

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