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.

plasma no terminal com Go

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:

Cesar Gimenes

Última modificação