Primeira vez aqui? Seja bem vindo e cheque o FAQ!
x

Como replicar modelos de inflorescências usando Lindenmayer Systems (L-Systems)?

+1 voto
94 visitas
perguntada Ago 24 em Ciência da Computação por Renata Oliveira (46 pontos)  

Figura 3.13(a) do livro "The Algorithmic Beauty of Plants" de Przemyslaw Prusinkiewicz e Aristid Lindenmayer (1991).

A imagem será apresentada aqui.

Compartilhe
comentou Out 5 por Philippe Azevedo (16 pontos)  
editado Out 5 por Philippe Azevedo
-x-x-x-x-x-x-x-x-

2 Respostas

+1 voto
respondida Ago 24 por Renata Oliveira (46 pontos)  

Para replicar a figura da pergunta, foi utilizado o pacote turtle. Duas funções foram criadas, a primeira para desenhar o eixo principal (axis) e a segunda para as inflorescências em forma de espiral para a direita (spiral_right). Ambas as funções utilizam a recursão para desenhar a imagem. Na primeira função, é possível escolher o número de segmentos do eixo principal, o comprimento dos segmentos, e o tamanho das folhas, que são arcos de circunferências (portanto a escolha é do raio e da extensão do arco). As folhas aparecem alternadamente, à direita e à esquerda. Para replicar este padrão, se o número do segmento a ser desenhado for par, a folha é desenhada à esquerda, caso contrário a folha é desenhada à direita.

A segunda função desenha as inflorescências em formato de espiral. Da mesma forma, escolhe-se o número de segmentos e seu comprimento, o tamanho das pequenas folhas, o ângulo que será alterado para criar a espiral e o incremento deste ângulo a cada segmento. O formato em espiral é reproduzido reduzindo, a cada segmento, o ângulo que indica a direção em que a imagem será desenhada, ou seja, a cada segmento o cursor se vira um pouco mais à direita. Para uma espiral mais "fechada", basta aumentar o parâmetro increment da função.

from turtle import *

def axis(n, x, r, s):
    '''
    Draws the main axis
    n = number of segments
    x = length of each segment
    r = radius of the leaf
    s = size of the leaf
    '''
    forward(x/2)
    circle(-r,s) #draws an arc in clockwise direction
    circle(-r,-s) #goes back to the axis
    forward(x/2)
    if n==0:
        forward(x)  
    elif n>0:
        if n%2==0: #alternates the side of the leaves
            axis(n-1, x, -r, s)
        else: 
            axis(n-2, x, -r, s)
    else:
        return


def spiral_right(n, x, r, s, angle, increment):
    '''
    Draws the spiral figure to the right
    n = number of segments 
    x = length of segments
    r = radius of small leaf
    s = size of small leaf
    increment = higher value means a more closed spiral.
    To create the spiral, the direction the turtle faces must be changed in each segment
    '''

    seth(30 - angle) #turtle faces northeast
    circle(-r, s) #draws tiny leaf
    circle(-r,-s) #back to the axis
    seth(90-angle) #turtle faces north/northeast
    forward(x) #draws the stem that holds the inflorescence
    seth(0-angle)
    fillcolor('black') #draws the inflorescence
    begin_fill()
    circle(8)
    end_fill()
    seth(90-angle)
    backward(x) #back to the axis
    seth(50-angle) #turtle faces northeast
    forward(x) #segment of the axis
    if n==0:
        forward(x)
    if n>0:
        spiral_right(n-1, x, r, s, angle+increment, increment)
    else:
        return

Para desenhar a imagem, as duas funções são chamadas em sequência. Os parâmetros foram escolhidos de modo a replicar a figura do livro.

if __name__ == '__main__':
    begin_fill()
    screensize(200,1500)
    penup()    
    goto(0,0)  
    pendown()  
    left(90) #turtle faces north
    axis(17, 30, 30, 80)
    spiral_right(7, 25, 20, 50, 0, 25)
    done()

O resultado obtido foi a imagem abaixo
A imagem será apresentada aqui.

