Desenhando o rosto de um palhaço

 

Considere a tarefa de desenhar o rosto de um palhaço, conforme esquema ao lado. Para facilitar a tarefa, vamos dividir o problem em três partes:

Considerando esta divisão de tarefas, já podemos implementar a ação #desenhaPalhaco (veja quadro que se segue), mesmo que ainda não saibamos exatamente como realizar cada uma destas tarefas.

desenhaPalhaco

    "Desenha o rosto de um palhaco"

    caneta
         desenhaOlhos;
         desenhaNariz;
         desenhaBoca.

Para desenhar o rosto do palhaço, comecemos pelo desenho dos olhos. Note que os eles são formados por um conjunto de circunferências que são tangentes umas às outras. Parece interessante, portanto, que "ensinemos" a caneta a desenhar circunferências para então dispô-las adequadamente para formar os olhos.

Inicialmente façamos algumas experiências com polígonos regulares. Observe os três comandos que se seguem e tente encontrar alguma regularidade.

2 vezesRepita: [ caneta anda: 50; gira: 180 ]
3 vezesRepita: [ caneta anda: 50; gira: 120 ]
4 vezesRepita: [ caneta anda: 50; gira: 90 ]

Um observador atento notará que nos três casos acima o número de  repetições (n) é inversamente proporcional ao ângulo (a) que a caneta gira a cada iteração (repetição). Em realidade, o produto n . a é constante e igual a 360 que, não por acaso, é o ângulo de uma circunferência. Esta relação nos dá uma forma geral para desenhar polígonos regulares quaisquer. Num pentágono, por exemplo, n = 5. Logo, o ângulo deve ser a = 360 / 5, conforme mostrado a seguir.

5 vezesRepita: [ caneta anda: 50; gira: 360 / 5 ]

Veja, agora, o que acontece quando contruímos um dodecágono (polígono regular de 12 lados) (note que o tamanho do lado foi reduzido de 50 para 20 unidades):

12 vezesRepita: [ caneta anda: 20; gira: 360 / 12 ]

Se continuarmos a aumentar o número de lados, no limite um polígono regular tende a uma circunferência. Na prática basta construirmos um polígono regular de uns 30 lados e com um tamanho de lado pequeno para confundí-lo com uma circunferência. Mas esta forma de desenhar círcunferências não nos permite controlar facilmente seu tamanho pois é preciso combinar os dois parâmetros: número de lados e o tamanho de cada lado.O que parece ser a mais prático para se desenhar circunferências de tamanho qualquer é tomarmos apenas o seu raio como parâmetro.

Da geometria sabemos que o comprimento de uma circunferência é dado pela fórmula , onde R é o seu raio. Considerando que façamos polígonos de 36 lados (múltiplo de 360), cada lado do polígono regular terá tamanho  . Assim a implementação da ação para desenhar uma circunferência  (um polígono regular de 36 lados, neste caso) dado o seu raio, pode ser:

circunferencia: raio

    "Desenha uma circunferência conforme
             o raio passado como parâmetro"

    |lado|
 
    lado := (2 * Float pi * raio) / 36.
    36 vezesRepita: [
         caneta anda: lado;
                gira: 10
    ].

Veja o resultado abaixo, considerando que inicialmente a caneta estava apontando para cima (900). Após desenhar a circunferência de raio 30 a caneta acaba retornando à posição inicial. Portanto, a circunferência de raio 40 é desenhada tangente à anterior exatamente nesta posição (ponto).

caneta circunferencia: 30;
	    circunferencia: 40

Mas para desenhar os olhos precisamos colorir a área interna à circunferência, ou seja, desenharmos um círculo. Vamos aplicar o mesmo processo utilizado quando desenhamos os quadrados pintados que apareceram no problema dos quadrados inscritos, ou seja, construir uma ação na qual a cor seja passada como parâmetro.

circulo: umRaio cor: umaCor
	"Desenha uma círculo colorido
	   conforme o raio e a cor
	   passados comos parâmetros"

	caneta circunferencia: umRaio;
	       semRastros;
	       gira: 90;
	       anda: 5;
	       pintaArea: umaCor;
	       anda: -5;
	       gira: -90;
	       comRastros.

A ação #circulo:cor: desenha um círculo a partir do raio e da cor que são passados como parâmetros. O que a ação faz é , em ordem:

Seguem um exemplo de comando que desenha círculos.

caneta circulo: 30  cor: Azul;
	  circulo: 20  cor: Vermelho.

Desenhar os olhos, agora, é só um questão de combinar corretamente o desenho de círculos e de circunferências até obtermos uma combinação que nos satisfaça. Segue abaixo uma possível combinação para desenho de um olho.

desenhaUmOlho

    "Desenha um olho do palhaço"

    caneta comRastros;
           circulo: 50 cor: Amarelo;
           circunferencia: 45;
           circunferencia: 40;
           circulo: 16 cor: Preto.

