Os fractais geométricos

Até agora apenas vimos modelos onde os agentes têm regras de comportamento probabilísticas. Neste capítulo vamos ver modelos onde as regras de comportamento são deterministas, que usaremos para construir fractais geométricos. Para tal vamos aproveitar a capacidade que as tartarugas têm para desenhar trajectórias, como vimos no capítulo anterior, e introduziremos os botões de escolha múltipla.

Um fractal geométrico é um objecto que possui uma estrutura auto-semelhante em toda as escalas. Esta propriedade resulta do facto de serem construídos pela iteração ad infinitum da mesma regra de construção.

Muitas formas na natureza são aproximadas por esta geometria. No entanto, os fractais apresentados neste programa não foram inventados para simular as propriedades de padrões naturais, mas sim para mostrar que a matemática continha mais objectos para além das curvas e superfícies regulares da geometria tradicional.

Consideremos a curva do floco de neve. Começa-se com um segmento de recta, ao qual se chama iniciador, de seguida dividimo-lo em três segmentos iguais, depois substitui-se o terço do meio por um triângulo equilátero tirando-lhe a sua base. Neste momento completou-se a construção da unidade fundamental: –o gerador. A iteração deste processo consiste em aplicar a mesma regra a cada um dos segmentos de recta que resultam da iterada anterior, como ilustrado na figura. A curva do floco de neve é o objecto que se obtém no limite em que o número de iterações tende para infinito.

A auto-semelhança é o ingrediente do processo de construção, pois cada um dos quatro segmentos de recta contido na iterada k é a redução para 1/3 do comprimento do segmento de recta iniciador da iterada anterior. Consequentemente o objecto que resulta da passagem ao limite deste processo recursivo é exactamente auto-semelhante e é igual à ampliação de qualquer uma das suas partes.

Utilização

O programa permite seleccionar a construção de diferentes fractais geométricos. Por exemplo, para gerar a árvore de Sierpinski, começa-se por escolher um ponto no plano e desenha-se três segmentos de recta desde esse ponto até aos vértices de um triângulo equilátero centrado no ponto inicial.

A iteração seguinte obtém-se aplicando a mesma operação aos vértices do triângulo considerados como novos pontos iniciais e tomando segmentos de recta com metade do comprimento dos da iteração anterior. A árvore de Sierpinski é o fractal que resulta da iteração sucessiva desta regra de construção.

Podemos ver este procedimento em acção seleccionando a árvore de Sierpinski no botão de escolha múltipla, cf. figura da direita, e pressionando sucessivamente o botão Desenhar uma iteracao. Pressionando este botão executa-se uma só vez a regra de construção do fractal seleccionado. O botão Desenhar ("forever button") executa sucessivamente o procedimento de Desenhar uma iteracao até que o utilizador o volte a pressionar.

A opção quadrado de Sierpinski utiliza uma regra similar a esta com outra simetria, como pode ser visto nas imagens seguintes. Neste caso colocamos o interruptor mostra-tartarugas em on para facilitar a compreensão da regra de construção do fractal.

Este fractal é construído recursivamente da seguinte forma: começa-se por escolher um ponto do plano e um certo comprimento l, depois, ao longo da vertical e da horizontal, desenha-se um segmento de recta entre 1/2 e 5/6 do comprimento inicial, de seguida coloca-se o ponto gerador da iteração seguinte à distância l do ponto inicial e faz-se a mesma coisa para as quatro direcções diagonais, só que neste caso o comprimento a considerar é l2. A cada iteração o comprimento l é reduzido para 1/3 do comprimento anterior.

A escolha da opção tapete de Sierpinski fornece outra forma de construir o quadrado de Sierpinski. O processo de construção consiste em dividir o quadrado inicial em nove quadrados iguais, retirar o do meio e aplicar sucessivamente este procedimento aos quadrados que restam ao fim de cada iteração, conforme ilustrado nas imagens seguintes:

O utilizador pode modificar o aspecto visual do seu fractal escolhendo a cor da primeira turtle criada (cor-inicial). As turtles das gerações seguintes terão uma cor que corresponde à cor da sua progenitora incrementada de incremento (ver representação numérica das cores no capítulo 3). Desta forma o código de cores permite visualizar o resultado das sucessivas iterações da regra de construção.

O écran do computador tem um número finito de píxeis que correspondem à porção mínima (ponto) da imagem que pode se desenhada. Quando o comprimento a desenhar for inferior a 1 píxel, o programa informa-o desse facto e oferece-lhe a possibilidade de terminar a execução do programa (halt).

