C#でマンデルブロ集合の描画奮闘記

C#の学習としてマンデルブロ集合を描画するプログラムを作ることにした。C#で使えるクラスなどについてほとんど知識がないので手探り感いっぱい。

※ この時点では、描画イベントの度に1x1の大きさのRectangleをwidth x height個配置して描画していたのですごく時間がかかっていた。そこで実際にどれぐらい時間がかかっているか計測することにした。


成果物

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using System.Numerics; // Complex
using System.Diagnostics; // Swapwatch

namespace WindowsFormsApplication3
{
    public partial class Form1 : Form
    {
        private const int w = 400;
        private const int h = 400;
        private const int iter = 70;
        private byte[] result;
        private Bitmap bmp;
        private bool done;
        public Form1()
        {
            InitializeComponent();
            done = false;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            MandelbrotSetCalculation();
            CreateBitmap();
        }
        private void MandelbrotSetCalculation()
        {
            // ラベルを書き換えたときにフェードアウトするように設定できないかなあ
            if (done) return;
            // [(-2.5, +2.0), (+1.5, -2.0)]
            const double x1 = -2.5; // upperleft
            const double y1 = +2.0;
            const double x2 = +1.5; // lowerright
            const double y2 = -2.0;
            result = new byte[w * h];
            for (int i = 0; i < h; i++)
            {
                for (int j = 0; j < w; j++)
                {
                    Complex c = new Complex(x1 + (x2 - x1) / w * j, y1 - (y1 - y2) / h * i);
                    Complex z = new Complex(0.0, 0.0);
                    byte k = 0;
                    for (k = 0; k < iter; k++)
                    {
                        z = z * z + c;
                        if (Math.Abs(Math.Sqrt(z.Real * z.Real + z.Imaginary * z.Imaginary)) > 1e10)
                            break;
                    }
                    // 結果
                    result[i * w + j] = k;
                }
            }
            // 1dotずつ描画するのはやはりあまりに遅かったのでイメージを作ることにする。
            // TODO: implement
            done = true;
            waitinglabel.Text = "";
       }
       private void CreateBitmap()
       {
           bmp = new Bitmap(w, h);
           for (int i = 0; i < h; i++)
               for (int j = 0; j < w; j++)
               {
                   int c = Convert.ToInt32(255 * Convert.ToDouble(result[i * w + j]) / iter);
                   bmp.SetPixel(j,i, Color.FromArgb(255,c,c,c));
//                   bmp.SetPixel(j, i, result[i * w + j] == iter ? Color.Black : Color.White);
               }
       }
       private void Form1_Paint(object sender, PaintEventArgs e)
       {
           if (done){
               // 時間計測
               Stopwatch s = new Stopwatch();
               s.Start();
               // 描画
               Graphics gs = Graphics.FromHwnd(this.Handle);
               gs.DrawImage(bmp, 0, 0);
               //速くなったのでもう表示いらない
               s.Stop();
               //MessageBox.Show("描画に" + s.ElapsedMilliseconds + "ミリ秒かかりました");
           }
       }
    }
}