desenhaOlhos

    "Desenha os olhos do palhaço"

    caneta semRastros;
           anda: 100;
           fixaCorDosRastros: Preto.

    2 vezesRepita: [
        caneta desenhaUmOlho;
        	     gira: 180.
    ].

    caneta semRastros;
           anda: -100;
           comRastros.

Na ação #desenhaOlhos inicialmente a caneta é movida 100 unidades para frente de forma a posicioná-la no local onde serão desenhados os olhos. Os dois olhos são  então desenhados (2 vezesRepita: [ ... ]) e por fim a caneta anda para trás 100 unidades  (andar um valor negativo é equivalente a andar para trás) para voltar à sua posição original. É interessante notar que os dois olhos foram desenhados com o auxílio da mesma ação #desenhaUmOlho, bastando que se gire a caneta em 1800. Como este giro é repetido duas vezes, ao completar o desenhos dos olhos a caneta volta a apontar para a direção inicial.

Note, agora, que há uma leve inclinação no desenho dos  olhos: o olho esquerdo esta ligeiramente mais baixo que o direito. Será que há algo errado com nosso desenho ou tem a haver com a direção para onde a caneta estava apontando antes de iniciarmos o desenho?

Na forma como a ação #desenhaOlhos foi implementada, os olhos podem ser desenhados em qualquer posição. No caso ao lado, por exemplo, foi solicitado que a caneta girasse 450 (inicialmente ela estava apontando para 900) e, em seguida, que ela desenhasse os olhos. O resultado é que os olhos foram desenhados com uma inclinação de 45 gráus.

Experimente forçar a caneta a apontar para 900 (para cima) através do comando "caneta apontaPara: 90" e, em seguida, execute o comando "caneta desenhaOlhos". Note que a inclinação permanece, o que nos mostra que o problema não tem a haver com a direção inicial para onde a caneta está apontando.

Para compreender o que está acontecendo, observe os dois desenhos de pentágonos que se seguem.

O primeiro desenho foi obtido através do comando: "5 vezesRepita: [ caneta anda: 50; gira: 72 ]", no qual a caneta repetidamente anda 50 unidades e em seguida gira 72 gráus (360 / 5).  Já o segundo desenho foi obtido por:

caneta gira: 36.
5 vezesRepita: [ caneta anda: 50; gira: 72 ].
caneta gira: -36.

No fundo as duas ações desenham um pentágono, sendo modificanda apenas a sua orientação. Mas observe nas figuras abaixo o que acontece quando aplicamos a mesma estratégia de girar 180 gráus utilizada para desenhar simetricamente os olhos do palhaço.

No caso dos pentágonos fica evidente a inclinação pois estamos trabalhando com ângulos de 720. Na circunferência (em realidade polígonos regulares de 36 lados) a inclinação é muito menor pois estão envolvidos ângulos de 100 (veja implementação da ação #circunferência:). Para corrigirmos esta inclinação basta acrescentarmos um giro inicial de 5 gráus antes do desenho da circunferência, e um giro de -5 gráus no final de forma a fazer a caneta retornar para a direção inicial. A implementação da ação #circunferencia: passa a ser.

circunferencia: raio

    "Desenha uma circunferência conforme
             o raio passado como parâmetro"

    |lado|
 
    lado := (2 * Float pi * raio) / 36.
    caneta gira: 5.
    36 vezesRepita: [
         caneta anda: lado;
                gira: 10
    ].
    caneta gira: -5.

Uma vez que já implementamos a ação que desenha círculos, fica bastante fácil desenhar o nariz do palhaço. Veja a seguir.

desenhaNariz

        "Desenha o nariz do palhaço"

    caneta
          gira: 90;
          circulo: 30 cor: VerdeMar;
          gira: -90.

Por último resta desenhar a boca do palhaço. Para facilitar a implementação da ação #desenhaBoca foram criadas outras duas ações: #semiCircunferencia e #desenhaPompom. A ação #semiCircunferencia: é quase igual à ação #circunferencia:, variando apenas no número de repetições do comando #vezesRepita: que reduziu de 36 para 18.

A ação #desenhaBoca está dividida em três instantes:

desenhaBoca

        "Desenha a boca do palhaço"

    caneta semRastros;
           anda: -10;
           gira: -90;
           anda: 120;
           gira: 90.

    caneta comRastros;
           desenhaPompom;
           semRastros;
           semiCircunferencia: 120;
           comRastros;
           desenhaPompom;
           fixaCorDosRastros: Preto;
           semiCircunferencia: 120.

    caneta semRastros;
           gira: 90;
           anda: 120;
           gira: -90;
           anda: 10;
           comRastros.
semiCircunferencia: raio
        "Desenha metade de uma circunferência
          conforme o raio passado como 
		 parâmetro"

        |lado |

        lado := (2 * Float pi * raio) / 36.
        caneta gira: 5.
        18 vezesRepita: [
                caneta anda: lado;
                       gira: 10
        ].
        caneta gira: -5.
desenhaPompom

        "Desenha um pompom"

    caneta fixaCorDosRastros: Rosa.

    72 vezesRepita: [
            caneta anda: 30;
                   anda: -30;
                   gira: 5
    ].