sábado, 12 de outubro de 2013

Devagar se vai ao longe

O módulo turtle é um velho conhecido nosso. Permite fazer desenhos e gráficos. Vamos supor que queríamos reproduzir o sinal de local perigoso devido a radioactividade.
Vamos transformar esta questão em mais um exercício para exemplificar como se pode dominar a complexidade. Primeiro, dividimos o problema em dois sub-problemas: (1) desenhar o fundo (um quadrado amarelo com um bordo espesso) e (2) ... desenhar o resto.
"""Devagar se vai ao longe: radioactividade."""
import turtle

def radioactividade():
    # Desenha fundo
    fundo()
    # Desenha frente
    frente()

def fundo():
    pass

def frente():
    pass
    
if __name__ == '__main__':
    radioactividade()
    turtle.exitonclick()
Pode parecer estranho, mas este programa já pode ser executado, embora não faça (quase) nada. Com este modelo fica claro que apenas tomámos a decisão de dividir o problema em dois. Nada mais. Tentemos resolver agora a primeira questão. Sabemos desenhar quadrados em função do comprimento do lado.
"""Devagar se vai ao longe: radioactividade."""
import turtle

def radioactividade(lado):
    # Desenha fundo
    fundo(lado)
    # Desenha frente
    frente()

def fundo(lado):
    for i in range(4):
        turtle.forward(lado)
        turtle.left(90)
    turtle.hideturtle()

def frente():
    pass
    
if __name__ == '__main__':
    radioactividade(100)
    turtle.exitonclick()
Ao concretizar introduzimos uma variável (lado) que terá que ser argumento do programa principal (radioactividade) e do fundo.Vamos completar a especificação tornando possível definir a cor e a espessura do bordo (não nos vamos preocupar para já nem com o posicionamento do quadrado nem com a orientação.)
"""Devagar se vai ao longe: radioactividade."""
import turtle

def radioactividade(lado):
    # Desenha fundo
    fundo(lado,'yellow',4)
    # Desenha frente
    frente()

def fundo(lado, cor, espessura):
    # Atributos
    turtle.width(espessura)
    turtle.fillcolor(cor)
    turtle.begin_fill()
    for i in range(4):
        turtle.forward(lado)
        turtle.left(90)
    turtle.end_fill()
    turtle.hideturtle()

def frente():
    pass
    
if __name__ == '__main__':
    radioactividade(100)
    turtle.exitonclick()
A solução acima cumpre a primeira parte. Se mandarmos correr o programa o que aparece é:
Notemos de passagem que a função fundo permite desenhar um fundo quadrado com qualquer cor e qualquer espessura. No entanto, preferimos não tornar essas opções possíveis no nosso problema inicial pelo que a cor e a espessura não são parâmetros da função radioactividade. Agora é a vez do segundo sub-problema. Vamos basear-nos no princípio ilustrado em post anterior: olhar e ver! E vemos a existência de um sector circular repetido três vezes e de uma circunferência. Daí que...
"""Devagar se vai ao longe: radioactividade."""
import turtle

def radioactividade(lado):
    # Desenha fundo
    fundo(lado,'yellow',4)
    # Desenha frente
    frente()

def fundo(lado, cor, espessura):
    # Atributos
    turtle.width(espessura)
    turtle.fillcolor(cor)
    turtle.begin_fill()
    for i in range(4):
        turtle.forward(lado)
        turtle.left(90)
    turtle.end_fill()
    turtle.hideturtle()

def frente():
    # Sectores
    sector()
    sector()
    sector()
    # Circunferência
    circunferencia()

def sector():
    pass

def circunferencia():
    pass

    
if __name__ == '__main__':
    radioactividade(100)
    turtle.exitonclick()
