スマホの上でGo言語を勉強しております。前回、どうにかファイルシステム上にローカルなモジュールを配置できるようになりました。今回はモジュール内のパッケージに別ファイルで機能追加をしてみます。斜め線が描ける単なる Draw Line コマンドなのですが、最近はそういうプリミティブな関数を作ったりしないので苦戦。
※「やっつけな日常」投稿順 indexはこちら
※実機実験はAndroidスマホ上のTermux環境で行っています。使用しているGoのバージョンは1.18です。
ppmモジュール更新
前回以下のパスにppm モジュールを配置しました。ppmフォーマットでカラー画像を出力するためのモジュールです。といって100行にも満たないもの
go/src/local/ppm
今回機能拡張するにあたって一部マズイところを直しました。goのプロは、self とかthis とか書いたりしないらしいですが、このモジュールは this のまま押し通してます。その全文が以下に。(ppm.go)
package ppm import "fmt" type PpmImage struct { width int height int cmax int buf []int } func (this *PpmImage) InitPpmImage(w int, h int, cx int) bool { this.width = w this.height = h this.cmax = cx this.buf = make([]int, w*h*3) return true } func (this *PpmImage) DotXY(x int, y int, r int, g int, b int) bool { if (x < 0) || (x >= this.width) || (y < 0) || (y >= this.height) { return false } else { this.buf[3*(y*this.width + x)] = r this.buf[3*(y*this.width + x) + 1] = g this.buf[3*(y*this.width + x) + 2] = b } return true } func (this *PpmImage) WritePPM() bool { fmt.Println("P3") fmt.Println(this.width, " ", this.height) fmt.Println(this.cmax) for y := 0; y < this.height; y++ { for x := 0; x < this.width; x++ { fmt.Print( this.buf[3*(y*this.width + x)], " ") fmt.Print( this.buf[3*(y*this.width + x) + 1], " ") fmt.Println(this.buf[3*(y*this.width + x) + 2]) } } return true }
さて機能の追加は以下の2つです
-
- 四角い箱(Box)の描画
- 直線(Line)描画
追加ファイルは同じディレクトリの中に gra2d.go という名前のファイルで、しかしソース先頭の package名は ppm とppm.go と同じにして作ってあります。
※以下ファイルにはバグあり、新版ご参照ください。2022年4月25日
package ppm import ( "fmt" "os" ) func (this *PpmImage) Box(x1 int, y1 int, x2 int, y2 int, r int, g int, b int) bool { if x1 > x2 { x1, x2 = x2, x1 } if y1 > y2 { y1, y2 = y2, y1 } for x := x1; x<=x2; x++ { this.DotXY(x, y1, r, g, b) this.DotXY(x, y2, r, g, b) } for y := y1; y<=y2; y++ { this.DotXY(x1, y, r, g, b) this.DotXY(x2, y, r, g, b) } return true } func (this *PpmImage) Line(x1 int, y1 int, x2 int, y2 int, r int, g int, b int) bool { var dx int var dy int var x, y int var revx bool = false var revy bool = false if x1 > x2 { dx = x1 - x2 revx = true } else { dx = x2 - x1 } if y1 > y2 { dy = y1 - y2 revy = true } else { dy = y2 - y1 } if dx > dy { fmt.Fprintln(os.Stderr, "X mode ", dx, dy) if revx { fmt.Fprintln(os.Stderr, "REVX") x1, x2 = x2, x1 y1, y2 = y2, y2 } dy = y2 - y1 for x= 0; x<=dx; x++ { if dx != 0 { y= (dy * x) / dx } else { y= 0 } this.DotXY(x+x1, y+y1, r, g, b) } } else { fmt.Fprintln(os.Stderr, "Y mode", dx, dy) if revy { fmt.Fprintln(os.Stderr, "REVY") x1, x2 = x2, x1 y1, y2 = y2, y2 } dx = x2 - x1 for y= 0; y<=dy; y++ { if dy != 0 { x= (dx * y) / dy } else { x= 0 } this.DotXY(x+x1, y+y1, r, g, b) } } return true }
箱の描画の方は、縦横90度縛りなので簡単ですが、直線描画の方は斜め線を引かないとならないのでメンドイ(まだバグ残っているかも)です。思ったより長く(汚く)なってしまいました。まあおいおいテコ入れ。なお、デバッグ用にstderrへの出力が散りばめてあります。落ち着いたら取り外します。
ちょっと今回多用したのが変数のスワップの定石
x1, x2 = x2, x1
というくだり。Cのように ++ とも書ければ、Pythonみたいなことも出来ると。Goは便利ですな。
呼び出し側、テスト用のメイン関数
呼び出し側は、前回と同じ以下のディレクトリ内に linePPM.go なるファイル名でソースを作りました。
go/proj/s2
テキトーに箱を描いたあとに、斜め線を描いてテストとしているもの。ザルですが。
package main import "local/ppm" func main() { var img ppm.PpmImage XMAX:= 256 YMAX:= 256 img.InitPpmImage(XMAX, YMAX, 256) img.Box ( 10, 10, 250, 250, 0, 0, 255) img.Box (100, 10, 150, 250, 0, 255, 255) img.Line(128, 128, 250, 10, 255, 255, 0) img.Line(128, 128, 250, 128, 255, 255, 0) img.Line(128, 128, 250, 250, 255, 255, 0) img.Line(128, 128, 150, 10, 255, 0, 0) img.Line(128, 128, 150, 250, 255, 0, 0) img.Line(128, 128, 10, 10, 0, 255, 255) img.Line(128, 128, 10, 128, 0, 255, 255) img.Line(128, 128, 10, 250, 0, 255, 255) img.Line(128, 128, 100, 10, 255, 0, 255) img.Line(128, 128, 100, 250, 255, 0, 255) img.WritePPM() }
ここで以下のコマンドを走らせると、下の画像ファイル lp.png が得られます。
$ go run linePPM.go > lp.ppm $ convert lp.ppm lp.png
描いた「線画」が以下に。
Go言語のパッケージ機能、同じpackage名をつけてファイルを並べれば良いというのは、何気に便利でないかい?機能毎に小分けにファイル作って行けばOKだし。
線が引けるとまた混沌が這いよってくる気がしないでもない。ホントか?