010-lissajous(二)

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


010-lissajous(二)_第1张图片
图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 以 0.01 为步长递增,一直增加到 1.

  • append 函数

append 函数是 go 的内建函数,用于给 slice 追加元素。定义如下:

func append(slice []Type, elems ...Type) []Type

append 第一个参数传入 slice 对象,第二个参数类似 C 语言里的变长参数。append 返回一个新的 slice. 如果传入的 slice 容量不足,append 会自动对其扩容。

3. 总结

  • 掌握动态图生成方法
  • 掌握 append 函数

练习:

  1. 尝试不同的相位(phi 变量)

你可能感兴趣的:(Go,语言学习笔记(更新中...),Go,语言修炼指南)