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.
01."""Devagar se vai ao longe: radioactividade."""
02.import turtle
03. 
04.def radioactividade():
05.    # Desenha fundo
06.    fundo()
07.    # Desenha frente
08.    frente()
09. 
10.def fundo():
11.    pass
12. 
13.def frente():
14.    pass
15.     
16.if __name__ == '__main__':
17.    radioactividade()
18.    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.
01."""Devagar se vai ao longe: radioactividade."""
02.import turtle
03. 
04.def radioactividade(lado):
05.    # Desenha fundo
06.    fundo(lado)
07.    # Desenha frente
08.    frente()
09. 
10.def fundo(lado):
11.    for i in range(4):
12.        turtle.forward(lado)
13.        turtle.left(90)
14.    turtle.hideturtle()
15. 
16.def frente():
17.    pass
18.     
19.if __name__ == '__main__':
20.    radioactividade(100)
21.    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.)
01."""Devagar se vai ao longe: radioactividade."""
02.import turtle
03. 
04.def radioactividade(lado):
05.    # Desenha fundo
06.    fundo(lado,'yellow',4)
07.    # Desenha frente
08.    frente()
09. 
10.def fundo(lado, cor, espessura):
11.    # Atributos
12.    turtle.width(espessura)
13.    turtle.fillcolor(cor)
14.    turtle.begin_fill()
15.    for i in range(4):
16.        turtle.forward(lado)
17.        turtle.left(90)
18.    turtle.end_fill()
19.    turtle.hideturtle()
20. 
21.def frente():
22.    pass
23.     
24.if __name__ == '__main__':
25.    radioactividade(100)
26.    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...
01."""Devagar se vai ao longe: radioactividade."""
02.import turtle
03. 
04.def radioactividade(lado):
05.    # Desenha fundo
06.    fundo(lado,'yellow',4)
07.    # Desenha frente
08.    frente()
09. 
10.def fundo(lado, cor, espessura):
11.    # Atributos
12.    turtle.width(espessura)
13.    turtle.fillcolor(cor)
14.    turtle.begin_fill()
15.    for i in range(4):
16.        turtle.forward(lado)
17.        turtle.left(90)
18.    turtle.end_fill()
19.    turtle.hideturtle()
20. 
21.def frente():
22.    # Sectores
23.    sector()
24.    sector()
25.    sector()
26.    # Circunferência
27.    circunferencia()
28. 
29.def sector():
30.    pass
31. 
32.def circunferencia():
33.    pass
34. 
35.     
36.if __name__ == '__main__':
37.    radioactividade(100)
38.    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.
01."""Devagar se vai ao longe: radioactividade."""
02.import turtle
03. 
04.def radioactividade(posx, posy,lado):
05.    # Desenha fundo
06.    fundo(posx, posy,lado,'yellow',4)
07.    # Desenha frente
08.    frente(lado)
09. 
10.def fundo(lado, cor, espessura):
11.    # Atributos
12.    turtle.width(espessura)
13.    turtle.fillcolor(cor)
14.    turtle.begin_fill()
15.    for i in range(4):
16.        turtle.forward(lado)
17.        turtle.left(90)
18.    turtle.end_fill()
19.    turtle.hideturtle()
20. 
21.def frente(posx, posy, lado):
22.    # Sectores
23.    sector()
24.    sector()
25.    sector()
26.    # Circunferência
27.    circunferencia(posx+lado/2,t posy+(lado/2) - (lado/10), lado/10, 'black','yellow',2)
28. 
29.def sector():
30.    pass
31. 
32.def circunferencia(posx,posy,raio,cor_fundo,cor_bordo, espessura):
33.    # Posiciona
34.    turtle.penup()
35.    turtle.goto(posx,posy)
36.    turtle.pendown()
37.    # Define atributos
38.    turtle.pencolor(cor_bordo)
39.    turtle.fillcolor(cor_fundo)
40.    turtle.width(espessura)
41.    # Desenha
42.    turtle.begin_fill()
43.    turtle.circle(raio)
44.    turtle.end_fill()
45. 
46.     
47.if __name__ == '__main__':
48.    radioactividade(400)
49.    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.
01."""Devagar se vai ao longe: radioactividade."""
02.import turtle
03. 
04.def radioactividade(posx,posy,lado):
05.    # Desenha fundo
06.    fundo(posx, posy,lado,'yellow',4)
07.    # Desenha frente
08.    frente(posx,posy,lado)
09. 
10.def fundo(posx,posy,lado, cor, espessura):
11.    # Define posicao
12.    turtle.penup()
13.    turtle.goto(posx,posy)
14.    turtle.pendown()
15.    # Atributos
16.    turtle.width(espessura)
17.    turtle.fillcolor(cor)
18.    turtle.begin_fill()
19.    for i in range(4):
20.        turtle.forward(lado)
21.        turtle.left(90)
22.    turtle.end_fill()
23.    turtle.hideturtle()
24. 
25.def frente(posx,posy,lado):
26.    # Sectores
27.    sector(posx+lado/2,posy+lado/2,0,0.4*lado,60,'black')
28.    sector(posx+lado/2,posy+lado/2,120,0.4*lado,60,'black')
29.    sector(posx+lado/2,posy+lado/2,240,0.4*lado,60,'black')
30.    # Circunferência
31.    circunferencia(posx+lado/2,posy+(lado/2) - (lado/10), lado/10, 'black','yellow',2)
32. 
33.def sector(posx,posy,orientacao,raio, angulo, cor_fundo):
34.    # Atributos
35.    turtle.penup()
36.    turtle.goto(posx,posy)
37.    turtle.pendown()
38.    turtle.setheading(orientacao)
39.    turtle.fillcolor(cor_fundo)
40.    turtle.begin_fill()
41.    # Desenha
42.    turtle.forward(raio)
43.    turtle.left(90)
44.    turtle.circle(raio, angulo)
45.    turtle.left(90)
46.    turtle.forward(raio)
47.    turtle.left(180-angulo)
48.    turtle.end_fill()
49.    turtle.hideturtle()
50. 
51.def circunferencia(posx,posy,raio,cor_fundo,cor_bordo, espessura):
52.    # Posiciona
53.    turtle.penup()
54.    turtle.goto(posx,posy)
55.    turtle.pendown()
56.    # Define atributos
57.    turtle.pencolor(cor_bordo)
58.    turtle.fillcolor(cor_fundo)
59.    turtle.width(espessura)
60.    # Desenha
61.    turtle.begin_fill()
62.    turtle.circle(raio)
63.    turtle.end_fill()
64.    turtle.hideturtle()
65. 
66.     
67.if __name__ == '__main__':
68.    radioactividade(-100,-100,400)
69.    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.
01."""Devagar se vai ao longe: radioactividade."""
02.import turtle
03. 
04.def radioactividade(posx,posy,lado):
05.    # Desenha fundo
06.    fundo(posx, posy,lado,'yellow',4)
07.    # Desenha frente
08.    frente(posx,posy,lado)
09. 
10.def fundo(posx,posy,lado, cor, espessura):
11.    # Define posicao
12.    turtle.penup()
13.    turtle.goto(posx,posy)
14.    turtle.pendown()
15.    # Atributos
16.    turtle.width(espessura)
17.    turtle.fillcolor(cor)
18.    turtle.begin_fill()
19.    for i in range(4):
20.        turtle.forward(lado)
21.        turtle.left(90)
22.    turtle.end_fill()
23.    turtle.hideturtle()
24. 
25.def frente(posx,posy,lado):
26.    # Sectores
27.    sector(posx+lado/2,posy+lado/2,0,0.4*lado,60,'black')
28.    sector(posx+lado/2,posy+lado/2,120,0.4*lado,60,'black')
29.    sector(posx+lado/2,posy+lado/2,240,0.4*lado,60,'black')
30.    # Circunferência
31.    circunferencia(posx+lado/2,posy+(lado/2) - (lado/10), 0, lado/10, 'black','yellow',2)
32. 
33.def sector(posx,posy,orientacao,raio, angulo, cor_fundo):
34.    # Atributos
35.    turtle.penup()
36.    turtle.goto(posx,posy)
37.    turtle.pendown()
38.    turtle.setheading(orientacao)
39.    turtle.fillcolor(cor_fundo)
40.    turtle.begin_fill()
41.    # Desenha
42.    turtle.forward(raio)
43.    turtle.left(90)
44.    turtle.circle(raio, angulo)
45.    turtle.left(90)
46.    turtle.forward(raio)
47.    turtle.left(180-angulo)
48.    turtle.end_fill()
49.    turtle.hideturtle()
50. 
51.def circunferencia(posx,posy,orientacao,raio,cor_fundo,cor_bordo, espessura):
52.    # Posiciona
53.    turtle.penup()
54.    turtle.goto(posx,posy)
55.    turtle.pendown()
56.    # Define atributos
57.    turtle.setheading(orientacao)
58.    turtle.pencolor(cor_bordo)
59.    turtle.fillcolor(cor_fundo)
60.    turtle.width(espessura)
61.    # Desenha
62.    turtle.begin_fill()
63.    turtle.circle(raio)
64.    turtle.end_fill()
65.    turtle.hideturtle()
66. 
67.     
68.if __name__ == '__main__':
69.    radioactividade(-100,-100,400)
70.    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