2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【C#】色分けして樹形図を描く(Bitmap)

Posted at

はじめに

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");
        }
    }
}

生成された画像

  • 幹/枝と葉が色分けされているので、葉の部分だけは木の雰囲気が出てきました。
  • 一方で幹/枝/葉の線の太さが同じなので、全体としてどうしても不自然な感じになってしまいました。

colored_tree.png

参考URL

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?