やっつけな日常(15) スマホでGo!じゃない。ヒルベルト曲線

Joseph Halfmoon

今回はヒルベルト曲線っす。フラクタルの定番。ダフィット・ヒルベルト大先生の御発見。空間を埋め尽くす空間充填曲線のひとつであります。勿論埋め尽くすところまで描かないけれど。プログラムは簡単。そのうえGWだし、ということでスマホ上でプログラムせず、Ubuntu20.04LTS上のGo言語で実習。手抜き。

超有名なフラクタル図形なので、解説ページなど多数発見(ぐぐっただけですが。)数学的な意味など説明しているページが多いなかで、以下のページがあり、純粋プログラミング的にこの手の図形を多数取り扱われていて感服いたしました。

ヒルベルト曲線

一か所、lurと書くべきところを lru と書かれているような(druの定義の中)。コマケー話ですが。

当方作成のGo言語版の実験プログラム

冒頭のアイキャッチ画像を描くのに使ったプログラムが以下です。Go言語のお勉強としてはほとんど進歩なし。今までに学んだことの繰り返し。忘却力に対抗するためには反復練習必要だと言い訳させていただきましょう。パラメータのうち、以下が重要であります。

    • -order オーダーを与える、アイキャッチ画像は5。多くすると急激に複雑になり計算量は増えていく。
    • -siz 一遍の長さのパラメータ。1.0とすると画像上の10ドットになるので適宜調整。

なお、自作の local/turtle モジュールに1関数追加してしまったので、そちらのソースを末尾に掲げました。本体コードが以下に(勿論スマホ上のGoでも走るはず。)

Hilbert.go

package main

import (
  "flag"
  "fmt"
  "local/turtle"
  "local/ppm"
)

func dru(t *turtle.TurtleGraphics, order int, siz float64) {
  if order == 0 {
    return
  } else {
    rdl(t, order - 1, siz)
    t.Step(0, -siz)
    dru(t, order - 1, siz)
    t.Step(siz, 0)
    dru(t, order - 1, siz)
    t.Step(0, siz)
    lur(t, order - 1, siz)
  }
}

func rdl(t *turtle.TurtleGraphics, order int, siz float64) {
  if order == 0 {
    return
  } else {
    dru(t, order - 1, siz)
    t.Step(siz, 0)
    rdl(t, order - 1, siz)
    t.Step(0, -siz)
    rdl(t, order - 1, siz)
    t.Step(-siz, 0)
    uld(t, order - 1, siz)
  }
}

func lur(t *turtle.TurtleGraphics, order int, siz float64) {
  if order == 0 {
    return
  } else {
    uld(t, order - 1, siz)
    t.Step(-siz, 0)
    lur(t, order - 1, siz)
    t.Step(0, siz)
    lur(t, order - 1, siz)
    t.Step(siz,0)
    dru(t, order - 1, siz)
  }
}

func uld(t *turtle.TurtleGraphics, order int, siz float64) {
  if order == 0 {
    return
  } else {
    lur(t, order - 1, siz)
    t.Step(0, siz)
    uld(t, order - 1, siz)
    t.Step(-siz, 0)
    uld(t, order - 1, siz)
    t.Step(0, -siz)
    rdl(t, order - 1, siz)
  }
}

func main() {
  sz      := flag.Float64("siz", 0.2, "Size")
  order   := flag.Int("order", 3, "Order")
  fnam    := flag.String("f", "Hilbelt_default.ppm", "PPM file name")
  verbose := flag.Bool("v", false, "verbose flag")
  flag.Parse()
  var img ppm.PpmImage
  XMAX:= 512
  YMAX:= 512
  img.InitPpmImage(XMAX, YMAX, 256)
  img.Verbose = *verbose
  var ttl turtle.TurtleGraphics
  ttl.InitTurtleGraphics(1, 1, 10.0, img.Line)
  ttl.Color(255, 0, 128)
  dru(&ttl, *order, *sz)
  if img.FwritePPM(*fnam) {
    fmt.Println("Success.")
  } else {
    fmt.Println("PPM file genaration, failed.")
  }
}

描けましたな。それだけかよ。GWだからって言って手抜きだな。自分。

やっつけな日常(14) スマホでGo!パラメータを変えると飽きない2分木?を描く へ戻る

やっつけな日常(16) スマホでGo!じゃない。「外部コマンドを実行」でコケたトホホな理由 へ進む

tutle.go 改訂版

package turtle

import "math"

type LineFunc func(x1 int, y1 int, x2 int, y2 int, r int, g int, b int) bool

type TurtleGraphics struct {
  xpos float64
  ypos float64
  direction float64
  ijSCALE float64
  iORG int
  jORG int
  ipos int
  jpos int
  iold int
  jold int
  colR int
  colG int
  colB int
  penDOWN bool
  line LineFunc
  verbose bool
}

func (t *TurtleGraphics) InitTurtleGraphics(x int, y int, sca float64, lf LineFunc) bool {
  t.xpos = 0.0
  t.ypos = 0.0
  t.direction = 0.0
  t.colR = 128
  t.colG = 128
  t.colB = 128
  t.penDOWN = true
  t.verbose = false
  t.line = lf
  t.ijSCALE = sca
  t.iORG = x
  t.jORG = y
  t.ipos = x
  t.jpos = y
  return true
}

func(t *TurtleGraphics) Mov(dist float64) bool {
  t.xpos += math.Cos(t.direction)*dist
  t.ypos += math.Sin(t.direction)*dist
  t.iold = t.ipos
  t.jold = t.jpos
  t.ipos = int(math.Round(t.xpos * t.ijSCALE)) + t.iORG
  t.jpos = t.jORG - int(math.Round(t.ypos * t.ijSCALE))
  if t.penDOWN {
    t.line(t.iold, t.jold, t.ipos, t.jpos, t.colR, t.colG, t.colB)
  }
  return true
}

func(t *TurtleGraphics) Rot(theta float64) bool {
  rad := theta * math.Pi / 180
  t.direction += rad
  if t.direction < (-math.Pi) {
    t.direction = 2.0*math.Pi + t.direction
  } else if t.direction > math.Pi {
    t.direction = t.direction - 2.0*math.Pi
  }
  return true
}

func(t* TurtleGraphics) Color(r int, g int, b int) {
  t.colR = r
  t.colG = g
  t.colB = b
}

func(t* TurtleGraphics) PenDown(x bool) {
  t.penDOWN = x
}

func(t *TurtleGraphics) Draw(x float64, y float64) bool {
  t.xpos = x
  t.ypos = y
  t.iold = t.ipos
  t.jold = t.jpos
  t.ipos = int(math.Round(t.xpos * t.ijSCALE)) + t.iORG
  t.jpos = t.jORG - int(math.Round(t.ypos * t.ijSCALE))
  if t.penDOWN {
    t.line(t.iold, t.jold, t.ipos, t.jpos, t.colR, t.colG, t.colB)
  }
  return true
}

func(t *TurtleGraphics) Step(x float64, y float64) bool {
  t.xpos = t.xpos + x
  t.ypos = t.ypos + y
  t.iold = t.ipos
  t.jold = t.jpos
  t.ipos = int(math.Round(t.xpos * t.ijSCALE)) + t.iORG
  t.jpos = t.jORG - int(math.Round(t.ypos * t.ijSCALE))
  if t.penDOWN {
    t.line(t.iold, t.jold, t.ipos, t.jpos, t.colR, t.colG, t.colB)
  }
  return true
}