【C#】樹形図を描く（Bitmap）

# はじめに

これまでにC#を使った画像処理の記事を4つほど書きましたが、前回の「ネコのイラストを描く」では目視で調整しながら顔のパーツを貼り付けていくという、非常に原始的な方法でイラストを作成しました。

そこで今回は「単純な幾何学模様ではないけれども、プログラムで自動的にイラストを作る」ということで、樹形図を作成してみました。

* [【C#】画像生成の基本（Bitmap）](https://qiita.com/nkojima/items/ab776e03ae80a2b2e3bb)
* [【C#】2つの画像の差分を得る（Bitmap）](https://qiita.com/nkojima/items/1fb901c13f9707808bc0)
* [【C#】市松模様を描画する（Bitmap）](https://qiita.com/nkojima/items/b61599c02f4d91ab6269)
* [【C#】ネコのイラストを描く（Bitmap）](https://qiita.com/nkojima/items/20d3e724f23ee29c7ac6)

# コード

* 描画処理そのものは、[【C#】ネコのイラストを描く（Bitmap）](https://qiita.com/nkojima/items/20d3e724f23ee29c7ac6)でネコの髭を描いた時と同様に、`Graphics.DrawLine()`を使いました。
* 角度と長さを調整しながら枝を描画する際に、今回は再帰処理を使っています。
* 描画してみるまでイラストの仕上がりサイズなどが分からないので、パラメーターを色々と調整して試行錯誤しました。

```c#:TreeDrawer.cs
using System;
using System.Collections.Generic;
using System.Drawing;

namespace CSharpStudy.Image
{
    public class TreeDrawer
    {
        List<Branch> branches;

        /// <summary>
        /// 樹形図を描画するキャンバス。
        /// </summary>
        private Bitmap bmp;

        private int canvasSize;

        /// <summary>
        /// コンストラクタ。
        /// </summary>
        /// <param name="canvasSize">キャンバスの幅と高さ（px）</param>
        public TreeDrawer(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<Branch>();
            CreateBranch(15, this.canvasSize / 2, this.canvasSize, ToRadian(90), this.canvasSize / 5);

            using (Graphics g = Graphics.FromImage(bmp))
            {
                foreach (var branch in branches)
                {
                    Pen blackPen = new Pen(Color.Black, 1);
                    g.DrawLine(blackPen, 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);

            var branch = new Branch(new Point((int)x1, (int)y1), new Point((int)x2, (int)y2));
            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 Branch
    {
        /// <summary>
        /// 枝の開始点
        /// </summary>
        public Point startPoint;

        /// <summary>
        /// 枝の終了点
        /// </summary>
        public Point endPoint;

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="start">枝の開始点</param>
        /// <param name="end">枝の終了点</param>
        public Branch(Point startPoint, Point endPoint)
        {
            this.startPoint = startPoint;
            this.endPoint = endPoint;
        }
    }
}
```

# 実行用のコード

```c#:TreeDrawerTest.cs
using CSharpStudy.Image;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace CSharpStudyTest
{
    [TestClass]
    public class TreeDrawerTest
    {
        [TestMethod]
        public void TestMethod1()
        {
            TreeDrawer td = new TreeDrawer(400);
            td.Draw(@".\tree.png");
        }
    }
}
```

# 生成された画像

* まあ...普通の樹形図が出来上がりました(笑)
* 幹と枝は茶色、葉は緑色で描画しようと思ったのですが、面倒だったので止めました。
 * でも黒一色だと味気ないですね...

![tree.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312166/9e99f19e-04b0-2276-081b-e9b382c69b3a.png)