前回、スマホ上のGo言語でフルカラー画像出力ができるようになったので、今回はその応用?としてマンデルブロ集合を描いてみたいと思います。昔、カオスとかフラクタルとか流行った?時期にはよく見たですが、正直この頃は影が薄い?Go言語のプログラムの下敷きにしたのは10年以上前にEXCELのVBAで自作したコードです。
※「やっつけな日常」投稿順 indexはこちら
※Androidスマホ上のLinux環境、Termux上にインストールした golang でソースをビルドしています。実験に使用したGoのソース全文は末尾に。
今回は古いVBAのソースをコピーしたものに前回作ったGoのPPM出力関数をマージして、エディタでVBAをGoに一行一行書き直すというスタイルで「移植」を行いました。意外と簡単。
Go言語の「学び」としては制御構造ですかね。
-
- Goのループは for のみである。
- Goには switch がある。Cのswitchより強力、VBA的なコードもOK
for一つで、whileも do until も書かねばならないですが、記法はフレキシブルで応用が効きます。今回はVBAの do untilループが mandelbrot()関数の中にあったのですが、まったく問題なかったです。
またPythonのように switchが無く、if 文の連なりに書くのも悪くはないと思うのですが、switchある方が見やすいです。何といっても良いのが、break要らないこと。Cで書いていてbreak 書き忘れてツボにハマったことも何度もあり、慌て者としては嬉しいです。またC同様に変数値で分岐もできれば、VBA風に条件かけるところも便利。
スマホ上で編集しているところが以下に。
実機上での実行
今回描くのは、縦横200ドットサイズの小さなマンデルブロ集合ですが、まあ計算量は多めということでコンパイルして実行しました。
$ go build mandelbrot.go $ ./mandelbrot > mb.ppm $ convert mb.ppm mb.png
ビルドしてしまえば実行時間は気にならなかったです。200ドットサイズなど一瞬。できた mb.ppm は、例によって ImageMagick(要インストール)のconvert コマンドで、表のAndroidアプリが表示できる PNG形式に変換しております。
表示された画像が以下に。
久しぶりにマンデルブロ集合見たな~ フラクタルな感じ。どんな感じだ?
カラー画像が作れるといろいろ描けて嬉しいです。
やっつけな日常(6) スマホでGo! fmt.Printで今度はフルカラー画像出力 へ戻る
実験に使用したGoのソース
package main import "fmt" type ppmImage struct { width int height int cmax int BUF []int } func newPpmImage(w int, h int, cx int) *ppmImage { p := ppmImage{width: w, height: h, cmax: cx} p.BUF = make([]int, w*h*3) return &p } func dotXY(img *ppmImage, x int, y int, r int, g int, b int) bool { if x > (img.width - 1) { return false } else if y > (img.height - 1) { return false } else { img.BUF[3*(y*img.width + x)] = r img.BUF[3*(y*img.width + x) + 1] = g img.BUF[3*(y*img.width + x) + 2] = b } return true } func writePPM(img *ppmImage) bool { fmt.Println("P3") fmt.Println(img.width, " ", img.height) fmt.Println(img.cmax) for y := 0; y < img.height; y++ { for x := 0; x < img.width; x++ { fmt.Print( img.BUF[3*(y*img.width + x)], " ") fmt.Print( img.BUF[3*(y*img.width + x) + 1], " ") fmt.Println(img.BUF[3*(y*img.width + x) + 2]) } } return true } func mandelbrot(a float64, b float64) int { var xn, yn float64 var x float64 = 0.0 var y float64 = 0.0 var r float64 = 0.0 var cnt int = 0 for { xn = x * x - y * y + a yn = 2.0 * x * y + b r = xn * xn + yn * yn x = xn y = yn cnt = cnt + 1 if (cnt > 999) || (r > 100.0) { break } } return cnt } func drawMandelbrot(img *ppmImage) bool { var pa, pb float64 var mdlev int for i := 1; i < 200; i++ { pa = -1.5 + float64(i) / 100.0 for j := 1; j < 200; j++ { pb = 1.0 - float64(j) / 100.0 mdlev = mandelbrot(pa, pb) switch { case mdlev > 999: dotXY(img, i, j, 0, 0, 0) case mdlev > 20: dotXY(img, i, j, 255, 255, 255) case mdlev > 15: dotXY(img, i, j, 200, 200, 120) case mdlev > 10: dotXY(img, i, j, 150, 150, 120) case mdlev > 5: dotXY(img, i, j, 100, 100, 120) case mdlev > 3: dotXY(img, i, j, 60, 60, 120) case mdlev > 2: dotXY(img, i, j, 20, 20, 120) default: dotXY(img, i, j, 10, 10, 80) } } } return true } func main() { XMAX := 200 YMAX := 200 img := newPpmImage(XMAX, YMAX, 256) drawMandelbrot(img) writePPM(img) }