Lissajous figures
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