É claro que não avançámos muito pois estão ainda muitas coisas por decidir: posição, orientação,cor de fundo, amplitude e raio dos sectores, e também a posição, cor de fundo, espessura do bordo, cor do bordo e raio da circunferência. Vamos tratar dessas questões mas, para variar, vamos começar pela circunferência. Como se vai ver vamos ter que fazer ligeiras alterações no código anterior, uma vez mais determinado pelo facto de que os sub-problemas não são independentes. Em particular, o posicionamento da circunferência depende do posicionamento do fundo. Vamos então generalizar, incluindo a posição como argumento do problema.
"""Devagar se vai ao longe: radioactividade."""
import turtle

def radioactividade(posx, posy,lado):
    # Desenha fundo
    fundo(posx, posy,lado,'yellow',4)
    # Desenha frente
    frente(lado)

def fundo(lado, cor, espessura):
    # Atributos
    turtle.width(espessura)
    turtle.fillcolor(cor)
    turtle.begin_fill()
    for i in range(4):
        turtle.forward(lado)
        turtle.left(90)
    turtle.end_fill()
    turtle.hideturtle()

def frente(posx, posy, lado):
    # Sectores
    sector()
    sector()
    sector()
    # Circunferência
    circunferencia(posx+lado/2,t posy+(lado/2) - (lado/10), lado/10, 'black','yellow',2)

def sector():
    pass

def circunferencia(posx,posy,raio,cor_fundo,cor_bordo, espessura):
    # Posiciona
    turtle.penup()
    turtle.goto(posx,posy)
    turtle.pendown()
    # Define atributos
    turtle.pencolor(cor_bordo)
    turtle.fillcolor(cor_fundo)
    turtle.width(espessura)
    # Desenha
    turtle.begin_fill()
    turtle.circle(raio)
    turtle.end_fill()

    
if __name__ == '__main__':
    radioactividade(400)
    turtle.exitonclick()
Para além do que já foi referido tivemos que ter em atenção aspectos como o facto de o raio ter Devemos também ter em conta que, se queremos o centro da circunferência no ponto (x,y), temos que ter a tartaruga afastada dessa posição, numa das coordenadas, de um valor igual ao raio. Qual das coordenadas e como afastamos depende da orientação da tartaruga. Do ponto de vista da tartaruga o centro está sempre à sua esquerda! Executemos para ver, um pouco como São Tomé.
Nada mau. Já faltou mais... Chegou a vez dos sectores. Aqui se queremos ter uma única definição que possa ser aplicada aos três sectores a única diferença significativa será a orientação. Claro que uma vez mais temos que pensar no valor da posição, do raio, da amplitude e da cor de fundo. Embora sejam os mesmos nos três casos, vamos implementar uma solução geral que possa eventualmente ser usada noutro tipo de problemas ( se pensar bem vai ver que encontra uma outra aplicação para o código geral ...).Vejamos então a nossa solução.
"""Devagar se vai ao longe: radioactividade."""
import turtle

def radioactividade(posx,posy,lado):
    # Desenha fundo
    fundo(posx, posy,lado,'yellow',4)
    # Desenha frente
    frente(posx,posy,lado)

def fundo(posx,posy,lado, cor, espessura):
    # Define posicao
    turtle.penup()
    turtle.goto(posx,posy)
    turtle.pendown()
    # Atributos
    turtle.width(espessura)
    turtle.fillcolor(cor)
    turtle.begin_fill()
    for i in range(4):
        turtle.forward(lado)
        turtle.left(90)
    turtle.end_fill()
    turtle.hideturtle()

def frente(posx,posy,lado):
    # Sectores
    sector(posx+lado/2,posy+lado/2,0,0.4*lado,60,'black')
    sector(posx+lado/2,posy+lado/2,120,0.4*lado,60,'black')
    sector(posx+lado/2,posy+lado/2,240,0.4*lado,60,'black')
    # Circunferência
    circunferencia(posx+lado/2,posy+(lado/2) - (lado/10), lado/10, 'black','yellow',2)

