sábado, 30 de setembro de 2017

Zen ou a Arte de Programar

(brincadeiras de um sábado de manhã)


Suponham que lhe pedem um programa capaz de desenhar o símbolo do Yin-Yang:
Por onde começar?? Olhando para a figura verificamos que é composta à base de circunferências e semi-circunferências a duas cores (preto e branco). Usando um princípio já nosso conhecido, tentemos simplificar a questão, esquecendo os círculos interiores e as cores:
def zen1(raio):
    turtle.setheading(90)
    turtle.circle(raio)
    turtle.circle(raio/2,-180)
    turtle.setheading(90)
    turtle.circle(raio/2,180) 
Executando o código é criado o seguinte:
Ao fazermos assim ficamos com o problema de colocar as cores… Com um pouco de reflexão chegamos a uma solução, baseada na ideia de separar a circunferência exterior em duas semi-circunferências. O cuidado a ter é com as orientações a cada momento da tartaruga. Aqui basta não esquecer o principio de que a tartaruga quando se movimenta vê sempre o centro à sua esquerda!
def zen11(raio):
    turtle.setheading(90)
    turtle.circle(raio,180)
    turtle.fillcolor('black')
    turtle.begin_fill()
    turtle.circle(raio/2,-180)
    turtle.setheading(270)
    turtle.circle(raio/2,180)
    turtle.circle(raio,-180)
    turtle.end_fill()
ao executar o programa obtemos:
Agora é “só” colocar os círculos pequenos. Uma vez mais basta saber definir os centros:
def zen12(raio):
    turtle.setheading(90)
    turtle.circle(raio,180)
    turtle.fillcolor('black')
    turtle.begin_fill()
    turtle.circle(raio/2,-180)
    turtle.setheading(270)
    turtle.circle(raio/2,180)
    turtle.circle(raio,-180)
    turtle.end_fill()
    # círculos pequenos
    # esquerdo
    turtle.setx(turtle.xcor() + raio/2 -raio/10)
    turtle.fillcolor('white')
    turtle.begin_fill()
    turtle.circle(raio/10)
    turtle.end_fill()
    # direito
    turtle.penup()
    turtle.setx(turtle.xcor()+ raio)
    turtle.pendown()
    turtle.fillcolor('black')
    turtle.begin_fill()
    turtle.circle(raio/10)  
    turtle.end_fill()
    turtle.hideturtle()    
Et voilá!

Mas será que é a única forma de resolver o problema? Será que ficamos satisfeitos??? Olhando para a solução acima o que podemos dizer? Bem, que há muita coisa “fixa”: a posição, a orientação, as cores. Embora estas duas últimas façam parte da definição clássica da imagem, podemos procurar libertar-mo-nos e permitir uma versão mais geral. Por outro lado, e talvez mais importante, o modo como construímos a solução usa componentes de baixo nível e não olha para a imagem como sendo composta por duas que se distinguem … pela posição, orientação e cores!!! Vamos tentar então uma segunda abordagem.
   
def zen(raio,x_cor,y_cor,orient, cor_1,cor_2):
    """
    Desenha uma componente.
    """
    # corpo principal
    vai_para_or(x_cor,y_cor,orient)
    turtle.fillcolor(cor_1)
    turtle.begin_fill()
    turtle.circle(raio,-180)
    #vai_para_or(x_cor,y_cor,orient)   
    turtle.circle(raio/2,-180)
    turtle.setheading(turtle.heading()+180)
    turtle.circle(raio/2,180)
    turtle.end_fill()
    # restaura
    vai_para_or(x_cor,y_cor,orient+90)
    # círculo pequeno  
    turtle.penup()
    turtle.forward(3*raio/2-raio/10)
    turtle.pendown()
    turtle.fillcolor(cor_2)
    turtle.setheading(orient)
    turtle.begin_fill()
    turtle.circle(raio/10)
    turtle.end_fill()
    turtle.hideturtle()
O código apresentado torna mais claro a importância da orientação em cada momento do desenho. O raio do círculo pequeno foi decidido que tenha um valor 10 vezes mais pequeno que o raio da semi-circunferência maior. O resultado quando escolhemos as cores vermelho e azul:
Agora podemos apresentar o nosso programa para o Yin-Yang:
   
def yin_yang(raio,posx,posy):
    zen(raio,posx,posy,90,'black','white')
    zen(raio,posx - 2*raio,posy,270, ‘white','black')
Com esta solução podemos então … brincar:
   
def zen_rose(raio,posx,posy,orientacao,cor_1, cor_2, num):
    for i  in range(num):
        zen(raio,posx,posy,orientacao,cor_1,cor_2)
        orientacao = orientacao + 360/num
Executando vem:
Mas podemos variar um pouco:
def zen_rose_2(raio,posx,posy,orientacao,cor_1, cor_2, num):
    for i  in range(num):
        if i % 2 == 0:
            zen(raio,posx,posy,orientacao,cor_1,cor_2)
        else:
            zen(raio,posx,posy,orientacao,cor_2,cor_1)
        orientacao = orientacao + 360/num
Ou criar objectos mais coloridos:
def zen_rose_tutti(raio,posx,posy,orientacao, num):
    turtle.colormode(255)
    for i  in range(num):
        cor_1 = (random.randint(0,255),random.randint(0,255),random.randint(0,255))
        cor_2 = (random.randint(0,255),random.randint(0,255),random.randint(0,255))
        zen(raio,posx,posy,orientacao,cor_1,cor_2)
        orientacao = orientacao + 360/num
E agora:
E como alguém disse: o céu é o limite! Invente!!!
def zen_voa(raio,posx,posy,orientacao, num):
    turtle.speed(10)
    turtle.colormode(255)
    for i  in range(1,num+1):
        cor_1 = (random.randint(0,255),random.randint(0,255),random.randint(0,255))
        cor_2 = (random.randint(0,255),random.randint(0,255),random.randint(0,255))        
        zen(raio,posx,posy,orientacao,cor_1,cor_2)
        #orientacao = orientacao + 360/num
        posx = posx + i * raio/5
        posy = posy + i * raio/5
 

Sem comentários:

Enviar um comentário