欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

010-lissajous(二)

程序员文章站 2022-03-09 20:22:39
...

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 pq=[0.01:0.01:1]

图 1 所示的动态图,初始值 p=0.01,q=1,每一帧的 p 值增幅是 0.01,也就是说 pq 以 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 变量)