はじめに
1年ほど前に「【C#】樹形図を描く(Bitmap)」という記事を書きましたが、この時は単色の樹形図を描くだけのプログラムでした。
別の記事の【C#】ネコのイラストを描く(Bitmap)では様々な色を使って描画したので、今回は「樹形図の幹や枝部分を茶色、葉の部分を緑色に色分けして描画する」というのにチャレンジしてみました。
幹/枝と葉の峻別
- 今回は「樹形図の15回の枝分かれ」のうち「スタートから10回までを幹/枝として茶色で描画して、残りを葉として緑色で描画する」というルールにしました。
作成したコード
- 基本的なコードは、【C#】樹形図を描く(Bitmap)に掲載されているコードと同じです。
- 今回修正したのは「幹/枝と葉を区別する部分」で、ColoredBranchオブジェクトを生成する際に"枝生成の残りの繰り返し回数"を使って「幹/枝と葉を区別するフラグ」をセットしました。
- 実際にBitmapオブジェクトに描画する際に、このフラグを参照して色付けを行いました。
ColoredTreeDrawer.cs
using System;
using System.Collections.Generic;
using System.Drawing;
namespace CSharpStudy.Image
{
public class ColoredTreeDrawer
{
List<ColoredBranch> branches;
/// <summary>
/// 樹形図を描画するキャンバス。
/// </summary>
private Bitmap bmp;
/// <summary>
/// キャンバスサイズ。
/// </summary>
private int canvasSize;
/// <summary>
/// コンストラクタ。
/// </summary>
/// <param name="canvasSize">キャンバスの幅と高さ(px)</param>
public ColoredTreeDrawer(int canvasSize = 300)
{
this.bmp = new Bitmap(canvasSize, canvasSize);
this.canvasSize = canvasSize;
}
/// <summary>
/// 樹形図を描画する。
/// </summary>
/// <param name="filePath">出力先のファイルパス。</param>
public void Draw(string filePath)
{
// 樹形図の「枝」を作る。
// 末端付近の枝(=葉)だけ緑色にして、それ以外は茶色で描画する。
branches = new List<ColoredBranch>();
CreateBranch(15, this.canvasSize / 2, this.canvasSize, ToRadian(90), this.canvasSize / 5);
using (Graphics g = Graphics.FromImage(bmp))
{
Pen brownPen = new Pen(Color.Brown, 1);
Pen greenPen = new Pen(Color.Green, 1);
foreach (var branch in branches)
{
if (branch.IsLeaf)
{
g.DrawLine(greenPen, branch.StartPoint, branch.EndPoint);
}
else
{
g.DrawLine(brownPen, branch.StartPoint, branch.EndPoint);
}
}
}
// 画像をPNG形式で保存する。
bmp.Save(filePath, System.Drawing.Imaging.ImageFormat.Png);
}
/// <summary>
/// 樹形図の枝を生成する。
/// </summary>
/// <param name="n">「枝生成」の残りの繰り返し回数</param>
/// <param name="x1">枝の開始点のx座標</param>
/// <param name="y1">枝の開始点のy座標</param>
/// <param name="angle">枝の角度</param>
/// <param name="length">枝の長さ</param>
void CreateBranch(int n, double x1, double y1, double angle, double length)
{
if (n == 0) { return; }
double x2 = x1 + length * Math.Cos(angle);
double y2 = y1 - length * Math.Sin(angle);
// 出力時の色をコントロールするため、末端付近の枝(=葉)であるかのフラグをセットする。
bool isLeaf = false;
if (n<=5)
{
isLeaf = true;
}
var branch = new ColoredBranch(new Point((int)x1, (int)y1), new Point((int)x2, (int)y2), isLeaf);
branches.Add(branch);
CreateBranch(n - 1, x2, y2, angle - Math.PI / 10, length * 0.75);
CreateBranch(n - 1, x2, y2, angle + Math.PI / 10, length * 0.75);
}
/// <summary>
/// 角度の「度」をラジアンにして返す。
/// </summary>
/// <param name="angle">角度の「度」</param>
/// <returns>ラジアン</returns>
double ToRadian(double angle)
{
return angle * Math.PI / 180;
}
}
/// <summary>
/// 「枝」を表すクラス。
/// </summary>
class ColoredBranch
{
/// <summary>
/// 枝の開始点
/// </summary>
public Point StartPoint { get; }
/// <summary>
/// 枝の終了点
/// </summary>
public Point EndPoint { get; }
/// <summary>
/// この枝が葉(末端付近)であるか?
/// </summary>
public bool IsLeaf { get; }
/// <summary>
/// コンストラクタ
/// </summary>
/// <param name="start">枝の開始点</param>
/// <param name="end">枝の終了点</param>
/// <param name="isLeaf">この枝が葉(=末端付近)であるか?</param>
public ColoredBranch(Point startPoint, Point endPoint, bool isLeaf)
{
StartPoint = startPoint;
EndPoint = endPoint;
IsLeaf = isLeaf;
}
}
}
実行用のコード
ColoredTreeDrawerTest
using CSharpStudy.Image;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace CSharpStudyTest
{
[TestClass]
public class ColoredTreeDrawerTest
{
[TestMethod]
public void TestMethod1()
{
ColoredTreeDrawer td = new ColoredTreeDrawer(400);
td.Draw(@".\colored_tree.png");
}
}
}
生成された画像
- 幹/枝と葉が色分けされているので、葉の部分だけは木の雰囲気が出てきました。
- 一方で幹/枝/葉の線の太さが同じなので、全体としてどうしても不自然な感じになってしまいました。