comentou Set 3 por Rodrigo Stuckert (46 pontos)  
Ola, Renata. Tudo bem? Achei muito boa sua solução, você resolveu o problema por recursão, como sugerido, e foi direto ao ponto nas explicações. A única sugestão que tenho a fazer é que talvez seja interessante descrever brevemente alguns comandos, como "penup" e "pendown", no último pedaço de código. Forte abraço!
comentou Set 13 por Renata Oliveira (46 pontos)  
Obrigada pela dica! De fato acabei não mencionando os comandos penup e pendown. Eles fazem parte da biblioteca turtle do python e determinam se as linhas estarão ou não sendo desenhadas enquanto o turtle se move. No código da resposta, o objetivo é que não seja desenhada uma linha enquanto o turtle vai até o ponto (0,0). Depois disso, a figura passa a ser desenhada enquanto o turtle está se movendo.
comentou Out 5 por Philippe Azevedo (16 pontos)  
Olá,

Vi seu código, ficou muito bom a simplificação utilizando a recursividade nas duas funções.
Minha observação fica em relação a esse trecho do código:

     elif n>0:
        if n%2==0: #alternates the side of the leaves
            axis(n-1, x, -r, s)
        else:
            axis(n-2, x, -r, s)

A alternância no sentido de produção das folhas se da pela alternância do sinal de r, a cada chamada da função seu sinal é trocado  e a folha tem um sentido diferente de criação.

Por outro lado eu retiraria esse if, de n interacoes solicitadas, seja na forma de 2k +1 , n impar, ou na forma 2k, n par, eh executado apenas k +1 vezes. Sugiro algo assim:

     elif n>0:
           axis(n-1, x, -r, s)

Assim n determinaria a quantidade de folhas e segmentos a serem criados.
0 votos
respondida Out 5 por Philippe Azevedo (16 pontos)  

Agora eu não sei, li o livro do rapaz, e quando ele expõe a gramática via o sistema L, temos um sentimento que devemos criar expressões geradas pela gramática e tratá-las graficamente.

Ou seja, da grámatica:
e0 = 'a' # axioma - condição inicial
p1 : a→ I[L]a #regra 1
p2 : a→ I[L]A #regra 2
p3 : A→ I[A]K #regra 3

Onde I = intervalo/segmento, L = folha (Leaf), A - Ápice (Apex) e K - Flor
Iremos utilizar a premissa de que com 10 interações (folhas e segmentos), se atinge a maturidade, onde a partir daí, um ramo onde se dá a inflorescencia espiral é criado.

import turtle
#Premissa
# Um L-Sistema consiste em um alfabeto de símbolos que podem ser usados para fazer uma cadeia de caracteres
# uma coleção de regras de produção (grámatica) que se expandem cada símbolo em algum maior cadeia de símbolos, 
# uma sequência inicial "axioma" a partir da qual começa a construção 
# e um mecanismo para traduzir as sequencias geradas em estruturas gráficas.

alfabeto = {'a','I','L','A','K','[',']'}

#axioma
e0 = 'a'
maturidade = 10 #qte de iteracoes para criar da espiral 

# gramatica
# p1 : a→ I[L]a 
# p2 : a→ I[L]A 
# p3 : A→ I[A]K

x_caule = 30 # tamanho do intervalo de crescimento do caule
r_caule = 30 # raio da folha do caule 
s_caule = 80  # tamanho da folha do caule - angulo do arco

x_espiral = 25 # tamanho do intervalo de crescimento da espiral
r_espiral = 20 # raio da folha na espiral
s_espiral = 50 # tamanho da folha na espiral - angulo do arco
incremento = 25 # o desvio angular efetuado em cada interacao para criar a espiral

def gramatica(palavra,iteracao):
    anexo = ""
    if palavra == 'a' and iteracao <=maturidade: # Regra 1
        anexo = 'I[L]a'   
    elif palavra == 'a' and iteracao >maturidade: # Regra 2
        anexo = 'I[L]A'
    elif palavra == 'A': # Regra 3
        anexo = 'I[A]K'          
    else:
        anexo = palavra    # nao aplica nenhuma regra
    return anexo

