
前回、スマホ上の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)
}


