2015年10月5日月曜日

マンデルブロ集合を描く[Processing]

 この間フラクタル図形を描いてみたけれど,一番の有名どころを描いてなかったのでやってみることにした。一応述べておくが私はフラクタルが好きというわけではないです。ただなんか無性に綺麗な図形を描きたくなる,いわば「Processingハイ」という状態であるというだけです。

1.マンデルブロ集合について

詳細はググるなり本で調べるなどしてください。ここでは図形を描くための最低限の情報について書き留めておきます。手元にある本も見ていますが,だいたいWikipediaにおんなじこと書いてある(Wikipediaさんすげー).
 まずマンデルブロ集合というのは次の漸化式で表される数列Znが収束するcの集合です。ただしZn,cは複素数。

 このときcを

 とあらわすならば,点(a,b)について打点していけばマンデルブロ集合を描ける。
 しかし,Processingでは複素数は標準ではサポートしていない。自分でComplex型を定義するのも難儀なので複素数を実部と虚部に分けて計算する。
 Znを次のように実部と虚部に分ける。
そうするとXn,Ynは次のように表される。

 以下に示すプログラムでは,このようにして計算していく。ただしZnの大きさの二乗が閾値Lを超えたら計算をやめるようにしてる。そのへんはプログラムを参考にしてほしいです。

2.ソースコード

まぁ長々と説明してきたがつまりこういうこと。
/* Znを何回漸化式で計算するか */
int N = 255;
/* Znの最大値 */
int L = 255;
/* 拡大率 */
float SCALE = 3.0;

void setup() {
  size(512, 512);
}

void draw() {
  /*中心に原点を持ってくる*/
  translate(width/2, height/2);
  background(0);

  for (int a = -width/2; a <= width/2; a++ ) {
    for (int b = -height/2; b <= height/2; b++ ) {
      /* 複素数c=x+yiを定める */
      float x = SCALE * a / width;
      float y = SCALE * b / height;
      /* 漸化式に基づいて計算 */
      int r = calc(x, y);

      /* 収束の速度に応じて色を指定 */
      /* 下のプログラムは適当。stroke(赤成分緑成分,青成分) */
      stroke(r%256, r*4%256, r*16%256);
      /* 点の描画 */
      rect(a, b, 1, 1);
    }
  }
}

/* 漸化式にもとづいてZnが発散するnを計算 */
int calc(float x, float y) {
  float tx, ty;
  float zx = 0.0;
  float zy = 0.0;

  for (int i = 1; i <= N; i++ ) {
    tx = zx;
    ty = zy;

    zx = tx*tx-ty*ty+x;
    zy = 2*tx*ty+y;

    /* 発散した(閾値を超えた)場合はiを返す */
    if ( zx*zx + zy*zy > L )
      return i;
  }
  
  /* 発散しなかった場合は0を返す */
  return 0;
}

 割りといい感じに描けた。

GUIライブラリのcontrolP5を使ってみる[Processing]

 Processingで何か作ろうかと思ったが,何も思いつかない。とりあえずProcessingでボタンやリストボックスなどのGUIのアプリケーションには必須のコントロールと呼ばれるものの使い方を覚えようと思う。

1.controlP5のインストール

ProcessingではcontrolP5というライブラリを使うことでボタンなどを扱う事ができる。Processingを起動し,上のメニューバーから「スケッチ」→「ライブラリのインポート」→「ライブラリの追加」を選択。

 上のテキストボックスにcontrolP5と入力して,選択してinstallをクリック。

2.スライドとボタンを使ってみる

exampleのプログラムを打ってボタンを配置して終わるのもつまらないのでこの間やったフラクタル図形のパラメータをいじるように設定してみる。
import controlP5.*;

/* Hフラクタル用のパラメータ */
int N = 5;
int L = 128;
/* コントロールを扱うControlP5クラス */
ControlP5 cp5;

void setup() {
  size(512, 512);

  /* コントロールの初期化 */
  cp5 = new ControlP5(this);

  /* Nの値を変化させるスライダーを追加 */
  cp5.addSlider("N")
    .setPosition(10, 10)/*位置*/
    .setSize(256, 10)/*大きさ*/
    .setRange(1, 8);/*値の範囲*/

  /* Lの値を変化させるスライダーを追加 */
  cp5.addSlider("L")
    .setPosition(10, 30)/*位置*/
    .setSize(256, 10)/*大きさ*/
    .setRange(10, 1024);/*値の範囲*/

  /* 押されたらbutton1_Clickを実行するボタンを追加 */
  cp5.addButton("button1_Click")
    .setPosition(10, 50)/*位置を設定*/
    .setSize(256, 10);/*大きさを設定*/
}

/* ボタンをクリックした際に呼び出される関数 */
public void button1_Click(){
  /* 画面を保存する */
  saveFrame("###.png");
}

void draw() {
  /* 背景を黒で塗りつぶす */
  background(0);
  /* 線の色を緑に指定 */
  stroke(0, 255, 0);

  /* 再帰関数を呼び出す */
  step(width/2, height/2, 0);
}

/* H-フラクタルを描く関数 */
void step(float x, float y, int n) {
  if ( n >= N )
    return;

  float len = L * pow(0.5, n);

  line(x-len, y, x+len, y);
  line(x-len, y-len, x-len, y+len);
  line(x+len, y-len, x+len, y+len);

  step(x-len, y-len, n+1);
  step(x-len, y+len, n+1);
  step(x+len, y-len, n+1);
  step(x+len, y+len, n+1);
}

 これでこの前やったHフラクタルのパラメータをいじれるようになった。