Go言語でマンデルブロ

マンデルブロ氏追悼兼ゴルーチンの練習として、Go言語でマンデルブロ集合を書いてみた。初めてマンデルブロ書いたけど、なんや深いところを理解しようとせずに実装するだけなら意外と簡単なんすね。あと、Go言語のライブラリは思いのほか充実してる。複素数普通に使えるし、ImageMagicとか使わなくても単体でPNGを書き出せるのもすばらしい。

package main

import (
  "cmath"
  "image"
  "image/png"
  "os"
)

type Settings struct {
  z0 complex128
  n complex128
  repeat int 

  viewCenter complex128
  viewWidth float64
  viewHeight float64

  imageWidth int 
  imageHeight int 
}

var settings = &Settings{
  z0 : complex128(cmplx(0, 0)),
  n : complex128(cmplx(2.0, 0)),
  repeat : 30, 
  viewCenter : complex128(cmplx(0, 0)),
  viewWidth : 3.5,
  viewHeight : 3.5,
  imageWidth : 300,
  imageHeight : 300,
}

func cmplx128(r float64, i float64) complex128 {
  return complex128(cmplx(r, i)) 
}

func checkMandelbrot(c complex128, res chan int) {
  z := settings.z0
  for i := 0; i < settings.repeat; i++ {
    z = cmath.Pow(z, settings.n) + c
    if 2.0 < cmath.Abs(cmath.Pow(z, 2)) {
      res <- i + 1
      return
    }
  }
  res <- -1
}

func calculate(results [][]chan int) {
  var comp complex128
  cr := real(settings.viewCenter) - settings.viewWidth / 2
  ci := imag(settings.viewCenter) - settings.viewHeight / 2
  dr := settings.viewWidth / float64(settings.imageWidth)
  di := settings.viewHeight / float64(settings.imageHeight)

  for y := 0; y < settings.imageHeight; y++ {
    results[y] = make([]chan int, settings.imageWidth)
    for x := 0; x < settings.imageWidth; x++ {
      results[y][x] = make(chan int)
      comp = cmplx128(cr + dr * float64(x), ci + di * float64(y))
      go checkMandelbrot(comp, results[y][x])
    }
  }
}

func generateImage(results [][]chan int) *image.RGBA {
  rgba := image.NewRGBA(settings.imageWidth, settings.imageHeight)
  for y := 0; y < settings.imageHeight; y++ {
    for x := 0; x < settings.imageWidth; x++ {
      res := <-results[y][x]
      if res < 0 {
        rgba.Set(x, y, image.RGBAColor{0, 0, 0, 255})
      } else {
        depth := uint8(25 * res)
        rgba.Set(x, y, image.RGBAColor{0, depth, depth, 255})
      }
    }
  }
  return rgba
}

func main() {
  var results [][]chan int
  results = make([][]chan int, settings.imageHeight)
  calculate(results)
  image := generateImage(results)
  png.Encode(os.Stdout, image)
}

設定をファイルかコマンドラインから読み取るようにしようと思ってたけど、面倒くさくなったので放置。