Uma grande coelhada!

Hoje foi o dia de passar de um desenho para muitos. E é para isso que serve o código e os computadores em geral. Para o heavy lifting das tarefas repetitivas.

A aula (programada) tem por objetivo explicar variáveis, ciclos e condições. Confesso que na turma da manhã me “deu uma coisinha má” e vai-se lá saber porquê embirrei com esta ordem lógica…

Na verdade, devia explicar as condições primeiro (porque um ciclo inclui uma condição). Mas, ao pensar nisso entre as sessões, percebi porque razão que tenho a matéria alinhada assim: é estúpido e pouco visual fazer condições antes dos ciclos. Apenas porque nesta fase ainda não estamos a trabalhar em modo dinâmico. Por isso, fica muito difícil demonstrar as condições a trabalhar. Este é o principal defeito dos cursos e manuais de código: tentar explicar demasiada teoria sem operacionalizar. Hoje de manhã, cometi o mesmo erro.

No futuro, apesar de menos lógico, vou manter a sequência original que usei durante a tarde e que tenho vindo a usar… Mesmo que não seja tão “responsável” permite criar e explicar os programas de uma forma, com um feedback visual em que é mais adequado a este perfil de estudantes.

Assim falámos das variáveis em ambas as turmas. Mas, na turma 2 falámos das condições. E acabei por explicar apenas um loop simples muito rapidamente nos últimos 15 minutos. Acho que a estratégia foi boa, mas… enfim. Podia ter sido melhor com mais tempo. Não volto a repetir esta ordem.

Na Turma 1 mantive a ordem lógica e falámos apenas dos ciclos (e não chegámos a falar das condições). Acabei por sentir que não foi necessário explicar as condições (aquela que está na operação de repetição do ciclo). E a (estudante) Inês foi instrumental a clarificar o que se passava com cada iteração do loop (obrigado!). No futuro tenho que ter mais cuidado e certificar-me melhor de que toda a gente compreende a linguagem e os exemplos.

Acabei por não fazer a aula toda. Mas vamos ver o que conseguem fazer esta semana: da turma 2 espero uma fila de robôs; da turma 1, um batalhão de robôs…

Mas pronto. As aulas de hoje cobriram curvas, beziers, variáveis —int, float, boolean e color—, e ainda um pouco de condições e de loops.

Exemplo de demonstração de dois segmentos / curvas e a relação dos pontos de influência

A aula da manhã com a turma 2 começou com a revisão das primitivas gráficas. Pediram-me para explicar as curvas e os béziers. Estivemos a brincar um pouco com isto para ampliar os conceitos de desenho.

