GO随笔(ch1\lissajous)

代码:

package main

import (
	"image"
	"image/color"
	"image/gif"
	"io"
	"math"
	"math/rand"
	"os"
)

//!-main
// Packages not needed by version in book.
import (
	"log"
	"net/http"
	"time"
)

//!+main

var palette = []color.Color{color.White, color.Black}

const (
	whiteIndex = 0 // first color in palette
	blackIndex = 1 // next color in palette
)

func main() {
	//!-main
	// The sequence of images is deterministic unless we seed
	// the pseudo-random number generator using the current time.
	// Thanks to Randall McPherson for pointing out the omission.
	rand.Seed(time.Now().UTC().UnixNano())

	if len(os.Args) > 1 && os.Args[1] == "web" {
		//!+http
		handler := func(w http.ResponseWriter, r *http.Request) {
			lissajous(w)
		}
		http.HandleFunc("/", handler)
		//!-http
		log.Fatal(http.ListenAndServe("localhost:8000", nil))
		return
	}
	//!+main
	lissajous(os.Stdout)
}

func lissajous(out io.Writer) {
	const (
		cycles  = 5     // number of complete x oscillator revolutions
		res     = 0.001 // angular resolution
		size    = 100   // image canvas covers [-size..+size]
		nframes = 64    // number of animation frames
		delay   = 8     // delay between frames in 10ms units
	)
	freq := rand.Float64() * 3.0 // relative frequency of y oscillator
	anim := gif.GIF{LoopCount: nframes}
	phase := 0.0 // phase difference
	for i := 0; i < nframes; i++ {
		rect := image.Rect(0, 0, 2*size+1, 2*size+1)
		img := image.NewPaletted(rect, palette)
		for t := 0.0; t < cycles*2*math.Pi; t += res {
			x := math.Sin(t)
			y := math.Sin(t*freq + phase)
			img.SetColorIndex(size+int(x*size+0.5), size+int(y*size+0.5),
				blackIndex)
		}
		phase += 0.1
		anim.Delay = append(anim.Delay, delay)
		anim.Image = append(anim.Image, img)
	}
	gif.EncodeAll(out, &anim) // NOTE: ignoring encoding errors
}

运行结果:

 代码解析:

var palette = []color.Color{color.White, color.Black}

这句代码首先声明了一个变量"palette",它是一个颜色(Color)类型的切片(slice)。切片是一种动态数组,可以存储多个元素。在这个例子中,切片中存储了两个颜色,分别是color.White和color.Black。color.White代表白色,color.Black代表黑色。所以这段代码的意思是创建了一个包含白色和黑色两种颜色的调色板。

const (
	whiteIndex = 0 // first color in palette
	blackIndex = 1 // next color in palette
)

这段代码定义了两个常量,分别是whiteIndexblackIndex。常量是在程序运行期间不可更改的值。whiteIndex的值为0,它表示调色板中的第一个颜色。blackIndex的值为1,它表示调色板中的下一个颜色。这些常量的目的是为了提供对调色板中颜色索引的易读性和可维护性。通过使用这些常量,可以在代码中使用whiteIndexblackIndex来表示调色板中的特定颜色,而不必直接使用数字0和1。这样可以使代码更具可读性,并且如果需要更改调色板中的颜色顺序,只需修改常量的值即可,而不必在整个代码中查找和替换数字。其中 const 是 Go 语言中的关键字,用于声明常量。声明语句为:

const identifier [type] = value
rand.Seed(time.Now().UTC().UnixNano())

这段代码是使用Go语言中的rand包来设置随机数生成器的种子(seed)。在生成随机数时,需要一个种子来初始化随机数生成器,以确保每次运行程序时生成的随机数序列是不同的。time.Now().UTC().UnixNano()是一个时间相关的表达式,它获取当前时间的UTC时间戳,并以纳秒为单位返回一个整数值。这个整数值作为种子传递给rand.Seed()函数。通过使用当前时间的纳秒级时间戳作为种子,可以使每次运行程序时生成的随机数序列都是不同的。这样可以增加随机性,使得每次运行程序时得到的随机数更加随机和不可预测。

if len(os.Args) > 1 && os.Args[1] == "web" {
		//!+http
		handler := func(w http.ResponseWriter, r *http.Request) {
			lissajous(w)
		}
		http.HandleFunc("/", handler)
		//!-http
		log.Fatal(http.ListenAndServe("localhost:8000", nil))
		return
	}

这段代码是一个条件语句,用于判断命令行参数是否包含"web"。如果命令行参数中包含"web",则执行相应的代码块。它创建了一个处理函数handler,该函数接收一个http.ResponseWriter和一个http.Request作为参数。w是一个http.ResponseWriter类型的变量,它用于向客户端发送HTTP响应。r是一个指向http.Request类型的指针变量,它用于获取客户端发送的HTTP请求的信息。通过将http.ResponseWriter赋值给变量w,我们可以在处理函数中使用w来发送HTTP响应。通过将http.Request赋值给*r,我们可以通过*r来访问和操作HTTP请求的信息。他的下一句代码就用到了它:在这个处理函数中,调用了lissajous函数,并将http.ResponseWriter(w)作为参数传递给它。http.HandleFunc("/", handler)是将处理函数handler注册到默认的HTTP路由器中的根路径("/")上。这意味着当有HTTP请求发送到服务器的根路径时,将调用handler函数来处理该请求。

		log.Fatal(http.ListenAndServe("localhost:8000", nil))
		return

