このエントリはGo その2 Advent Calendar 2015 - Qiitaの8日目です。
7日目のS_Shiomtoriさんの記事もコマンドラインツールの話でした。
% go get github.com/otiai10/amesh/amesh
% amesh -g
思ったことや詰まったこと書きます
Goでコマンドラインツールをつくりはじめるとき
- コンパイルが非常に早いので、ほぼスクリプト言語のようにソースファイルをWrite&Runして開発することができるから
- クロスコンパイルが容易なので、Goのコンパイル環境を要求せず、実行可能ファイルを納品できるから
- 並行処理を書きやすく、わりとデカめのことをさくっとやってのけたりするから
- flagパッケージわりと使いにくい
- flag.Args、flag.Arg(n)、os.Argsで迷う
- コマンドfooのとき
foo hoge -fuga
とfoo -fuga hoge
で挙動がけっこう違ったりする
今回該当するコードはこのへん
なお、Goでコマンドラインツールつくるときは@deeeetさんの記事がめっちゃ参考になります
- コマンドラインツールについて語るときに僕の語ること - YAPC::Asia Tokyo 2014
- 高速にGo言語のCLIツールをつくるcli-initというツールをつくった | SOTA
- Go言語でテストしやすいコマンドラインツールをつくる | SOTA
Goで画像を扱う
Goは標準ライブラリが上から下まで充実している。ここでは、imageパッケージとimage/drawパッケージの紹介。
package main import ( "fmt" "image" "os" // ここでimportしたフォーマットのみ // image.Decodeでデコードが可能 _ "image/png" // _ "image/gif" // _ "image/jpeg" // 複数取りうるならすべてimportしておけばよい ) func main() { f, _ := os.Open("ritsu.png") // image.Decodeはio.Readerインターフェースを受けるので // http.Response.Bodyとかでもいける img, format, err := image.Decode(f) // 画像フォーマットによらず、image.Imageは // カラーモデル ColorModel() と // 大きさ Bounds() と // 座標における色情報 At(x, y int) を提供するインターフェースを持つ fmt.Println(img.Bounds(), format, err) } // (0,0)-(256,256) png <nil>
で、image/drawはもっとすごい
みんな大好きアメッシュは、あれ実は3枚の画像から構成されていて、これをターミナルに表示しようと思ったら画像を合体させる必要があった。該当箇所はここ。
大きさが等しい3枚の画像だったので、あまり深く考えずにdraw.Drawに2枚の画像をぶっ込んでるだけ。
Goでターミナルの大きさを取得する
画像をドット絵にしなきゃいけない限り、表示範囲が小さいと意味不明な絵になってしまう。なので、ターミナルの可視領域を取得したい。 そんなことできるのだろうか、と思ってたらあった。
上では画像処理というわりと高レベルなパッケージを提供している傍ら、こちらではsyscall
というごくごく低レベルな機能も標準パッケージで提供している。Go言語しゅごい。
で、取得したターミナルのサイズで、画像を分割して、ターミナル上で表示できる限りの近似色を探して表示すればよい。
どうせなら雨降ってたらSlackでおしえてくれや
ターミナルに表示するとかしないとかはあんまり関係ないんですが、アメッシュの画像を取得し、画像の色情報をごにょごにょするので、「雨降ってる判定」だけ追加すれば通知できるんではないか、と思ったので作った。
amesh -d
とすれば、3分おきにアメッシュ見に行って、取得できた画像を雨判定する。もし必要な環境変数がそこにあれば、SlackとかTwitterとかで通知してくれるようにした。
touch develop.env docker-compose up
で立つはずです。
現状では、雨天判定を以下のようにしてる
// DefaultIsRainingFunc ... // とりあえず全ピクセル舐めで // ちょっとでも雨のピクセルが全体の30%を越えてたら雨ってことにする func DefaultIsRainingFunc(ev Event) bool { max := ev.Img.Bounds().Max var hit, all float64 = 0, float64(max.X) * float64(max.Y) for y := 1; y < max.Y-1; y++ { for x := 1; x < max.X-1; x++ { r, g, b, a := ev.Img.At(x, y).RGBA() if r+g+b+a > 100 { hit++ } } } var threshold float64 = 30 if (hit*100)/all > threshold { return true } return false // 快晴だこれ }
雨天判定がこれでいいのか、いい塩梅を探したいんだけど検証できてない。
ここ数日、雨がまったく降ってないので。
結論
- アメッシュ見に行ったほうが早い
明日はogataka50氏です