Para mim foi bom também. Para praticar as curvas, porque não uso muito. A explicação mais simples que consegui dar foi que: “os beziers funcionam com uma coisa do tipo atração magnética, como a atração da lua sobre as marés; e as curvas funcionam como dois ímanes com a mesma polaridade, que se repelem entre si (com uma força relativa à distância entre si e dos pontos de influência”.

Isto é, os pontos de controlo das handles das curvas bezier “puxam” a curva para perto deles à medida que estamos mais perto do início ou do fim da curva. E os pontos de controlo das curvas são como “polos de carga magnética” que repelem a direção da curva. Sei que isto não é muito fácil descrever [e agora a ler o reference também não está exatamente correto], mas é assim que os “vejo”.

Curves

// configurar app
size(500, 500);

// transform matrix para ser mais visível na projeção online
scale(5);

// usar valores entre -5 e 5
// https://processing.org/reference/curveTightness_.html
curveTightness(0);

// Ponto de "influência" da curva 1. 
// Start off-curve control point.
noStroke();
fill(200, 200, 0);
ellipse(40, 10, 5, 5);

// Ponto inicial da curva 1
fill(200, 200, 0);
ellipse(40, 50, 5, 5);

// Ponto final da curva 1
fill(200, 200, 0);
ellipse(60, 50, 5, 5);

// Ponto de "influência final" da curva 1. 
// End off-curve control point.
fill(200, 200, 0);
ellipse(60, 10, 5, 5);

// ponto inicial da curva 2 (coincidente com o ponto de influência)
noStroke();
fill(200, 80, 0);
ellipse(40, 10, 3, 3);

// Curva 1 (preta) usa os dois pontos iniciais e termina no terceito ponto da primeira curva
stroke(0);
noFill();
curve(40, 10, 40, 50, 60, 50, 60, 10); 

//stroke(0);

// Para a curva 2 usamos 
// o ponto inicial da curva 1 como ponto de inflência/controlo
// o ponto final da curva 1 como ponto inicial da curva
// o ponto final de influência da curva como ponto 2
// e acrescentamos uma direção
noFill();
stroke(0, 200, 0);
curve(40, 10, 40, 10, 40, 50, 60, 50)

Usar apenas as curvas, como “vêm de fábrica” dá-nos pouco controlo sobre a forma/tensão da curva. Para isso vale a pena ver o reference e usar o curveTightness .

// usar valores entre -5 e 5
// https://processing.org/reference/curveTightness_.html
curveTightness(0);

O que é engraçado é que o curveTightness = 1 dá-nos… er… uma reta? Enfim. É o que é e até tem lógica. Mas confesso que não faço ideia da equação que a está a gerar…

Exemplo de uma curva com vários segmentos de curveVertex. Semelhante ao anterior mas mais fácil de escrever

Este exemplo serve apenas para se perceber como a curva é calculada e como podemos ter uma transição “seamless” entre segmentos de curva. No fundo é para se perceber como gerar a tensão da curva.

Tipo de curvas contínua pode e deve ser controlado com o curveVertex.

size(450, 300);

scale(3);

// colocar os "pontos" visíveis entre segmentos de curva
noStroke();
fill(200, 100, 0);
ellipse(10, 26, 3, 3);
ellipse(10, 26, 3, 3);
ellipse(83, 24, 3, 3);
ellipse(83, 61, 3, 3);
ellipse(25, 65, 3, 3); 
ellipse(25, 65, 3, 3);

// controlar a tensão da curva
curveTightness(5);

// desenhar uma curva fluída contínua
noFill();
stroke(0);
beginShape();
curveVertex(10, 26);
curveVertex(10, 26);
curveVertex(83, 24);
curveVertex(83, 61);
curveVertex(25, 65); 
curveVertex(25, 65);
endShape();

O que se seguiu foram as curvas bézier. Na realidade começámos antes, mas utilizei a demonstração para explicar as variáveis (simples) que se seguiu.

Uma curva bézier usa pontos de controlo externos à curva para “puxar” a curvatura na sua direção. Mas, o melhor é deixar explicar quem sabe: vejam o site do Fábio Duarte Martins.

Depois e ver como “funcionam” implementámos um exemplo simples. E, depois de explicar as variáveis, modificamos o exemplo para criar pontos aleatórios e desenhar uma curva com a influência deste.

Demonstração dos béziers
// PAmado aula TP03 2021-02-25
// declarar variáveis
int x1; // <-- atenção que são variáveis do tipo "inteiro"
int y1;

float x2, y2; // <-- outra forma de declarar vários valores

// inicializar programa
size(500, 500);
x1 = int( random(500) ); // <-- o random devolve um numero real e tem que ser convertio em inteiro
println("valor do x1:", x1);

y1 = int( random(500) );
println("valor do y1:", y1);

x2 = 480;
y2 = 50;

//line(85, 20, 10, 10);
//line(90, 90, 15, 80);

stroke(0, 0, 0);
//bezier(85, 20, 10, 10, 90, 90, 15, 80);

noStroke();
fill(255, 0, 0);
ellipse(x1, y1, 10, 10);


ellipse(x2, y2, 10, 10);

fill(0);
ellipse(10, 10, 5, 5);
ellipse(150, 50, 5, 5);

noFill();
stroke(0, 200, 0);
line(x1, y1, 10, 10);
line(x2, y2, 150, 50);

noFill();
strokeWeight(3);
stroke(0);
bezier(x1, y1, 10, 10, 150, 50, x2, y2)

Após esta demonstração, passámos às condições (turma 2), e, na turma 1, essencialmente aos loops. Na turma 1, da parte da tarde deu para fazer isto com alguma calma.

Como sempre, as “bolinhas da praxe”.

Um nested loop com as famigeradas bolinhas. Ah… e com um pouco de random à mistura.
// declarar variáveis
int linhas; // <-- estas vão ser usadas para modificar os loops
int colunas;

// inicializar 
size(800, 800);
linhas = 5;
colunas = 7;

background(255);
fill(0);

// fazer um "nested loop"
// começamos com as linhas (5 linhas >> de avanço vertical)
for (int k = 0; k < linhas; k++) {
  
  // para cada linha (k) desenha 7 colunas/células (de avanço horizontal)
  for (int i = 0; i < colunas; i++) {
    
    // usa o random() para fazer um valor aleatório
    ellipse(100 + i*100, 100 + k*100, random(50, 100), random(50, 100));
  }
  
}

Mesmo não dando para cumprir a aula toda, foi o suficiente para fazer um loop (mais simples de manhã) e um nested loop (para criar grelhas) à tarde. Confesso que nestas alturas me faz falta o quadro branco da sala de aula. Se o confinamento continua, acho que vou mandar vir um quadro branco e marcadores e voltar às aulas no Zoom (porque o Teams é… bom… para ser simpático, vou dizer que o Teams poderia ter sido feito pela Apple… não digo mais nada).

Bom, mas continuando. Usámos um random. Isto implica a conversão dos tipos de variáveis (ou melhor… dos dados incluídos).

E, não tinha planeado, mas também mostrei rapidamente um transform (um scale). Isto é totalmente extemporâneo, mas pode ser que os ajude a avançar com o trabalho.

Demonstradas as variáveis, aplicámos no sketch da Miffy, que tinha vindo da aula anterior. Primeiro, substituímos o “centro” do desenho da cara da Miffy por uma variável horizontal. isto implicou uma mudança de um valor estático (320 creio…) para uma variável. Um-a-um, com a ajuda do CTRL+F 😉

Apesar do esforço na mudança de todo o código de desenho, isto agora permite “mover” o desenho, mudando apenas uma variável. Uma linha de código, em vez das dezenas ou centenas que alguns estudantes fizeram.

Depois, acrescentamos a mesma técnica à variável Y e movemos livremente pelo ecrã da aplicação.

Um loop simples de 3 Miffies

Depois aplicamos um primeiro loop. Bastou copiar o código do desenho da Mify para dentro do primeiro loop “for” que usámos. Ah… e, este ano —acho que estou a ficar “mole”— em vez de os obrigar a adicionar a cada instrução de desenho o offset do loop, simplesmente redefinimos o valor do “centro X” e do “centro Y” do desenho de cada Miffy, conforme a iteração do loop.

    // isto "afasta" a Miffy da margem do ecrã
    int margem_esquerda = 200;
    int margem_topo = 200;
    
    // isto permite que a Miffy "salte" 300px para a direita
    // de cada vez que o loop corre
    int incremento_horizontal;
    int incremento_vertical;
    incremento_horizontal = i * 300; // <-- sempre que o i aumenta, o incremento aumenta * 300
    incremento_vertical = k*300;

    // modifica/atualiza o "novo centro" do desenho da Miffy
    cx = margem_esquerda + incremento_horizontal ;
    cy = margem_topo + incremento_vertical

Et voilá… é só isto. Resumindo a aula, até é uma coisa que se faz em 20 minutos… mas na realidade demora um pouco mais a compreender.

Ficou a faltar a manipulação da cor (se bem que fizemos um pouco de manhã… mas… lá está… sem feedback visual interessante a aula é uma seca!). E as condições. Agora que olho para o sketch, acho que vou abrir a próxima aula para explicar e concluir esta com algumas Miffies de olhos abertos, outras mortas (porque não?) e, claro, uma marota! 😉


Uma grelha simples de 3×3 Miffies

A dada altura, quando quis demonstrar a rapidez e a versatilidade deste nested loop quis desenhar 900 Miffies, mas ups! Acabei por desenhar 90 000!… Foi rápido —quer dizer, demorou uns 2 a 3 segundos a aparecer, mas acho que deu para perceber para que queremos um computador…

// PAmado 2021-02-25
// Miffy from Dick Bruna

// declarar (variáveis e objetos)
int cx;
int cy;

// Configurar a aplicação
size(1510, 540);

// inicializar variáveis e objetos
cx = 200;
cy = height/2;

// Cenário
background(200, 0, 0);

// atenção a este "transform" (Matrix)
// ver https://lsi.fba.up.pt/2019/2020/03/21/mais-uma-moeda-mais-uma-volta-todos-a-distancia-claro/
scale(0.1);

// quero 50 x 40 miffies <-- aqui começo com as colunas (avanço horizontal) e depois as linhas (avanço vertical… tem lógica, mas não acho tão intuitivo…?
for (int i = 0; i < 50; i++) {

  for (int k = 0; k < 40; k++) {
    /// ———— Miffy! ———————————————————————————— //////

    int margem_esquerda = 200;
    int margem_topo = 200;
    
    int incremento_horizontal;
    int incremento_vertical;
    
    incremento_horizontal = i * 300;
    incremento_vertical = k * 300;

    cx = margem_esquerda + incremento_horizontal;
    cy = margem_topo + incremento_vertical;
    
    // Configurar as propriedades da miffy
    // noStroke();
    stroke(0);
    strokeWeight(20); // hack: double the weight 

    fill(255);
    rectMode(CENTER);


    // Orelhas // Orelha direita (stroked)
    rect(cx -70, cy-75, 70, 150);
    ellipse(cx -70, cy-154, 70, 80);

    // Orelha esquerda
    rect(cx + 70, cy-75, 70, 150);
    ellipse(cx + 70, cy-154, 70, 80); // <-- hack para acertar o contorno… subir um pouco

    // restore stroke
    stroke(0);
    strokeWeight(10);

    // Cara
    ellipse(cx, cy, 300, 200);

    // "clean" srossing stroke shapes
    noStroke();
    rect(cx -70, cy-75, 70, 150);
    ellipse(cx -70, cy-154, 70, 80);
    rect(cx +70, cy-75, 70, 150);
    ellipse(cx +70, cy-154, 70, 80);

    // Olhos
    // Olho Direito
    fill(0);
    ellipse(cx -70, cy, 30, 50);

    // Passar a linhas para focinho e olho
    noFill();
    stroke(0);
    strokeWeight(10);

    // Olho Esquerdo

    arc(cx +70, cy+20, 40, 40, 5* PI/4, 7 * PI/4);

    //arc(320, 260, 40, 40, 1.75 * PI, 2.25 * PI);

    // Focinho
    line(cx-20, cy+50, cx+20, cy+30);
    line(cx-20, cy+30, cx+20, cy+50);

    ///  fim da miffy!!!!——————————————————————————
  }
}

Ah!… em nota alta do dia. Se não conseguir mostrar mais nada de novo no semestre, hoje mostrei como fazer “find-and-replace” no computador! Dou a minha missão como cumprida! 😉

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *