Представленная на этом шаге программа демонстрирует основы применения стандартных пакетов Go для работы с изображениями, возможности которых мы используем для создания последовательности растровых изображений и их сборки в анимированное GIF- изображение. Это параметрические кривые, полученные с помощью гармонических колебаний в двух измерениях, таких как две синусоиды, поданные на входы х и у осциллографа. На рис. 1 приведено несколько примеров таких фигур.
Рис.1. Некоторые фигуры Лиссажу
Раскрыть/скрыть решение и комментарии.
package main import ( "image" "image/color" "image/gif" "io" "math" "math/rand" "os" ) import ( "log" "net/http" "time" ) var palette = []color.Color{color.White, color.Black} const ( whiteIndex = 0 // Первый цвет палитры blackIndex = 1 // Следующий цвет палитры ) func main() { rand.Seed(time.Now().UTC().UnixNano()) if len(os.Args) > 1 && os.Args[1] == "web" { handler := func(w http.ResponseWriter, r *http.Request) { lissajous(w) } http.HandleFunc("/", handler) log.Fatal(http.ListenAndServe("localhost:8000", nil)) return } lissajous(os.Stdout) } func lissajous(out io.Writer) { const ( cycles = 5 // Количество полных колебаний x res = 0.001 // Угловое разрешение size = 100 // Канва изображения охватывает [size..+size] nframes = 64 // Количество кадров анимации delay = 8 // Задержка между кадрами (единица - 10мс) ) freq := rand.Float64() * 3.0 // Относительная частота колебаний у anim := gif.GIF{LoopCount: nframes} phase := 0.0 //Разность фаз 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) // Примечание: игнорируем ошибки }
После импорта пакета, путь к которому содержит несколько компонент (наподобие image/color), мы обращаемся к пакету по имени последнего компонента. Таким образом, переменная color.White принадлежит пакету image/color, а gif.GIF принадлежит пакету image/gif.
Объявление const дает имена константам, т.е. значениям, которые фиксированы во время компиляции, таким как числовые параметры циклов, задержки и т.п. Подобно объявлениям var, объявления const могут находиться на уровне пакетов (так что имена видимы везде в пакете) или в функции (так что имена видимы только в пределах данной функции). Значение константы должно быть числом, строкой или логическим значением.
Выражения []color.Color{...} и gif.GIF{...} являются составными литералами — компактной записью для инстанцирования составных типов Go из последовательности значений элементов. В приведенном примере первый из них представляет собой срез, а второй — структуру.
Тип gif.GIF является структурным типом. Структура представляет собой группу значений, именуемых полями, зачастую различных типов, которые собраны в один объект, рассматриваемый как единое целое. Переменная anim является структурой типа gif.GIF. Структурный литерал создает значение структуры, поле LoopCount которого устанавливается равным nframes; все прочие поля имеют нулевые значения соответствующих типов. Обращение к отдельным полям структуры выполняется с помощью записи с точкой, как в двух последних присваиваниях, которые явно обновляют поля Delay и Image переменной anim.
Функция lissajous содержит два вложенных цикла. Внешний цикл выполняет 64 итерации, каждая из которых генерирует отдельный кадр анимации. Она создает новое изображение размером 201x201 с палитрой из двух цветов, белого и черного. Все пиксели изначально устанавливаются равными нулевому значению палитры, т.е. имеют нулевой цвет, который мы определили как белый. Каждая итерация внешнего цикла генерирует новое изображение путем установки черного цвета для некоторых пикселей. Результат добавляется (с помощью встроенной функции append) к списку кадров в anim, вместе с указанной ранее задержкой в 80 мс. Наконец последовательность кадров и задержек кодируется в виде изображения в формате GIF и записывается в выходной поток out. Типом out является io.Writer, что позволяет выполнять запись в множество возможных целевых мест назначения.
Внутренний цикл запускает два осциллятора. Осциллятор х представляет собой простую функцию синуса. Осциллятор у также является синусоидой, но ее частота относительно частоты осцилляторах является случайным числом от 0 до 3, а его фаза относительно осцилляторах изначально равна 0, но увеличивается с каждым кадром анимации. Цикл выполняется до тех пор, пока осциллятор х не выполнит пять полных циклов. На каждом шаге вызывается функция SetColorlndex для того, чтобы окрасить пиксель, соответствующий координате (х,y), в черный цвет (позиция 1 в палитре).
Функция main вызывает функцию lissajous, заставляя ее осуществлять запись в стандартный вывод, так что приведенная далее команда генерирует анимированный GIF. Достаточно выполнить команду \lissajous.exe >out.gif.
Архив примера можно взять здесь.
На следующем шаге рассмотрим пример SVG-представления трехмерного графика функции.