WisdomSoft - for your serial experiences.

2.8 多角形

複数の頂点を結ぶ多角形を描画します。

2.8.1 複数の点を結ぶ

多数の連続した線を描画する場合、DrawLine() メソッドを繰り返して呼び出すよりも Graphics クラスの DrawLines() メソッドを呼び出した方が便利です。このメソッドは Point 構造体の配列から連続した線をつなげて描画します。

Graphics クラス DrawLines() メソッド
public void DrawLines(Pen pen, Point[] points)

pen パラメータには線の色や幅を設定するペンオブジェクトを、points パラメータには頂点の配列を設定します。

コード1
using System.Drawing;
using System.Windows.Forms;

public class Test : Form
{
	protected override void OnPaint(PaintEventArgs e) 
	{
		base.OnPaint(e);
		Pen pen = new Pen(Color.Black, 5);
		Point[] pts = {
			new Point(10, 10), new Point(110, 60), new Point(210, 10),
			new Point(210, 210), new Point(110, 160), new Point(10, 210)
		};
		e.Graphics.DrawLines(pen, pts);
	}
	static void Main() 
	{
		Application.Run(new Test());
	}
}
実行結果
コード1 実行結果

コード1は6つの頂点を定義する Point 構造体の配列 pts を使って DrawLines() メソッドで描画しています。

DrawLines() メソッドは単純な連続したペンですが、多角形を描画する場合は DrawPolygon() メソッドを呼び出します。このメソッドは、Point 構造体の配列から連続した線を描画するまでは DrawLines() メソッドと同じですが、終点から始点まで線を繋げることで図形を閉じます。

Graphics クラス DrawPolygon() メソッド
public void DrawPolygon(Pen pen, Point[] points)

このメソッドのパラメータは DrawLines() メソッドと同じです。

コード2
using System.Drawing;
using System.Windows.Forms;

public class Test : Form
{
	protected override void OnPaint(PaintEventArgs e) 
	{
		base.OnPaint(e);
		Pen pen = new Pen(Color.Black, 1);
		Point[] pts = {
			new Point(10, 10), new Point(110, 60), new Point(210, 10),
			new Point(210, 210), new Point(110, 160), new Point(10, 210)
		};
		e.Graphics.DrawPolygon(pen, pts);
	}
	static void Main() 
	{
		Application.Run(new Test());
	}
}
実行結果
コード 実行結果

コード2コード1の処理を DrawPolygon() メソッドに置き換えたものです。終点から始点に向かって線を引くことで図形を閉じていることがわかります。

2.8.2 多角形の塗りつぶし

多角形は閉じた図形なのでブラシで内部を塗りつぶすことができます。多角形を塗りつぶすには Graphics クラスの FillPolygon() メソッドを使います。

Graphics クラス FillPolygon() メソッド
public void FillPolygon(Brush brush, Point[] points)

Pen オブジェクトの代わりに内部を塗りつぶすための Brush オブジェクトを設定することを除いて、使い方は DrawPolygon() メソッドと同じです。

コード3
using System.Drawing;
using System.Windows.Forms;

public class Test : Form
{
	protected override void OnPaint(PaintEventArgs e) 
	{
		Brush brush = new SolidBrush(Color.Black);
		Point[] pts = {
			new Point(10, 10), new Point(110, 60), new Point(210, 10),
			new Point(210, 210), new Point(110, 160), new Point(10, 210)
		};
		e.Graphics.FillPolygon(brush, pts);
	}
	static void Main() 
	{
		Application.Run(new Test());
	}
}
実行結果
コード3 実行結果

コード3コード2と同じ多角形を塗りつぶしたものです。多角形の内部がブラシで塗りつぶされていることが結果から確認できます。

2.8.3 多角形モード

多角形の内部を塗りつぶすとき、多角形が複雑に重なり合っている場合はどうなるのでしょうか。複雑に交差する多角形とは、例えば次のようなものです。

図1 交差する多角形
図1 交差する多角形

この場合、多角形が交差しているため、塗りつぶすべき多角形の内部が曖昧です。さて、どこが塗りつぶされるでしょうか。基本的な考え方としては、外側の多角形が塗りつぶされると考えてよいでしょう。交差する内側の多角形は塗りつぶされません。