def sector(posx,posy,orientacao,raio, angulo, cor_fundo):
    # Atributos
    turtle.penup()
    turtle.goto(posx,posy)
    turtle.pendown()
    turtle.setheading(orientacao)
    turtle.fillcolor(cor_fundo)
    turtle.begin_fill()
    # Desenha
    turtle.forward(raio)
    turtle.left(90)
    turtle.circle(raio, angulo)
    turtle.left(90)
    turtle.forward(raio)
    turtle.left(180-angulo)
    turtle.end_fill()
    turtle.hideturtle()

def circunferencia(posx,posy,raio,cor_fundo,cor_bordo, espessura):
    # Posiciona
    turtle.penup()
    turtle.goto(posx,posy)
    turtle.pendown()
    # Define atributos
    turtle.pencolor(cor_bordo)
    turtle.fillcolor(cor_fundo)
    turtle.width(espessura)
    # Desenha
    turtle.begin_fill()
    turtle.circle(raio)
    turtle.end_fill()
    turtle.hideturtle()

    
if __name__ == '__main__':
    radioactividade(-100,-100,400)
    turtle.exitonclick()
Como se vê na solução apresentada, os sectores estão centrados e têm um raio de tamanho igual a 10% do lado. No entanto se executarmos este código verificamos que agora a circunferência não está bem posicionada. A razão está no facto de os problemas sector e circunferência ... não serem independentes. O desenho dos sectores altera a orientação final da tartaruga! A solução mais simples e cómoda é tornar a orientação um parâmetro da circunferência.
"""Devagar se vai ao longe: radioactividade."""
import turtle

def radioactividade(posx,posy,lado):
    # Desenha fundo
    fundo(posx, posy,lado,'yellow',4)
    # Desenha frente
    frente(posx,posy,lado)

def fundo(posx,posy,lado, cor, espessura):
    # Define posicao
    turtle.penup()
    turtle.goto(posx,posy)
    turtle.pendown()
    # Atributos
    turtle.width(espessura)
    turtle.fillcolor(cor)
    turtle.begin_fill()
    for i in range(4):
        turtle.forward(lado)
        turtle.left(90)
    turtle.end_fill()
    turtle.hideturtle()

def frente(posx,posy,lado):
    # Sectores
    sector(posx+lado/2,posy+lado/2,0,0.4*lado,60,'black')
    sector(posx+lado/2,posy+lado/2,120,0.4*lado,60,'black')
    sector(posx+lado/2,posy+lado/2,240,0.4*lado,60,'black')
    # Circunferência
    circunferencia(posx+lado/2,posy+(lado/2) - (lado/10), 0, lado/10, 'black','yellow',2)

def sector(posx,posy,orientacao,raio, angulo, cor_fundo):
    # Atributos
    turtle.penup()
    turtle.goto(posx,posy)
    turtle.pendown()
    turtle.setheading(orientacao)
    turtle.fillcolor(cor_fundo)
    turtle.begin_fill()
    # Desenha
    turtle.forward(raio)
    turtle.left(90)
    turtle.circle(raio, angulo)
    turtle.left(90)
    turtle.forward(raio)
    turtle.left(180-angulo)
    turtle.end_fill()
    turtle.hideturtle()

def circunferencia(posx,posy,orientacao,raio,cor_fundo,cor_bordo, espessura):
    # Posiciona
    turtle.penup()
    turtle.goto(posx,posy)
    turtle.pendown()
    # Define atributos
    turtle.setheading(orientacao)
    turtle.pencolor(cor_bordo)
    turtle.fillcolor(cor_fundo)
    turtle.width(espessura)
    # Desenha
    turtle.begin_fill()
    turtle.circle(raio)
    turtle.end_fill()
    turtle.hideturtle()

    
if __name__ == '__main__':
    radioactividade(-100,-100,400)
    turtle.exitonclick()
E agora, já está. Como vê devagar se vai ao longe.
Esta solução final resulta da nossa preguiça. E também claro, de uma análise apressada do problema. Deixamos ao leitor a tarefa de tornar a orientação do desenho um parâmetro do problema. Se quiser ainda pode alterar o código de modo que as cores do preenchimento possam ser outras. Deste modo pode inventar uma simbologia diferente.

Sem comentários:

Enviar um comentário