Sistemas-L: Implementação em Java Luís Gil Setembro 2003 Conteúdo 1 Breve Introdução aos Sistemas-L 2 2 Utilização e invocação do programa 3 3 Ficheiro de descrição do sistema 4 4 Discussão da implementação 5 5 Resultados 7 Resumo Os Sistemas-L (ou L-Systems) são gramáticas cujas palavras geradas contém a descrição de uma imagem, e são utilizados para modelar a evolução de plantas e fractais. Este relatório descreve brevemente os Sistemas-L, apresenta o programa que fiz para obter imagens a partir de Sistemas-L, discute certos aspectos da implementação e mostra algumas imagens. estudante de Ciências Informáticas no Instituto Superior Técnico 1
1 Breve Introdução aos Sistemas-L Os Sistemas-L, ou sistemas de Lindenmayer, foram propostos em 1968 pelo biólogo Aristid Lindenmayer para descrever o crescimento de plantas de uma maneira precisa, através de padrões, utilizando uma gramática e um sistema de rescrita paralelo. Uma gramática é um quadruplo < V, Σ, S, R > V é um conjunto se simbolos não terminais, também designados por variáveis. Σ é um conjunto de simbolos terminais. S é o simbolo inicial, ou axioma como é designado nestes sistemas. R é o conjunto das produções do tipo: V {V Σ} O processo de crescimento das plantas em intervalos de tempo discretos, é descrito pelas produções, começando no axioma. O crescimento efectua-se substituindo simulataneamente numa palavra todos os simbolos não terminais pela sua respectiva produção. O procedimento anterior é repetido n vezes para se obter o n-ésimo grau de crescimento. Aqui está o exemplo típico (o significado dos símbolos [,],+ e - será explicado a seguir) : Axioma: G F FF G F[+G][-G]FG Vamos efectuar duas iterações de crescimento para exemplificar 1 : n=0: G n=1: F[+G][-G]FG n=2: FF[+F[+G][-G]FG][-F[+G][-G]FG]FFF[+G][-G]FG Agora temos de interpretar as palavras que produzimos. Para tal, utilizam-se gráficos de tartaruga semelhantes aos da linguagem de programação LOGO, que basicamente consistem em dar ordens de movimento e desenho num referencial XY a uma estrutura de dados que guarda a posição e orientação (ângulo com a horizontal) actual. No caso dos L-Systems, as ordens são dadas através dos caracteres que compõem a palavra a interpretar: Letras maiúsculas (símbolos não terminais): Andar em frente uma certa distância e desenhar uma linha. Letras minúsculas dos símbolos não terminais: distância sem desenhar. Andar em frente certa Símbolos + e - : rodar (sem desenhar) alguns graus para a esquerda ou direita respectivamente. 1 A itálico encontram-se as produções de G e a negrito as produções de F. 2
Símbolo [ : guarda a posição actual numa pilha. Símbolo ] : a posição actual torna-se a que está no topo da pilha. Os dois símbolos anteriores servem para desenhar ramificações da planta. Antes de desenhar uma ramificação é necessário guardar a posição actual numa pilha, para a obter depois novamente após o ramo estar desenhado. Figura 1: iterações para n=0,1 e 2, do exemplo anterior Os comandos referidos anteriormente servem apenas para efectuar desenhos a duas dimensões, pois é essa a capacidade do sistema que fiz. Existem também comandos para fazer desenhos a três dimensões e para fazer sistemas paramétricos - uma variável possui duas ou mais produções, que são escolhidas segundo um valor - mas não os vou descrever. Exemplo de produções paramétricas: Axioma: G(0) G(d) : d > 0 G(d-1) G(d) : d==0 F(1)[+G(4)][-G(4)]F(1)G(0) F(a) F(1.23*a) Neste caso F(x) e G(x) indicam que se deve desenhar uma linha com comprimento x. Os comandos para desenho a três dimensões e as explicações sobre sistemas paramétricos podem ser encontrados em [1], [4], [5], [6]. 2 Utilização e invocação do programa O programa SistemaL recebe um ficheiro contendo a descrição de um L-System e produz um ficheiro de imagem bitmap (bmp) com o mesmo nome. Alternativamente pode produzir uma sequência de ficheiros relativos às várias etapas do desenvolvimento do sistema. Para o ecrã é enviada informação sobre o tempo de execução de cada tarefa. O programa é evocado da seguinte maneira: java SistemaL [-s] ficheiro [saida], onde: -s é a opção para gerar uma sequência de imagens. ficheiro é o nome do ficheiro com o sistema a desenhar saida é o nome do ficheiro resultado (caso se queira que seja diferente do original). 3
3 Ficheiro de descrição do sistema A tabela seguinte apresenta a estrutura do ficheiro que contém a descrição do sistema e o significado de cada campo. Importante: os valores numéricos do ficheiro devem ser todos números inteiros! passos X número de etapas de crescimento angulo inicial X ângulo (em graus) inicial feito com a horizontal incremento angulo X ângulo (em graus) que os ramos rodam largura linha X largura da linha em pixeis (valor impar) decremento largura X decremento da largura (valor par) altura linha X altura da linha em pixeis decremento altura X decremento da altura axioma X axioma cor tronco R G B cor inicial do tronco em RGB entre 0 e 255 cor ramo R G B cor final dos ramos regra letra palavra produções da gramática regra letra palavra não existem restrições quanto ao número de regras A largura e a altura são decrementadas quando se inicia o processamento de um ramo, ou seja, quando se encontra o caracter [. A largura deve assumir um valor impar, e o decremento desta deve ser um valor par, para simplificar a implementação do algoritmo. Tanto a largura como a altura podem decrescer até ao valor mínimo de 1 pixel. A cor é uma interpolação entre a cor do ramo mais velho e a cor do ramo mais novo, e varia sempre que se processa um novo ramo. Para obter uma imagem com a mesma cor atribui-se aos dois campos as mesmas cores. Quando se descreve plantas podemos atribuir ao tronco a cor castanha (100 50 0) e ao ramo mais novo a verde (0 255 0). Desta forma obtém-se um tronco castanho, os ramos produzidos pela última iteração (mais novos) verdes, e os ramos intermédios com uma cor entre as duas anteriores. O ficheiro da figura 2 é o seguinte: passos 4 angulo_inicial 90 incremento_angulo 35 largura_linha 9 decremento_largura 2 altura_linha 13 decremento_altura 1 cor_tronco 150 70 0 cor_ramo 0 200 0 axioma F regra F F[+F]F[-F]F 4
Figura 2: exemplo da interpolação de cores 4 Discussão da implementação Passo a descrever alguns aspectos relevantes sobre a implementação do programa. A imposição da largura da linha ser impar introduz uma facilidade de desenho que passa a ser explicada. Suponha-se que vamos desenhar uma linha do ponto (5,5) ao ponto (5,10) com largura de três pixeis. Para tal, basta subtrair uma unidade à largura e dividir o resultado por dois. Deste modo sabemos quantas linhas temos que desenhar antes e depois do ponto (5,5). No nosso exemplo temos 3 1 = 2, 2/2 = 1, há que desenhar uma linha antes e uma depois do ponto (5,5). Desenham-se então as seguintes linhas: (4,5) a (4,10), (5,5) a (5,10) e (6,5) a (6,10). Impondo que o decremento da largura seja um número par, vamos obter sempre uma largura impar. Porém, este sistema tem um defeito. Se quisermos desenhar uma linha horizontal com largura de três pixeis, vamos obter uma linha com 1 pixel de largura. Exemplo: linha entre os pontos (5,5) e (15,5) com três pixeis de espessura. Desenham-se três segmentos de recta entre (4,5) e (14,5), (5,5) e (15,5), (6,5) e (16,5). Obtém-se uma sobreposição! 5
A solução adoptada para linhas não verticais é fazer variar os extremos das rectas ao longo do eixo dos YY, em vez do eixo dos XX. Para o exemplo anterior teríamos linhas de (5,4) a (15,4), (5,5) a (15,5) e (5,6) a (15,6). As dimensões da imagem são obtidas através do cálculo das coordenadas das extremidades da figura: x máximo e mínimo para a largura e y máximo e mínimo para a altura. Para tal é necessário efectuar as seguintes acções: Quando se processa a palavra, no momento da leitura de uma letra maiúscula, não se desenha logo a linha na imagem. Cria-se uma estrutura de dados para armazenar os dois pontos, e esta é guardada numa lista. Também se verifica se as coordenadas do ponto que acaba de ser calculado correspondem a novos máximos ou mínimos. No final do processamento temos uma lista com todas as linhas da figura e quatro valores com o x Max, x min, y Max e y min. A partir destes quatro valores fica-se a saber que largura = x Max x min e altura = y Max y min. As coordenada onde se inicia o desenho é a origem (0,0), facto que faz com que se obtenham linhas com extremidades negativas. Há que efectuar uma translação destas para o 1 o quadrante do referencial, onde ambas as componentes são positivas, para permitir o seu desenho na imagem. Para tal adiciona-se ao x de cada ponto o valor absoluto de x min e ao y de cada ponto o valor absoluto de y min. À adição anterior é preferivel somar também um valor que designo por moldura, para impedir que a espessura da linha não ultrapasse a fronteira da imagem. Deste modo obtemos uma imagem com uma moldura de pixeis em branco. A moldura é obtida calculando a largura máxima da linha, dividindo esta por 2 e somando 2 ao resultado. A moldura serve para evitar situações como a seguinte: no desenho de uma linha entre (0,0) e (0,5) com espessura 3, irá ocorrer uma pintura inválida de (-1,0) a (-1,5). Assim, temos os cálculos seguintes: largura = x Max x min + 2 moldura altura = y Max y min + 2 moldura novo x = x antigo + Abs(x min ) + moldura novo y = y antigo + Abs(y min ) + moldura A sequência de passos a efectuar para a geração da imagem é: 1. Obter a palavra em n passos a partir do axioma 6
2. Interpretar a palavra e obter a lista com as linhas e os valores de x Max, x min, y Max e y min 3. Calcular a largura e altura, e criar a imagem 4. Mover as linhas para o 1 o quadrante 5. Desenhar as linhas na imagem 5 Resultados Nesta secção são apresentadas algumas imagens e os respectivos ficheiros com a descrição. Figura 3: vento passos 7 angulo_inicial 90 incremento_angulo 20 largura_linha 9 decremento_largura 2 altura_linha 13 decremento_altura 1 cor_tronco 100 50 0 cor_ramo 0 200 0 axioma B regra A AA regra B A[-B]+C[-CA+B][A+C][-CA]B regra C C 7
Figura 4: arbusto passos 5 angulo_inicial 90 incremento_angulo 45 largura_linha 11 decremento_largura 2 altura_linha 5 decremento_altura 0 cor_tronco 150 40 0 cor_ramo 0 230 0 axioma F regra F FF-[-F+F+F]+[+F-F-F] Figura 5: Fractal do floco de neve passos 4 angulo_inicial 0 incremento_angulo 45 8
largura_linha 1 decremento_largura 0 altura_linha 7 decremento_altura 1 cor_tronco 0 0 0 cor_ramo 0 0 0 coordenadas 30 20 dim_imagem 900 220 axioma F regra F F+F--F+F Figura 6: Sequência do quadrado de Koch para 3 passos passos 3 angulo_inicial 90 incremento_angulo 90 largura_linha 1 decremento_largura 0 altura_linha 6 9
decremento_altura 0 cor_tronco 100 0 0 cor_ramo 100 0 0 axioma F+F+F+F regra F F+F-F-FF+F+F-F Figura 7: Outro floco de neve passos 3 angulo_inicial 0 incremento_angulo 45 largura_linha 1 decremento_largura 0 altura_linha 4 decremento_altura 1 cor_tronco 0 0 0 cor_ramo 0 0 0 axioma F--F--F--F regra F F+F--F+F Figura 8: Sequência de crescimento de uma erva 10
passos 4 angulo_inicial 80 incremento_angulo 20 largura_linha 9 decremento_largura 2 altura_linha 16 decremento_altura 2 cor_tronco 150 50 20 cor_ramo 50 180 10 axioma A regra A AB regra B C+B[-AB[-C]]C regra C A[+C] Referências [1] A. Lindenmayer, P. Prusinkiewicz, et al: The algorithmic beauty of plants, Springer-Verlag, Nova Iorque, 1990. [2] A. van Dam, J. Foley, J. Hughes, S. Feiner: Computer Graphics principles and practice, Addison Wesley. [3] A minha página na internet http://isabelle.math.ist.utl.pt/ l51334 [4] Introdução aos L-Systems. [5] L-Systems com desenho a 3 dimensões. [6] Outro tutorial sobre L-Systems com imagens 3D e sistemas paramétricos. 11