より正確に説明すると、奇数から偶数番目の辺の間にある閉じた空間を塗りつぶします。外側から順に、反対側に向かって辺の数を数えて1番目の辺から2番目の辺の間を塗りつぶし、2番目と3番目の辺の間は塗りつぶさず、3番目と4番目の辺の間を塗りつぶし…という法則になります。面から適当な方向に向かって直線を書き、その線に交わる変の数を外側から順に数えるとわかりやすいでしょう。

図2 辺の数え方
図2 辺の数え方

図2は、縦に引かれた直線が存在すると仮定して、直線に交わる辺を上から順に数えたものです。奇数番号から偶数番号までの間にある面が現在のブラシで塗りつぶされ、逆に偶数番号から奇数番号の間にある面は塗りつぶされません。この場合、2~3、4~5 番の間にある矩形は塗りつぶされないのです。横に線を引いて数えても同じ結果となります。

交差する多角形の内側も塗りつぶしたい場合は、FillPolygon() メソッドで多角形モードを設定します。

Graphics クラス FillPolygon() メソッド
public void FillPolygon(Brush brush, Point[] points, FillMode fillMode)

先頭 2 つのパラメータはこれまでと同じで、fillMode パラメータに多角形モードを表す System.Drawing.Drawing2D.FillMode 列挙体のメンバを設定します。

System.Drawing.Drawing2D.FillMode 列挙体
[Serializable]
public enum FillMode

FillMode 列挙体は、交差する多角形の内側を塗りつぶさないことを表す Alternate メンバと、全域を塗りつぶす Winding メンバを宣言しています。FillMode を設定しない場合は Alternate がデフォルトとして扱われます。

基本的に Winding 多角形モードは多角形全域を塗りつぶしますが、塗りつぶす面から外側に向かって交差する辺の方向を調べ、それぞれ方向に向かっている線の数が等しくない場合とされています。左に向かって描かれている線と、右に向かって描かれている線の数が同一の場合は塗りつぶされません。

よって Winding を設定しても必ずしもすべてを塗りつぶすわけではないのです。図3に Winding モードでも塗りつぶされない面が 1 ヶ所だけあります。左上の頂点を開始点とした場合、どの面が塗りつぶされないのでしょう。

図3 交差する辺の向き
図3 交差する辺の向き

図3の矢印は各辺の描画方向を表しています。この中で、外側に向かって線を引いている面だけが、交差する辺の方向の数が両方向とも一致する性質を持っています。横に伸びる線に交差する辺に着目してみましょう。内側の辺は上に、外側の辺は下に向かっています。上に向かう辺が 1 つ、下に向かう辺が 1 つと、各方向に向かう辺の数が等しいことが重要です。Winding モードの塗りつぶし条件は、それぞれの方向に向かっている線の数が等しくない場合なので、この面は条件に一致しないため塗りつぶされないのです。下に延びる線を見ても同様に、右に向かう辺と左に向かう辺がそれぞれ 1 つずつ存在します。

コード4
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

public class Test : Form
{
	protected override void OnPaint(PaintEventArgs e) 
	{
		Pen pen = new Pen(Color.Red);
		Brush brush = new SolidBrush(Color.Black);
		Point[] pts1 = {
			new Point(10, 10), new Point(110, 10), new Point(110, 210), new Point(60, 210),
			new Point(60, 60), new Point(210, 60), new Point(210, 160),  new Point(10, 160),
			new Point(10, 130), new Point(130, 130), new Point(130, 90),  new Point(10, 90)
		};
		Point[] pts2 = {
		   new Point(220, 10), new Point(320, 10), new Point(320, 210), new Point(270, 210),
		   new Point(270, 60), new Point(420, 60), new Point(420, 160),  new Point(220, 160),
		   new Point(220, 130), new Point(340, 130), new Point(340, 90),  new Point(220, 90)
		};
		
		e.Graphics.FillPolygon(brush, pts1);
		e.Graphics.FillPolygon(brush, pts2, FillMode.Winding);
		e.Graphics.DrawPolygon(pen, pts1);
		e.Graphics.DrawPolygon(pen, pts2);
	}
	static void Main() 
	{
		Application.Run(new Test());
	}
}
実行結果
コード4 実行結果

コード4は、デフォルトの多角形モード(FillMode.Alternate)と、FillMode.Winding に変更した場合とで、多角形の内部の塗りつぶしがどのように変化するかを調べたプログラムです。実行結果の左側の多角形が Alternate、右側が Winding です。Winding は交差する内部の多角形も塗りつぶしますが、それでも一ヶ所だけ塗りつぶされない面が存在します。