Implementação do algorítmo Wave Function Collapse (WFC)

Joismar BragaJoismar Braga
5 min read

Mini projeto em P5.js que implementa uma versão básica do algoritmo Wave Function Collapse (WFC) para gerar padrões baseados em ladrilhos (tiles). O WFC é um algoritmo de geração procedural que cria padrões complexos a partir de regras locais. Ele começa com um estado "não observado" (todas as possibilidades estão abertas para cada ladrilho) e as "colapsa" com base em regras e observações.

⚙️ Como Funciona

A ideia central por trás desta implementação do WFC é:

  • Ladrilhos (Tiles): Cada ladrilho possui "bordas" definidas (representadas como strings como "AAA" ou "ABA"). Essas bordas ditam como os ladrilhos podem se conectar uns aos outros.

  • Propagação: Quando um ladrilho é "colapsado" (ou seja, seu tipo é escolhido para uma célula específica da grade), as opções de ladrilhos possíveis para seus vizinhos são reduzidas com base nas bordas do ladrilho colapsado. Isso "propaga" as restrições por toda a grade.

  • Entropia: O algoritmo prioriza o colapso de células com a menor "entropia" (ou seja, o menor número de opções de ladrilhos restantes). Isso ajuda a resolver primeiro as áreas de alta restrição.

🛠️ Tecnologias Utilizadas

  • JavaScript: A linguagem de programação principal.

  • p5.js: Uma biblioteca JavaScript para programação criativa, usada para renderizar gráficos e lidar com funções básicas de desenho.

✨ Funcionalidades Principais

  • Definição de Ladrilhos: Os ladrilhos são definidos com imagens e padrões de bordas, permitindo várias regras de conexão.

  • Regras de Adjacência Automáticas: O sistema analisa automaticamente as bordas de cada ladrilho para determinar quais outros ladrilhos podem ser colocados ao seu lado.

  • Colapso Baseado em Entropia: O algoritmo escolhe inteligentemente a próxima célula a ser colapsada, encontrando aquela com o menor número de opções de ladrilhos possíveis, guiando o processo de geração.

  • Depuração Visual:

    • Células Colapsadas: Exibem a imagem do ladrilho escolhido.

    • Células Não Colapsadas: Mostram o número de opções de ladrilhos restantes para aquela célula, fornecendo feedback visual sobre a entropia.

    • Guia de Ladrilhos: Um guia visual no lado direito da tela exibe todos os ladrilhos disponíveis e seus respectivos índices, útil para entender o conjunto de ladrilhos.

  • Propagação Passo a Passo: A função propagate usa setTimeout para introduzir um atraso, permitindo que você observe visualmente o processo passo a passo de colapso e propagação do algoritmo WFC.

🎮 Demonstração

🧩 Classes

Tile

Representa um único tipo de ladrilho.

  • img: O objeto de imagem p5.js para o ladrilho.

  • edges: Um array de strings representando as bordas do ladrilho (ex: ["top", "right", "bottom", "left"]).

  • analyze(tiles): Um método (provavelmente dentro da classe Tile) que pré-calcula vizinhos válidos para cada lado do ladrilho, comparando suas bordas com todos os outros ladrilhos disponíveis.

  • rotate(numRotations): Um método para criar um novo objeto Tile que é uma versão rotacionada do original, atualizando sua imagem e padrões de bordas de acordo.

Cell

Representa uma única célula na grade.

  • options: Um array de índices representando os tipos de Tile possíveis que podem ser colocados nesta célula.

  • collapsed: Um booleano indicando se um ladrilho foi escolhido para esta célula.

🚀 Funções Chave

  • preload(): Carrega as imagens dos ladrilhos antes do início do esboço.

  • setup(): Inicializa a tela, define os ladrilhos base, os rotaciona para criar variações, analisa as regras de adjacência para todos os ladrilhos e inicializa a grid com todas as opções de ladrilhos possíveis para cada célula.

  • draw(): O loop principal onde o algoritmo WFC é iniciado. Ele escolhe uma célula inicial aleatória e um ladrilho aleatório para colapsar e então chama propagate para iniciar o efeito cascata. É definido como noLoop() para rodar apenas uma vez após o colapso inicial.

  • drawGrid(): Renderiza o estado atual da grade, mostrando os ladrilhos colapsados ou o número de opções para as células não colapsadas. Também exibe o guia de ladrilhos.

  • drawText(pos, string, color): Função auxiliar para desenhar texto na tela, usada principalmente para exibir o número de opções em uma célula.

  • drawCell(pos, color): Função auxiliar para desenhar um retângulo colorido para células não colapsadas.

  • isValidNeighbor(neighbor): Verifica se as coordenadas de um vizinho estão dentro dos limites da grade e se a célula vizinha ainda não foi colapsada.

  • getNeighbors(pos): Retorna as coordenadas dos quatro vizinhos diretos de uma dada célula, juntamente com o lado da célula atual que está voltado para o vizinho.

  • getValidNeighbors(pos): Filtra os resultados de getNeighbors para retornar apenas vizinhos válidos e não colapsados.

  • intersectArrays(arr1, arr2): Uma função utilitária para encontrar elementos comuns entre dois arrays. Isso é crucial para reduzir opções durante a propagação.

  • addGlobalNeighbor(neighbor, cell): Adiciona uma célula vizinha válida e não colapsada a uma lista global (neighborsCells) para ser processada posteriormente durante a propagação. Esta lista garante que cada vizinho não colapsado seja considerado apenas uma vez.

  • propagate(pos, tile): O coração do algoritmo WFC:

    1. Marca a célula atual em pos como colapsada e atribui a ela o tile.

    2. Identifica vizinhos válidos e não colapsados.

    3. Para cada vizinho, atualiza suas options intersectando-as com os ladrilhos permitidos com base nas bordas do tile atual.

    4. Adiciona a célula vizinha atualizada a neighborsCells se ainda não estiver lá.

    5. Chama-se recursivamente com a próxima célula a colapsar, escolhida de neighborsCells como aquela com o menor número de opções (menor entropia). Um setTimeout é usado para visualizar o processo.

📋 Constantes e Variáveis

  • TILE_SIZE: O tamanho em pixels de cada ladrilho.

  • W, H: A largura e altura da grade em ladrilhos.

  • tiles: Um array que armazena todos os objetos Tile (incluindo as versões rotacionadas).

  • grid: Um array 2D de objetos Cell representando a grade WFC.

  • gridImgs: Um array 2D para armazenar o objeto Tile uma vez que uma célula é colapsada.

  • neighborsCells: Um array global usado durante a propagação para rastrear as células vizinhas não colapsadas que precisam ter suas opções atualizadas, classificadas por entropia.

  • IT: Um contador de iterações para prevenir loops infinitos durante a propagação (definido como 100).

  • imgT, imgI, imgL, imgB: Objetos p5.Image carregados em preload para diferentes tipos de ladrilhos.

0
Subscribe to my newsletter

Read articles from Joismar Braga directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Joismar Braga
Joismar Braga