2011/09/01

C#でマンデルブロ集合を描いてみた

,






















練習がてら書いてみました。だいたい3時間。
マンデルブロ集合たん綺麗だよ'`ァ,、ァ(*´Д`*)'`ァ,、ァ
マンデルブロ集合は複素数を扱うので、複素数クラスを探したのですが、公式だと.Net Framework4からしかComplexクラスが入ってないので、しょうがないので自分で書きました。ぐぐっても複素数クラスをジェネリック?を使ったりして「如何に上手く作るか」説明してあるサイトはあったのですが、初心者向けの簡単なクラスは見つかりませんでした…(´・ω・`)
正直こういうところは動作が確実な、そして俺でも読める(重要)ライブラリを拝借するのが一番楽で確実と思うですよ?

コードの解説。

1) 色
Wikiには、マンデルブロ集合の周りも発散の速度で色分けしてあります。よって、発散まで(Infinityがtrue)のループ回数で判定してますが、結構シビアです。大抵は殆ど直ぐに発散します。集合の境界あたりは発散までの回数が急激に変わるみたいです。

2) delegate
マルチスレッドじゃないと重くてやってらんねーので、UIを弄るときはマルチスレッド特有の処理が必要です。ここらへん素人なので他のサイトをあたってくだしあ

3) ループ回数
今のところ10000回です。どっかのサイトに10000回って書いてあるのを昔見た気がする、それが全て。

4) 複素数クラス
長くなったので別の投稿にするかもですね。
Complexとある所は全て自分の複素数クラスです。

5) UI
WindowSize : width 480, height 480
Button1 : これを押すとDrawします。
Label1 : ループが何回目かを表示します。
えっと、参考にしてください。

ええと、C#について幾つか今日はじめて知ったことを。
(´ー`)oO(この言語進化するの早すぎだろ…)

1) checked, unchecked
これを書くとOverflowがごにょごにょ こちらのリンク先に詳しい説明がありました。

2) doubleはOverflow Exceptionを投げない
これも上のリンク先に(ry

3) Double.IsPositiveInfinity(), Double.IsNaN()
doubleがOverflowするとInfinityという値を取ります。ここらへんはMSDNのDoubleに詳しく書いてあります。で、それの判定はDouble.IsPositiveInfinity(double d)です。NaNについても同様。

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.Threading;    //Threadクラスで必要

namespace WindowsFormsApplication43
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {

            Thread t = new Thread(
                delegate()
                {
                    for (int x = 0; x < 480; x++)
                    {
                        //中央は(240, 240)
                        double __x = x;
                        double _x = (__x - 240.0d) / 240.0d;

                        for (int y = 0; y < 480; y++)
                        {
                            double __y = y;
                            double _y = (__y - 240.0d) / 240.0d;

                            Complex v = new Complex(_x, _y);

                            //発散しない
                            if (IsDivergence(v) == -1)
                            {
                                if(this.IsHandleCreated)
                                {
                                    this.Invoke(
                                        (MethodInvoker)delegate()
                                        {
                                            Graphics g = this.CreateGraphics();
                                            g.FillRectangle(Brushes.Black, x, y, 1, 1);
                                            g.Flush();
                                            g.Dispose();
                                            label1.Text = x.ToString();
                                            label1.Show();
                                        });
                                }
                            }
                                //早い発散
                            else if (IsDivergence(v) > 0 && IsDivergence(v) < 40)
                            {
                                if (this.IsHandleCreated)
                                {
                                    this.Invoke(
                                        (MethodInvoker)delegate()
                                        {
                                            Graphics g = this.CreateGraphics();
                                            g.FillRectangle(Brushes.Aqua, x, y, 1, 1);
                                            g.Flush();
                                            g.Dispose();
                                            label1.Text = x.ToString();
                                            label1.Show();
                                        });
                                }
                            }
                                //中くらいの発散速度
                            else if (IsDivergence(v) >= 40 && IsDivergence(v) < 70)
                            {
                                if (this.IsHandleCreated)
                                {
                                    this.Invoke(
                                        (MethodInvoker)delegate()
                                        {
                                            Graphics g = this.CreateGraphics();
                                            g.FillRectangle(Brushes.Blue, x, y, 1, 1);
                                            g.Flush();
                                            g.Dispose();
                                            label1.Text = x.ToString();
                                            label1.Show();
                                        });
                                }
                            }
                                //遅い発散
                            else
                            {
                                if (this.IsHandleCreated)
                                {
                                    this.Invoke(
                                        (MethodInvoker)delegate()
                                        {
                                            Graphics g = this.CreateGraphics();
                                            g.FillRectangle(Brushes.RoyalBlue, x, y, 1, 1);
                                            g.Flush();
                                            g.Dispose();
                                            label1.Text = x.ToString();
                                            label1.Show();
                                        });
                                }
                            }
                        }
                    }
                });
            t.Start();
        }

        private int IsDivergence(Complex initval)
        {
            //初期値
            Complex z = new Complex(0.0d, 0.0d);  //z0 = 0

            for (int i = 0; i < 10000; i++)
            {
                z = z * z + initval;
                //catch (OverflowException ex)
                //{
                    //発散
                //    return true;
                //}

                if (Double.IsPositiveInfinity(z.re) || Double.IsPositiveInfinity(z.im))
                {
                    //発散
                    return i;
                }
                else if (Double.IsNaN(z.re) || Double.IsNaN(z.im))
                {
                    //発散
                    return i;
                }
            }
            //発散してない
            return -1;
        }
    }
}
Read more →