Efeito plasma no terminal com Golang
Um dos programas que sempre achei interessantes são as demos com efeitos gráficos atrativos e códigos que extraem cada grama de performance da máquina. O efeito plasma é um dos que considero mais legais, simulando um fluido em movimento.
Para criar o efeito plasma, utilizo várias funções trigonométricas para calcular a cor de fundo de cada caractere e passo o tempo para gerar a animação. O código é relativamente simples, mas a otimização é um pouco mais complexa.
Calcular esses valores continuamente é muito lento e o terminal também não é muito rápido. Para evitar lentidão que terminal fique piscando a saída é pré-computo alguns dos cálculos.
func precalculateValues(r, c int) {
preSinX = make([]float64, c)
preCosX = make([]float64, c)
preSinY = make([]float64, r)
preCosY = make([]float64, r)
preSinXY = make([][]float64, r)
preCosXY = make([][]float64, r)
for i := 0; i < r; i++ {
preSinXY[i] = make([]float64, c)
preCosXY[i] = make([]float64, c)
}
for x := 0; x < c; x++ {
aX := (float64(x) / float64(c)) * math.Pi * 3
preSinX[x] = math.Sin(aX)
preCosX[x] = math.Cos(aX)
}
for y := 0; y < r; y++ {
aY := (float64(y) / float64(r)) * math.Pi * 3
preSinY[y] = math.Sin(aY)
preCosY[y] = math.Cos(aY)
for x := 0; x < c; x++ {
aXY := ((float64(x) +
float64(y)) / float64(c)) * math.Pi * 3
preSinXY[y][x] = math.Sin(aXY)
preCosXY[y][x] = math.Cos(aXY)
}
}
}
Pré-computar é apenas uma parte da otimização. Este código poderia ser mais rápido, mas já está rápido o suficiente sem prejudicar a legibilidade.
Outra otimização é pré-computar as cores. Para isso, uso o seguinte código:
func precalculateColorStrings() {
for i := 0; i < 256; i++ {
colorStrings[i] = "[48;5;" + strconv.Itoa(i) + "m[38;5;0m"
}
}
Dessa forma, evitamos recomputar muitas vezes a mesma string.
Falando em strings, outra otimização importante é enviar o máximo de dados para o terminal de uma vez. Para isso, uso o seguinte código, utilizando um buffer pré-alocado.
bufCap := 20000
buf := make([]byte, 0, bufCap)
Isso acelera bastante a execução do programa. Deixei o buffer com tamanho fixo, mas o ideal seria calcular seu tamanho baseado no tamanho do terminal. Porém, isso é difícil porque não basta multiplicar altura por largura; é preciso considerar o tamanho das strings e dos códigos de escape. Portanto, criei um buffer suficientemente grande.
O código fonte completo está no repositório do Grupo de Estudos de Go
Vídeo com a execução do programa: