Alan A. A. Donovan and Brian W. Kernighan, The Go Programming Language. Addison-Wesley, 2016, Chapter 1, Tutorial

Exercises 1.5 and 1.6

Exercise 1.5: Change the Lissajous program’s color palette to green on black, for added authenticity. To create the web color #RRGGBB, use color.RGBA{0xRR, 0xGG, 0xBB, 0xff}, where each pair of hexadecimal digits represents the intensity of the red, green, or blue component of the pixel.

Exercise 1.6: Modify the Lissajous program to produce images in multiple colors by adding more values to palette and then displaying them by changing the third argument of SetColorIndex in some interesting way.

My solution

For Exercise 1.6, I defined four shades of gray and cycled them for each frame.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
package main

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

const (
	blackIndex = 0 // first color in palette
	whiteIndex = 1 // next color in palette
	greenIndex = 2 // for Exercise 1.5
	gray1Index = 3 // for Exercise 1.6
	gray2Index = 4
	gray3Index = 5
	gray4Index = 6
)

var (
	green   = color.RGBA{0, 255, 0, 255}
	gray1   = color.RGBA{211, 211, 211, 255} // light gray
	gray2   = color.RGBA{169, 169, 169, 255} // dark gray
	gray3   = color.RGBA{105, 105, 106, 255} // dim gray
	gray4   = color.RGBA{112, 128, 144, 255} // slate gray
	palette = []color.Color{color.Black, color.White, green,
		gray1, gray2, gray3, gray4}
)

// Create a Lissajous figure.
func lissajous(out io.Writer) {
	const (
		cycles  = 5     // number of complex 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

	// Set line color index value. The third argument of 
	// `image.Palleted.SetColorIndex()` must be of type `uint8`,
	// so cast it here.
	colorChoice := uint8(3)

	// Create `nframes` images, each representing a frame of the
	// animation, storing them in the struct `anim`, interspersed
	// with delays of the value of `delay` each.
	for i := 0; i < nframes; i++ {
		rect := image.Rect(0, 0, 2*size+1, 2*size+1)
		img := image.NewPaletted(rect, palette)

		// Cycle the line color choice.
		colorChoice += 1
		if colorChoice == 7 {
			colorChoice = uint8(3)
		}

		// Run the two oscillators.
		for t := 0.0; t < cycles*2*math.Pi; t += res {

			// x oscillator is a sine function.
			x := math.Sin(t)

			// Frequency of y oscillator incorporates a random
			// value and the increasing value of `phase`.
			y := math.Sin(t*freq + phase)

			// Set the pixel x,y to a randomly chosen color.
			img.SetColorIndex(
				size+int(x*size+0.5),
				size+int(y*size+0.5),
				colorChoice)
		}
		phase += 0.1
		anim.Delay = append(anim.Delay, delay)
		anim.Image = append(anim.Image, img)
	}

	// Encode the sequence of frames and delays in `anim` and
	// write to output.
	gif.EncodeAll(out, &anim)
}

func main() {
	lissajous(os.Stdout)
}

Exercise 1.5 output

1
$ go build solution.go && ./solution > out.gif && open out.gif

Exercise 1.6 output

1
$ go build solution.go && ./solution > out.gif && open out.gif