O leitor é agora convidado a experimentar estes fractais no seguinte applet:

Para correr este modelo no ambiente NetLogo, basta seguir o link fractais, se tiver instalado o software

Breve análise do código

O modelo está dividido nas seguintes secções:

Variáveis globais
iteracaocontém o número de iterações já decorridas
comprimentocontém o comprimento a ser utilizado na iteração corrente
corcontém a cor definida na iteração anterior
Rotinas principais
prepararlimpa e inicia as variáveis de acordo com o fractal pretendido, desenha a iteração 0 do fractal e cria uma turtle na posição especificada.
desenharitera as regras de construção do fractal escolhido e actualiza as variáveis iteracao e cor.
Rotinas auxiliares
prepara-arvore-de-sierpinski cria a iterada 0 da árvore de Sierpinski.
prepara-floco-de-nevecria a iterada 0 da curva do floco de neve.
prepara-quadrado-de-sierpinskicria a iterada 0 do quadrado de Sierpinski.
prepara-tapete-de-sierpinskicria a iterada 0 do tapete de Sierpinski.
desenha-arvore-de-sierpinskiitera as regras de construção da árvore de Sierpinski.
desenha-floco-de-neveitera as regras de construção da curva do floco de neve.
desenha-quadrado-de-sierpinskiitera as regras de construção do quadrado de Sierpinski.
desenha-tapete-de-sierpinskiitera as regras de construção do tapete de Sierpinski.
saltamove a turtle a distancia especificada sem que esta deixe rastro.
muda-coraltera a cor das turtles.
Função auxiliar
calcula-factorfunção necessária para saber quanto é que a turtle deve avançar em função da orientação (heading) desta.

O código

O código para gerar este programa é o seguinte. Este não é tão grande quanto parece, pois cada um dos quatro fractais tem regras de construção diferentes.

globals [ iteracao comprimento cor ]

to preparar
    ca
    set iteracao 0
    set cor cor-inicial
    cct 1 [
        set size 10
        set color cor-inicial
        if not mostra-tartarugas? [ ht ]
    ]
    if fractal = "arvore de Sierpinski" [ prepara-arvore-de-sierpinski ]
    if fractal = "floco de neve" [ prepara-floco-de-neve ]
    if fractal = "quadrado de Sierpinski" [ prepara-quadrado-de-sierpinski ]
    if fractal = "tapete de Sierpinski" [ prepara-tapete-de-sierpinski ]    
end

to desenhar
    if comprimento < 1 [ user-message "Ultrapassou o limite de resolucao do ecran." ]
    if fractal = "arvore de Sierpinski" [ desenha-arvore-de-sierpinski ]
    if fractal = "floco de neve" [ desenha-floco-de-neve ]
    if fractal = "quadrado de Sierpinski" [ desenha-quadrado-de-sierpinski ]
    if fractal = "tapete de Sierpinski" [ desenha-tapete-de-sierpinski ]
    set iteracao iteracao + 1
    muda-cor
end

to prepara-arvore-de-sierpinski
    ask turtles [ 
        setxy 0 -70 
        pd
    ]
    set comprimento 140
end

to desenha-arvore-de-sierpinski
    locals [ angulo ]
    ask turtles [
        set angulo 0
        hatch 3 [
            set heading angulo
            fd comprimento
            set angulo angulo + 120
        ]
        die
    ]
    set comprimento comprimento / 2
end

to prepara-floco-de-neve
    set comprimento 420
    ask turtles [ 
        setxy -121 -210
        pd
        repeat 3 [
            hatch 1 []
            fd comprimento
            rt 120
        ]
        die
    ]
    set comprimento comprimento / 3
end

to desenha-floco-de-neve 
    ask turtles [
        repeat 2 [
            hatch 1 []
            fd comprimento
            lt 60
            hatch 1 []
            fd comprimento
            rt 120
        ]
        die
    ]
    set comprimento comprimento / 3
end

to prepara-quadrado-de-sierpinski
    ask turtles [ 
        setxy 0 0
        pd
    ]
    set comprimento 128
end

to desenha-quadrado-de-sierpinski
    locals [ angulo ]
    ask turtles [
        set angulo 0
        hatch 8 [
            set heading angulo
            salta (comprimento / 2) * calcula-factor heading        
            fd (comprimento / 3 ) * calcula-factor heading
            salta (comprimento / 6) * calcula-factor heading
            set angulo angulo + 45
        ]
        die
    ]
    set comprimento comprimento / 3
end