def aplicarGramaticarRec(sentenca, iteracao):
    anexo = ''
    palavra = ''
    palavra = sentenca[0]
    if iteracao ==0:
        return sentenca
    if palavra == 'a' and iteracao - maturidade >= 0: # Regra 1
        anexo = 'I[L]' + aplicarGramaticarRec(sentenca, iteracao-1) # I[L]a

    elif palavra == 'a' and maturidade > iteracao: # Regra 2
        anexo = 'I[L]' + aplicarGramaticarRec('A' + sentenca.replace('a','',1), iteracao-1) # I[L]A
    elif palavra == 'A': # Regra 3
        anexo = 'I[' + aplicarGramaticarRec(sentenca +']K', iteracao-1) # I[A]K
    else:
        anexo = palavra    # nao aplica nenhuma regra
    return anexo

def aplicarGramatica(sentenca, totalIteracoes):
    i = 0
    qteIteracoes = 0
    while True:
        letra = sentenca[0]
        if letra not in alfabeto:
            print('palavra inválida: ' + letra)
            break

        txtSubstituir = gramatica(letra,qteIteracoes)
        if txtSubstituir != letra:
            sentenca = sentenca.replace(letra,txtSubstituir,1)
            qteIteracoes = qteIteracoes + 1
        if qteIteracoes == totalIteracoes or i == len(sentenca)-1:
            break
        i=i+1
    return sentenca

def criaLeaf(t,maturidadeG): # cria a folha. se estiver maduro, iremos fazer a folha da inflorescencia
    global r_caule
    global r_espiral
    global s_caule
    global s_espiral
    if maturidadeG == False:
        t.circle(-r_caule,s_caule) 
        t.circle(-r_caule,-s_caule) 
        r_caule = -r_caule # permitir a alternacao de sentido na criacao da flor
    else:
        t.circle(-r_espiral,s_espiral) 
        t.circle(-r_espiral,-s_espiral) 

def criaIntervalo(t,maturidadeG): # cria o segmento. se estiver maduro, iremos fazer o da inflorescencia
    global x_caule
    global x_espiral
    global n
    if maturidadeG == False:
        t.forward(x_caule)
    else:
        t.forward(x_espiral)

def criaFlor(t,angulo,maturidadeG):
    t.seth(30 - angulo) 
    criaLeaf(t,maturidadeG)
    t.seth(90-angulo) 
    criaIntervalo(t,maturidadeG)
    t.seth(0-angulo)
    t.fillcolor('black') 
    t.begin_fill()
    t.circle(8)
    t.end_fill()
    t.seth(90-angulo)
    t.backward(x_espiral) 
    t.seth(50-angulo) 
    criaIntervalo(t,maturidadeG)


# a partir do texto de instrucoes obtido do L-Systems, vamos desenhar caractere a caractere
#maturidadeG = False
#n = 0
#angulo = 0
#primeiraFlor = True
def desenhar(t,instrucoes,maturidadeG,angulo,primeiraFlor):
    global incremento
    if len(instrucoes) == 0:
        return   
    cmd = instrucoes[0]
    if cmd == 'I' 
        if len(instrucoes) > 3: # evitar erro em tempo de execucao com tamanho de string menor que 3
            if instrucoes[instrucoes.index(cmd) + 2] == 'L': 
                criaIntervalo(t,maturidadeG)
    elif cmd == 'L':
        criaLeaf(t,maturidadeG)
    elif cmd == '[': # a ideia dos parenteses eh guardar o status da tartaruga em uma pilha, isso seria interessante implementar se fossemos criar galhos, por exemplo.
        if instrucoes[instrucoes.index(cmd) + 2] == '[' and maturidadeG == False:
            maturidadeG = True
    elif cmd == 'K':
        if primeiraFlor == True:
            criaIntervalo(t,False)
            primeiraFlor = False 
        criaFlor(t,angulo,maturidadeG)
        angulo = angulo + incremento
    insNovo = instrucoes[1:] #elimino o primeiro caractere e continuo a processar
    desenhar(t,insNovo,maturidadeG,angulo,primeiraFlor)


if __name__ == '__main__':
    maturidade = 10
    str = aplicarGramaticarRec(e0,18)
    #str = 'I[L]I[L]I[L]I[L]I[L]I[L]I[L]I[L]I[L]I[L]A'

    print(str)
    t = turtle.Turtle()        
    wn = turtle.Screen()
    wn.screensize(200,2600)
    t.speed(9)
    t.begin_fill()
    t.penup()    
    t.goto(0,0)  
    t.pendown()  
    t.left(90) #turtle faces north
    desenhar(t,str,False,0,True)
    wn.exitonclick()
...