这段代码使用http.ListenAndServe函数来启动一个HTTP服务器,并监听本地主机的8000端口。它将服务器绑定到localhost:8000地址上,以便接收来自客户端的HTTP请求。http.ListenAndServe函数是一个阻塞调用,它会一直运行,直到发生错误或服务器被关闭。如果发生错误,它会返回一个非空的错误值。为了处理这种情况,代码使用log.Fatal函数来打印错误信息并终止程序的执行。在这段代码中,nil作为第二个参数传递给http.ListenAndServe函数。这表示我们没有指定一个自定义的路由器,而是使用默认的路由器。默认的路由器会根据请求的URL路径来匹配注册的处理函数,并调用相应的处理函数来处理请求。最后,代码中的return语句用于显式地结束函数的执行。在这种情况下,它可以用来确保在服务器启动失败时,程序不会继续执行后续的代码。综上所述,这段代码的作用是启动一个HTTP服务器,监听本地主机的8000端口,并使用默认的路由器来处理接收到的HTTP请求。如果启动失败,程序将打印错误信息并终止执行。

lissajous(os.Stdout)

这是在args[1]不为web的情况直接使用输入的值执行函数并将结果输出到终端上。

综上所述,这段main代码的作用是根据命令行参数的不同,以不同的方式展示生成的Lissajous曲线的动画或图像。如果命令行参数中包含"web",则通过HTTP服务器模式在浏览器中展示;否则,在终端上直接输出。

 func lissajous(out io.Writer)

 这段代码是一个函数声明,函数名为lissajous,它接受一个参数out,类型为io.Writer。函数的作用是生成Lissajous图形,并将结果写入到out中。

const (
		cycles  = 5     // number of complete x oscillator revolutions
		res     = 0.001 // angular resolution
		size    = 100   // image canvas covers [-size..+size]
		nframes = 64    // number of animation frames
		delay   = 8     // delay between frames in 10ms units
	)
  • cycles:表示x轴震荡器完成的完整周期数。
  • res:表示角度分辨率,即每个点之间的角度差。
  • size:表示图像画布的大小,范围为[-size, +size]。
  • nframes:表示动画帧的数量。
  • delay:表示帧之间的延迟,以10毫秒为单位。
	freq := rand.Float64() * 3.0 // relative frequency of y oscillator
	anim := gif.GIF{LoopCount: nframes}
	phase := 0.0 // phase difference
  • freq := rand.Float64() * 3.0:这行代码使用rand.Float64()函数生成一个0到1之间的随机浮点数,并将其乘以3.0,得到一个相对频率(relative frequency)的值。这个相对频率将用于控制y轴震荡器的频率。

  • anim := gif.GIF{LoopCount: nframes}:这行代码创建了一个gif.GIF类型的变量anim,并设置了它的LoopCount属性为nframes,即动画的循环次数。

  • phase := 0.0:这行代码初始化了一个名为phase的变量,用于表示相位差(phase difference)。

for i := 0; i < nframes; i++

这行代码表示一个循环,从0到nframes-1,用于生成指定数量的动画帧。

rect := image.Rect(0, 0, 2*size+1, 2*size+1)

这行代码创建了一个image.Rect类型的变量rect,表示一个矩形区域,其左上角坐标为(0, 0),右下角坐标为(2size+1, 2size+1)。

img := image.NewPaletted(rect, palette)

这行代码创建了一个新的image.Paletted类型的变量img,使用之前定义的rect作为图像的尺寸,并使用palette作为调色板。

for t := 0.0; t < cycles*2*math.Pi; t += res

这行代码表示一个循环,从0.0到cycles*2*math.Pi(周长),以res为步长,用于生成Lissajous图形中的每个点的坐标。

x := math.Sin(t)
y := math.Sin(t*freq + phase)

这行代码计算了x坐标,使用了正弦函数math.Sin()。计算了y坐标,使用了正弦函数,并乘以相对频率freq,并加上相位差phase

img.SetColorIndex(size+int(x*size+0.5), size+int(y*size+0.5), blackIndex)

这行代码将指定坐标位置的像素颜色设置为调色板中的黑色。

		phase += 0.1
		anim.Delay = append(anim.Delay, delay)
		anim.Image = append(anim.Image, img)

这行代码更新相位差phase的值,每次循环增加0.1。将延迟值delay添加到动画的延迟序列中。将生成的图像帧img添加到动画的图像序列中。

综上所述:这段代码的循环过程用于生成Lissajous图形的动画帧。在每个循环中,它创建一个新的图像对象,然后根据循环变量t计算每个点的坐标,并将对应的像素颜色设置为黑色。然后,它更新相位差phase的值,将延迟值delay添加到动画的延迟序列中,并将生成的图像帧添加到动画的图像序列中。最终,循环结束后,就生成了指定数量的动画帧,可以用于播放Lissajous图形的动画。

gif.EncodeAll(out, &anim) // NOTE: ignoring encoding errors

这段代码用于将生成的Lissajous图形动画编码为GIF格式,并将结果写入到out中。gif.EncodeAll(out, &anim):这行代码调用了gif包中的EncodeAll函数,将动画anim编码为GIF格式,并将结果写入到out中。out是一个io.Writer类型的参数,用于指定输出的目标位置

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值