to prepara-tapete-de-sierpinski
    set comprimento 243 
    ask patches with [ abs pxcor <= 121 and abs pycor <= 121 ][ set pcolor blue ]
    set comprimento comprimento / 3
end

to desenha-tapete-de-sierpinski
    locals [ lado angulo x y ]
    set lado (comprimento - 1) / 2
    ask turtles [
        without-interruption [
            set angulo 0
            set x xcor
            set y ycor
            hatch 8 [
                set heading angulo
                fd comprimento * calcula-factor angulo
                setxy round xcor round ycor
                set angulo angulo + 45
            ]
            ask patches in-radius (ceiling (lado * sqrt 2)) [
                if abs (pxcor - x) <= lado and abs (pycor - y) <= lado [
                    set pcolor black
                ]
            ]
            die
        ] 
    ]
    set comprimento comprimento / 3
end

to salta [ distancia ]
    pu
    fd distancia
    pd
end

to muda-cor
    set cor cor + incremento
    if cor = black [ set cor cor + incremento ]
    ask turtles [ set color cor ]
end

to-report calcula-factor [ angulo ]
    ifelse (angulo / 45) mod 2 = 0 [ report 1 ][ report sqrt 2 ] 
end

Vejamos em detalhe os conceitos que são novos:

Rotinas principais
preparar
to preparar
    ca
    set iteracao 0
    set cor cor-inicial
    cct 1 [
        set size 10
        set color cor-inicial
        if not mostra-tartarugas? [ ht ]
    ]
    if fractal = "arvore de Sierpinski" [ prepara-arvore-de-sierpinski ]
    if fractal = "floco de neve" [ prepara-floco-de-neve ]
    if fractal = "quadrado de Sierpinski" [ prepara-quadrado-de-sierpinski ]
    if fractal = "tapete de Sierpinski" [ prepara-tapete-de-sierpinski ]    
end

A rotina preparar tem o objectivo de gerar a iterada 0 do fractal escolhido, para tal limpa o écran e devolve às variáveis os seus valores iniciais.

O comando if not mostra-tartarugas? [ ht ] esconde as tartarugas criadas se a variável mostra-tartarugas definida no interruptor com o mesmo nome for false. O comando ht (hide-turtle) é equivalente a atribuir o valor true à variável hidden? da turtle correspondente. Tal implica que todas as suas descendentes (instrução hatch) herdarão esta característica.

A variável fractal é definida no botão de escolha múltipla com o mesmo nome e consoante o seu valor a rotina preparar chama a rotina auxiliar que prepara o fractal correspondente.

Para criarmos um botão de escolha múltipla clicamos em e em seguida na área de trabalho. Esta acção abrirá a caixa de diálogo Chooser como pode ser vista na figura:

nesta caixa especificamos quais são os valores possíveis para a variável fractal. Os valores das variáveis definidas por botões de escolha múltipla têm de ser números ou strings, no segundo caso têm de começar e de acabar com o símbolo ".

desenhar
to desenhar
    if comprimento < 1 [
        user-message "Ultrapassou o limite de resolucao do ecran."
    ]
    if fractal = "arvore de Sierpinski" [ desenha-arvore-de-sierpinski ]
    if fractal = "floco de neve" [ desenha-floco-de-neve ]
    if fractal = "quadrado de Sierpinski" [ desenha-quadrado-de-sierpinski ]
    if fractal = "tapete de Sierpinski" [ desenha-tapete-de-sierpinski ]
    set iteracao iteracao + 1
    muda-cor
end

A rotina desenhar parte da iterada 0 do fractal escolhido e aplica-lhe as regras de construção especificadas na rotina auxiliar correspondente.

O comando if comprimento < 1 [ user-message "Ultrapassou o limite de resolucao do ecran." ] abre uma caixa de dialogo (pop up) que mostra a mensagem "Ultrapassou o limite de resolucao do ecran.", quando a variável comprimento for inferior a 1 píxel.

Rotinas da árvore de Sierpinski
prepara-arvore-de-sierpinski
to prepara-arvore-de-sierpinski
    ask turtles [ 
        setxy 0 -70
        ask patch-here [ set pcolor cor ]
        pd
    ]
    set comprimento 140
end

A rotina prepara-arvore-de-sierpinski cria a iterada 0 do fractal árvore de Sierpinski e inicia a variável comprimento.

O comando ask patch-here [ set pcolor cor ] muda a cor do patch sobre o qual a turtle se encontra para a cor especificada.

desenha-arvore-de-sierpinski
to desenha-arvore-de-sierpinski
    locals [ angulo ]
    ask turtles [
        set angulo 0
        hatch 3 [
            set heading angulo
            fd comprimento
            set angulo angulo + 120
        ]
        die
    ]
    set comprimento comprimento / 2
