lissajous 曲線随着參數的不同,曲線的形狀也會随之改變。如果每次修改一下參數再生成一張圖檔,未免也太麻煩了。這一次,我們希望生成一個動态圖(GIF,Graphics Interchange Format).
前面我已經學會了将 in-memory 圖像編碼成某種格式(png, jpeg)的方法,如果要将 in-memory 編碼成 gif,那就使用 gif 編碼器就好了。同樣的,gif 也會有相應的
Encode
方法。不過,動态圖的一個特點是它是由很多很多的幀表示,每一幀都是一幀圖像。gif 也提供了一個
EncodeAll
圖像,用于将多個 in-memory 編碼成 gif.
1. gif 編碼
gif 包的
EncodeAll
定義如下:
func EncodeAll(w io.Writer, g *GIF) error
其中 GIF 定義是這樣的:
type GIF struct {
Image []*image.Paletted // in-memory 的 slice
Delay []int // 目前幀延時,機關是 10ms
LoopCount int // 循環次數
// ..
可以看到 GIF 這個『結構體』(暫且這麼稱呼它)裡有一個成員是 Image 『數組』。這就好辦了,隻要我們生成所有不同參數下的 in-memory 圖像再塞到這個『數組』就 ok。(注意,這裡我盡量避免使用 slice 這個類型,說數組你會更容易了解。實際上在 go 裡數組和 slice 是兩種不同的東西)
2. 程式
這個程式的路徑是
gopl/tutorial/image/lissajous/lissajous2.go
- 代碼
// lissajous2.go
package main
import (
"image"
"image/color"
"image/gif"
"math"
"os"
)
const (
size = 128
nframes = 100
)
func main() {
p := 1.0
q := 2.0
phi := 0.0
palett := []color.Color{color.RGBA{0xee, 0xee, 0xee, 0xff}, color.RGBA{0xff, 0, 0, 0xff}}
rec := image.Rect(0, 0, 2*size, 2*size)
s := float64(size - 10)
step := 0.01
// 生成 100 幀
anim := gif.GIF{LoopCount: 1}
for frames := 0; frames < nframes; frames++ {
img := image.NewPaletted(rec, palett)
for t := -2 * math.Pi; t <= 2*math.Pi; t += 0.0001 {
x := math.Sin(p * t)
y := math.Sin(q*t + phi)
img.SetColorIndex(size+int(s*x+0.5), size+int(s*y+0.5), 1)
}
anim.Image = append(anim.Image, img)
anim.Delay = append(anim.Delay, 10)
// 每一幀 q 遞增 0.01,這裡相當于 q/p 以 0.01 步長遞增
p += step
}
gif.EncodeAll(os.Stdout, &anim)
}
- 運作
$ go run lissajous2.go
圖1
pq=[0.01:0.01:1]
p
q
=
[
0.01
:
0.01
:
1
]
圖 1 所示的動态圖,初始值 p=0.01,q=1
p
=
0.01
,
q
=
1
,每一幀的 p 值增幅是 0.01,也就是說 pq
p
q
- append 函數
append 函數是 go 的内建函數,用于給 slice 追加元素。定義如下:
func append(slice []Type, elems ...Type) []Type
3. 總結
- 掌握動态圖生成方法
- 掌握 append 函數
- 嘗試不同的相位(
變量)phi