end

A rotina desenha-arvore-de-sierpinski itera as regras de construção do fractal árvore de Sierpinski e actualiza a variável comprimento.

A declaração locals [ angulo ] indica a existência de uma variável local, o angulo, que vai ser posteriormente atribuído à orientação (heading) de cada turtle.

O comando hatch numero [ comandos ] funciona como um ciclo, i.e. cria uma turtle, executa a lista de comandos e repete o procedimento até que não hajam mais turtles para criar. Graças a este comportamento podemos fazer com que a orientação (heading) de cada "cria" rode de 120° para a direita relativamente à "cria" que foi criada antes com os comandos:

set heading angulo
set angulo angulo + 120

e ordenar que esta avance comprimento com o comando fd comprimento.

O comando die não mata todas as turtles, apenas mata as "progenitoras" pois o NetLogo "protege" as "crias", as quais só ficam activas na iteração seguinte. Por essa razão, neste caso, não precisamos de usar a instrução without-interruption. Para o NetLogo "próxima iteração" significa a próxima instrução ask, pelo que se estivéssemos a usar o seguinte código:

ask ConjuntoDeAgentes1 [ 
    comandos1
    hatch numero [ comandos2 ]
    ask ConjuntoDeAgentes2 [ comandos3 ]
    die
]

todos os agentes de ConjuntoDeAgentes1 morreriam, pelo que neste caso precisaríamos de usar a instrução without-interruption para que só morressem os "progenitores", como podemos ver no caso do tapete de Sierpinski.

Rotinas da curva do floco de neve
prepara-floco-de-neve
to prepara-floco-de-neve
    set comprimento 420
    ask turtles [ 
        setxy -121 -210
        pd
        repeat 3 [
            hatch 1 []
            fd comprimento
            rt 120
        ]
        die
    ]
    set comprimento comprimento / 3
end

A rotina prepara-floco-de-neve cria a iterada 0 do fractal floco de neve e inicia a variável comprimento.

Para gerar a iteração 0 a turtle 0 gera a cria 1, avança até à posição 2, roda 120° e gera a cria 2, depois avança até à posição 3, roda 120° e gera a cria 3, por fim avança até à posição 1 e morre, conforme esquematizado na figura da direita. Estas instruções estão condensadas no seguinte código:

repeat 3 [
    hatch 1 []
    fd comprimento
    rt 120
]
die
desenha-floco-de-neve
to desenha-floco-de-neve 
    ask turtles [
        repeat 2 [
            hatch 1 []
            fd comprimento
            lt 60
            hatch 1 []
            fd comprimento
            rt 120
        ]
        die
    ]
    set comprimento comprimento / 3
end

A rotina desenha-floco-de-neve itera as regras de construção do fractal floco de neve e actualiza a variável comprimento.

Para gerar a iterada seguinte, a turtle 0 gera a cria 1, avança 1/3 do comprimento, roda para a esquerda 60°, gera a cria 2, avança 1/3 do comprimento, roda para a direita 120° gera a cria 3, avança 1/3 do comprimento, roda para a esquerda 60°, gera a cria 4 e morre, conforme esquematizado nas imagens seguintes:

Estas instruções estão condensadas no seguinte código:

repeat 2 [
    hatch 1 []
    fd comprimento
    lt 60
    hatch 1 []
    fd comprimento
    rt 120
]
die
Rotinas do quadrado de Sierpinski
prepara-quadrado-de-sierpinski
to prepara-quadrado-de-sierpinski
    ask turtles [ 
        setxy 0 0
        pd
    ]
    set comprimento 128
end

A rotina prepara-quadrado-de-sierpinski cria a iterada 0 do fractal quadrado de Sierpinski e inicia a variável comprimento.

desenha-quadrado-de-sierpinski
to desenha-quadrado-de-sierpinski
    locals [ angulo ]
    ask turtles [
        set angulo 0
        hatch 8 [
            set heading angulo
            salta (comprimento / 2) * calcula-factor heading        
            fd (comprimento / 3 ) * calcula-factor heading
            salta (comprimento / 6) * calcula-factor heading
            set angulo angulo + 45
        ]
        die
    ]
    set comprimento comprimento / 3
end

A rotina desenha-quadrado-de-sierpinski itera as regras de construção do fractal quadrado de Sierpinski e actualiza a variável comprimento.

O caso do quadrado de Sierpinski é semelhante ao caso da árvore de Sierpinski, só que neste caso temos 8 turtles que avançam ao longo das direcções dos eixos coordenados, ou ao longo das direcções das diagonais, i.e. cada cria roda 45° relativamente à cria anterior. Por essa razão temos as instruções:

set heading angulo
set angulo angulo + 45

Designando por l o valor da variável comprimento, as crias que estão orientadas segundo os eixos saltam l/2, avançam l/3 e saltam l/6, enquanto que as crias orientadas segundo as diagonais saltam l2/2, avançam l2/3 e saltam l2/6. Para simplificar o código usamos a função calcula-factor, a qual fornece o valor 1 ou √2, consoante a cria esteja orientada segundo os eixos ou segundo as direcções diagonais. Em consequência de tal as regras de movimento das turtles ficam condensadas nas três seguintes instruções:

salta (comprimento / 2) * calcula-factor heading        
fd (comprimento / 3 ) * calcula-factor heading
salta (comprimento / 6) * calcula-factor heading
Rotinas do tapete de Sierpinski
prepara-tapete-de-sierpinski
to prepara-tapete-de-sierpinski
    set comprimento 243 
    ask patches with [ abs pxcor <= 121 and abs pycor <= 121 ][
        set pcolor blue
    ]
    set comprimento comprimento / 3
end

A rotina prepara-tapete-de-sierpinski cria a iterada 0 do fractal tapete de Sierpinski e inicia a variável comprimento.

A iterada 0 consiste em desenhar um quadrado azul de lado comprimento centrado na origem e em criar uma turtle na origem, conforme esquematizado na figura da direita.

desenha-tapete-de-sierpinski
to desenha-tapete-de-sierpinski
    locals [ lado angulo x y ]
    set lado (comprimento - 1) / 2
    ask turtles [
        without-interruption [
            set angulo 0
            set x xcor
            set y ycor
            hatch 8 [
                set heading angulo
                fd comprimento * calcula-factor angulo
                setxy round xcor round ycor
                set angulo angulo + 45
            ]
            ask patches in-radius (ceiling (lado * sqrt 2)) [
                if abs (pxcor - x) <= lado and abs (pycor - y) <= lado [
                    set pcolor black
                ]
            ]
            die
        ] 
    ]
    set comprimento comprimento / 3
end

A rotina desenha-tapete-de-sierpinski itera as regras de construção do fractal tapete de Sierpinski e actualiza a variável comprimento.

Para gerar a iterada seguinte a turtle 0 divide o quadrado onde se encontra em nove quadrados iguais e pinta de preto aquele onde se encontra. Cf. imagens seguintes.

Tal é feito usando os seguintes comandos:

ask patches in-radius (ceiling (lado * sqrt 2)) [
    if abs (pxcor - x) <= lado and abs (pycor - y) <= lado [
        set pcolor black
    ]
]

A variável lado designa a distância do centro do quadrado a um dos seus lados, logo lado = (comprimento - 1 )/ 2.

Poderíamos ter usado o seguinte comando

ask patches with [ abs (pxcor - x) <= lado and abs (pycor - y) <= lado ][
   set pcolor black
]

para realizar a mesma tarefa, porém este comando é muito menos eficiente em termos computacionais quando os quadrados são pequenos, pois a condição abs (pxcor - x) <= lado and abs (pycor - y) <= lado é verificada para todos os patches e não apenas para aqueles que estão dentro do menor círculo que contem o quadrado.

De seguida cria oito descendentes, envia-os para o centro dos oito quadrados adjacentes e morre. Usando-se para tal os seguintes comandos:

hatch 8 [
    set heading angulo
    fd comprimento * calcula-factor angulo
    setxy round xcor round ycor
    set angulo angulo + 45
] 

O comando setxy round xcor round ycor atribui a cada turtle as coordenadas do patch onde se encontra.

Rotinas auxiliares
salta
to salta [ distancia ]
    pu
    fd distancia
    pd
end

Move todas as turtles para a frente o número de passos dado no argumento mas sem pintar os patches por debaixo.

muda-cor
to muda-cor
    set cor cor + incremento
    if cor = black [ set cor cor + incremento ]
    ask turtles [ set color cor ]
end

Muda a cor das tartarugas de iteração em iteração, de acordo com o incremento.

Função auxiliar
calcula-factor
to-report calcula-factor [ angulo ]
    ifelse (angulo / 45) mod 2 = 0 [ report 1 ][ report sqrt 2 ] 
end

Função do angulo (orientação da turtle) que dá o valor 1 ou √2, consoante a orientação da turtle seja ao longo dos eixos ou ao longo das diagonais.