Aula 03 – Expressões Aritméticas, Relacionais e Lógicas

17 março, 2009 (13:47) | aulas | Por: admin

Objetivo

Anteriormente, entendemos o Processing como uma ferramenta de desenho e como uma ferramenta de animação e interação. Cada vez mais funções são apresentadas para que se entenda como funcionam as funcionalidades de uma linguagem de programação voltada para a computação gráfica e design digital.

As variáveis apresentadas no capítulo anterior foram utilizadas como entradas imediatas de dados das funções gráficas. Tais variáveis, porém, podem ser alteradas e modificadas através de expressões , operadores e funções. Desta forma, o tema do capítulo corrente trata de como aplicar elementos da aritmética e lógica em um programa de computador. Cada um destes elementos será enxergado como se fosse um bloco de lego, ou melhor, um filtro no qual um ou mais valores serão transformados em outros.

No início, outro tipo de interação também é apresentada: a interação via teclado.

Destaca-se que o uso de expressões relacionais e lógicas neste capítulo deve ser estudado com cuidado por ter um enfoque mais matemático que o tradicional. Entretanto, em situações reais seu uso é mais simples se combinadas com o uso de estruturas condicionais. Se você compreende bem as operações lógicas e relacionais fique confortável para pular este capítulo e partir direto para o trabalho com a estrutura condicional na seqüencia.

Exemplos do Capítulo

Os exemplos mostrados neste capítulo estão disponíveis para download.

Sintaxe introduzida (link para documentação em Português)

Esta seção serve como resumo e referência aos comandos explorados neste capítulo. Pule para o tópico “Usando as variáveis de teclado no modo contínuo”, se for sua primeira leitura.

  • Variáveis de teclado:
    • key: contém o valor da tecla mais recentemente pressionada ou solta, representa o caractere da tecla;
    • keyCode: permite a detecção de teclas especiais como UP, DOWN, LEFT e RIGHT (cima, baixo, esquerda e direita);
    • keyPressed: valor booleano que indica se uma tecla está pressionada (true) ou solta (false).
  • Expressões Aritméticas:
    • operador +: Adiciona dois valores ou concatena dois strings. Pode ser usado para modificar a posição de um elemento na tela em relação a uma coordenada existente, deixando o elemento mais à direita em relação ao eixo x ou mais para baixo em relação ao eixo y.
    • operador -: Subtrai dois valores. Semelhante ao operador “+”, o – também pode deslocar um elemento. No caso do operador – o elemento é deslocado para cima em relação ao eixo y e para a esquerda em relação ao eixo x. O operador “-” também pode ser usado para inverter uma direção de movimento.
    • operador *: Multiplica dois valores. Sua utilidade está, por exemplo, no reescalonamento de uma posição. A operação de multiplicação por zero também terá vez em relação à habilitação de desenho de um elemento.
    • operador /: Divide um valor por outro. Indica quantas vezes um elemento serve dentro de outro. Operações com “/” podem ser usadas, por exemplo, para criar funções que aumentam de uma unidade após um certo tempo.
    • operador %: Retorna o resto inteiro da divisão. Usado para restringir um conjunto de números a um intervalo desejado.
  • Expressões Relacionais:
    • ==: Operador de igualdade. Devolve verdadeiro quando os dois operandos são iguais;
    • !=: Operador de diferença. Devolve verdadeiro quando os dois operandos são diferentes;
    • >: Operador de maior. Devolve verdadeiro quando o operando da esquerda é maior que o da direita;
    • <: Operador de menor. Devolve verdadeiro quando o operando da esquerda é menor que o da direita;
    • >=: Operador maior ou igual. Devolve verdadeiro quando o operando da esquerda é maior ou igual que o da direita.
    • <=: Operador menor ou igual. Devolve verdadeiro quando o operando da esquerda é menor ou igual que o da direita.
  • Expressões Lógicas:
    • && – Operador E.
    • || – Operador OU.
    • ! – Operador NÃO.

Sintaxe introduzida (link para documentação em Inglês)

Esta seção serve como resumo e referência aos comandos explorados neste capítulo. Pule para o tópico “Usando as variáveis de teclado no modo contínuo”, se for sua primeira leitura.

  • Variáveis de teclado:
    • key: contém o valor da tecla mais recentemente pressionada ou solta, representa o caractere da tecla;
    • keyCode: permite a detecção de teclas especiais como UP, DOWN, LEFT e RIGHT (cima, baixo, esquerda e direita);
    • keyPressed: valor booleano que indica se uma tecla está pressionada (true) ou solta (false).
  • Expressões Aritméticas:
    • operador +: Adiciona dois valores ou concatena dois strings. Pode ser usado para modificar a posição de um elemento na tela em relação a uma coordenada existente, deixando o elemento mais à direita em relação ao eixo x ou mais para baixo em relação ao eixo y.
    • operador -: Subtrai dois valores. Semelhante ao operador “+”, o – também pode deslocar um elemento. No caso do operador – o elemento é deslocado para cima em relação ao eixo y e para a esquerda em relação ao eixo x. O operador “-” também pode ser usado para inverter uma direção de movimento.
    • operador *: Multiplica dois valores. Sua utilidade está, por exemplo, no reescalonamento de uma posição. A operação de multiplicação por zero também terá vez em relação à habilitação de desenho de um elemento.
    • operador /: Divide um valor por outro. Indica quantas vezes um elemento serve dentro de outro. Operações com “/” podem ser usadas, por exemplo, para criar funções que aumentam de uma unidade após um certo tempo.
    • operador %: Retorna o resto inteiro da divisão. Usado para restringir um conjunto de números a um intervalo desejado.
  • Expressões Relacionais:
    • ==: Operador de igualdade. Devolve verdadeiro quando os dois operandos são iguais;
    • !=: Operador de diferença. Devolve verdadeiro quando os dois operandos são diferentes;
    • >: Operador de maior. Devolve verdadeiro quando o operando da esquerda é maior que o da direita;
    • <: Operador de menor. Devolve verdadeiro quando o operando da esquerda é menor que o da direita;
    • >=: Operador maior ou igual. Devolve verdadeiro quando o operando da esquerda é maior ou igual que o da direita.
    • <=: Operador menor ou igual. Devolve verdadeiro quando o operando da esquerda é menor ou igual que o da direita.
  • Expressões Lógicas:
    • && – Operador E.
    • || – Operador OU.
    • ! – Operador NÃO.

Usando as variáveis de teclado no modo contínuo

O teclado é, sem dúvida, um dos dispositivos de interação de maior  popularidade no uso de dispositivos eletrônicos. Desde teclados computadorizados, aparelhos celulares, eletrodomésticos como microondas, máquinas de escrever, e, até mesmo, brinquedos, a interação via teclado é conhecida por todos. O teclado do computador é derivado da interface da máquina de escrever. Mesmo que o computador tenha evoluído nas últimas décadas e deixado de ser meramente uma máquina de criar textos, o teclado persiste e foi adaptado para o uso em games, navegação pela Internet e comunicação pessoal.

O Processing expõe três variáveis que indicam o estado atual do teclado:

  • key: contém o valor da tecla mais recentemente pressionada ou solta, representa o caractere da tecla;
  • keyCode: permite a detecção de teclas especiais como UP, DOWN, LEFT e RIGHT (cima, baixo, esquerda e direita);
  • keyPressed: valor booleano que indica se uma tecla está pressionada (true) ou solta (false).

A variável key retorna o caractere pressionado. No exemplo abaixo, cada vez que o usuário altera a tecla uma letra diferente é exibida na tela. Observe o uso da função textAlign para alinhar o texto no centro da tela.

tecladokey

void setup()
{
   // Carrega a fonte Arial com metade do tamanho da tela
   textFont(createFont("Arial", width/2));
   // Alinha o texto
   textAlign(CENTER, BASELINE);
}
void draw()
{
    // Limpa a tela
    background(100);
    // Exibe a tecla pressionada
    text(key, width/2, height/2);

}

Executar!

Embora o valor de key seja um caractere também é possível convertê-lo para uma representação numérica. Essa representação numérica é derivada de uma tabela de caracteres chamada tabela ASCII. Esta tabela representa cada caractere com um valor diferente de 0 até 255. Por exemplo, entre 48 e 57 temos os valores dos caracteres de 0 a 9. Entre 65 e 90 tem-se o intervalo das letras maiúsculas. O exemplo abaixo, associa a posição de desenho de uma linha à tecla pressionada.

tecladokey2

void setup()
{
    size(300, 100);
    // Carrega a fonte Arial com metade do tamanho da tela
    textFont(createFont("Arial", height/2));
    // Alinha o texto
    textAlign(CENTER, BASELINE);
}
void draw()
{
    // Limpa a tela
    background(100);
    // Exibe a tecla pressionada como uma linha
    // O valor de x corresponde à tecla
    line(key, 0, key, height);
    // Exibe o valor ASCII da tecla pressionada
    text(int(key), width/2, height);
}

Executar!

Enquanto a variável key retorna um caractere ASCII, a variável keyCode permite que se utilize a representação para teclas especiais. Esta representação inclui também as teclas como: UP, DOWN, LEFT, RIGHT, ALT, SHIFT e CONTROL, por exemplo. Observe que keyCode representa a tecla e não a letra. Por exemplo, ao pressionar a tecla ‘A’, independente se o valor é minúsculo ou maiúsculo, o valor obtido é 65. O exemplo abaixo demonstra a diferença entre key e keyCode. Uma lista dos valores está disponível no site do Java.

tecladokey3

void setup()
{
  size(300, 100);
  // Carrega a fonte Arial com metade do tamanho da tela
  textFont(createFont("Arial", height/2));
  // Alinha o texto
  textAlign(CENTER, BASELINE);
}
void draw()
{
  // Limpa a tela
  background(100);
  // Exibe a tecla pressionada como uma linha
  // O valor de x1 corresponde à key
  // O valor de x2 corresponde à keyCode
  line(key, 0, keyCode, height);
  // Exibe o valor da tecla pressionada (key)
  text(int(key), width/3, height/2);
  text(key, 2*width/3, height/2);
  // Exibe o valor da tecla pressionada (keyCode)
  text(keyCode, width/2, height);
}

Executar!

A última variável que se preocupa com o teclado é a variável keyPressed. Tal variável é um valor lógico que poderá ser utilizado para a tomada de decisões. O código a seguir exemplifica a mudança da cor da tela caso qualquer tecla seja pressionada. A tecla pressionada determina a cor escolhida. O uso da função int se dá para converter o valor lógico (true ou false) para 1 ou zero.

tecladokey4

void setup()
{
  // Carrega a fonte Arial com metade do tamanho da tela
  textFont(createFont("Arial", height/2));
  // Alinha o texto
  textAlign(CENTER, BASELINE);
}
void draw()
{
  // Limpa a tela com a cor da tecla
  // caso a tecla seja pressionada
  background(key * int(keyPressed));
  text(key, width/2, height/2);
}

Executar!

Para usar o teclado efetivamente, será necessário diferenciar cada uma das teclas pressionadas e associar ações diferentes a cada tecla conforme sua relação com um valor.  Será necessário, portanto, necessário o uso dos operadores, principalmente, os relacionais. Nas próximas seções, conheceremos os principais operadores que podem ser utilizados no Processing e como eles podem ser utilizados para manipulação das variáveis de mouse e de teclado, ou mesmo para aplicar operações na variável frameCount.

Expressões Aritméticas

A matemática pode ser utilizada para produzir forma e movimento em um programa. Os operadores aritméticos tradicionais, embora sejam bastante intuitivos podem ser aplicados na criação de comportamentos complexos, principalmente quando misturados entre si para gerar funções. As expressões aritméticas são construídas através dos operadores explicados a seguir:

  • operador +: Adiciona dois valores ou concatena dois strings. Pode ser usado para modificar a posição de um elemento na tela em relação a uma coordenada existente, deixando o elemento mais à direita em relação ao eixo x ou mais para baixo em relação ao eixo y.
  • operador -: Subtrai dois valores. Semelhante ao operador “+”, o – também pode deslocar um elemento. No caso do operador – o elemento é deslocado para cima em relação ao eixo y e para a esquerda em relação ao eixo x. O operador “-” também pode ser usado para inverter uma direção de movimento.
  • operador *: Multiplica dois valores. Sua utilidade está, por exemplo, no reescalonamento de uma posição. A operação de multiplicação por zero também terá vez em relação à habilitação de desenho de um elemento.
  • operador /: Divide um valor por outro. Indica quantas vezes um elemento serve dentro de outro. Operações com “/” podem ser usadas, por exemplo, para criar funções que aumentam de uma unidade após um certo tempo.
  • operador %: Retorna o resto inteiro da divisão. Usado para restringir um conjunto de números a um intervalo desejado.

Observa-se exemplos de aplicação de cada um destes operadores em seguida, principalmente para a implementação de deslocamentos e movimentos.

No exemplo a seguir, o mouse determina a posição de um retângulo na tela. O círculo branco é desenhado à direita do retângulo através da aplicação do operador + e o círculo cinza é desenhado à esquerda pelo uso do operador -.

deslocamento1

void setup()
{
  rectMode(CENTER);
}

void draw()
{
  background(100);
  // Retângulo ao centro
  fill(0);
  rect(mouseX, mouseY, 20, 20);
  // Círculo à direita
  fill(255);
  ellipse(mouseX+20, mouseY, 20, 20);
  // Círculo à esquerda
  fill(128);
  ellipse(mouseX-20, mouseY, 20, 20);
}

Executar!

O caso abaixo demonsta o texto sendo exibido à esquerda, acima, à direita e abaixo conforme a aplicação dos deslocamentos.

deslocamento2

void setup()
{
  // Carrega a fonte Arial com tamanho 10
  textFont(createFont("Arial", 10));
  // Alinha o texto
  textAlign(CENTER);
}

void draw()
{
  background(100);
  noFill();
  text("N", mouseX, mouseY-20);
  text("O", mouseX-20, mouseY);
  text("S", mouseX, mouseY + 20);
  text("E", mouseX+20,mouseY);
}

Executar!

A combinação dos operadores + e – pode ser feita com a variável frameCount, width e height. No exemplo abaixo, cada letra é deslocada em relação a anterior. O valor do deslocamento determina também a posição inicial do objeto.

deslocamento3

void setup()
{
  // Carrega a fonte Arial com tamanho 20
  textFont(createFont("Arial", 20));
  // Alinha o texto
  textAlign(CENTER);
}
void draw()
{
  background(100);
  text("3", frameCount, height - 20);
  text("2", frameCount+10, height - 40);
  text("1", frameCount+20, height - 60);
}

Executar!

O operador de multiplicação também pode ser usado para aumentar ou diminuir a velocidade de um movimento. No exemplo abaixo, cada letra tem uma velocidade diferente a partir do multiplicador.

deslocamento4

void setup()
{
  // Carrega a fonte Arial com tamanho 20
  textFont(createFont("Arial", 20));
  // Alinha o texto
  textAlign(CENTER);
}
void draw()
{
  background(100);
  text("3", frameCount*0.5, height - 20);
  text("2", frameCount, height - 40);
  text("1", frameCount*2, height - 60);
}

Executar!

A partir dos exemplos anteriores pode-se observar que a posição do objeto em relação ao tempo reflete a relação física entre velocidade e tempo (sendo frameCount uma boa aproximação do tempo passado desde o início do programa). Assim, considerando a independência dos movimentos no eixo x e no eixo y, as fórmulas para cálculo da posição de um objeto após a passagem de um certo tempo é:

x = posição inicial em x + (velocidade em x * frameCount )
y = posição inicial em y + (velocidade em y * frameCount)

Esta é a fórmula do Movimento Retilínio Uniforme. No exemplo, abaixo, cada instrução implementa uma fórmula de posicionamento diferente em x e em y. A tabela sumariza as posições, direções e velocidades de cada objeto. Observe que a velocidade é dada em pixels por frame.

deslocamento5

void setup()
{
  // Carrega a fonte Arial com tamanho 20
  textFont(createFont("Arial", 20));
  // Alinha o texto
  textAlign(CENTER);
  frameRate(5);
}
void draw()
{
  background(100);
  text("3", 10+frameCount*0.5, 20+frameCount*3);
  text("2", 45-frameCount*2, 50+frameCount*3);
  text("1", 90-frameCount*7, 60-frameCount*5);
}

Executar!

Instrução Posição Inicial em X
Velocidade em X (pixels / frame)
Direção
em X
Posição Inicial em Y
Velocidade em Y (pixels / frame)) Direção em Y
text(”3″, 10+frameCount*0.5, 20+frameCount*3); 10
0.5
Para a direita (+)
20
3
De cima para baixo (+)
text(”2″, 45-frameCount*2, 50+frameCount*3); 45
2
Para a esquerda (-)
50
3
De cima para baixo (+)
text(”1″, 90-frameCount*7, 60-frameCount*5); 90
7
Para a esquerda (-)
60
5
De baixo para cima (-)

 

Exercício de Fixação 01 – Completar a tabela

Conhecendo a fórmula do MRU e dadas as seguintes posições, complete a tabela a seguir.

Fórmula:

x = posição inicial em x + (velocidade em x * frameCount )
y = posição inicial em y + (velocidade em y * frameCount)


Instrução Posição Inicial em X
Velocidade em X (pixels / frame)
Direção
em X
Posição Inicial em Y
Velocidade em Y (pixels / frame)) Direção em Y
text(”X”, frameCount*0.7, 13-frameCount*6);





text(”Y”, -45+frameCount*10, 50+frameCount*3);





text(”Z”, 12-frameCount*3, 30+frameCount*2);





 

 

Outro tipo de movimento de possível implementação apenas usando operadores aritméticos é o movimento retilíneo uniformemente variado. Neste caso, a velocidade varia de maneira constante, ou seja, uma aceleração é aplicada. Um exemplo típico de aceleração que pode ser aplicada em um corpo é a aceleração da gravidade.

A fórmula para determinação da posição a partir do frameCount é:

posição atual = posição inicial + velocidade inicial * frameCount+ (1/2*aceleração*frameCount * frameCount )

Foge do escopo deste material a demonstração desta fórmula.

deslocamento6

void setup()
{
  size(100, 300);
  frameRate(5);
}
void draw()
{
  ellipse(width/2,
  10+1*frameCount+(0.5*5*frameCount*frameCount),
  10, 10);
}

Executar!

É claro, o movimento em x e em y pode ser enxergado de maneira independente. Por exemplo, a seguir é demonstrado o movimento de um projétil: velocidade constante em x e variável em relação à velocidade em y.

deslocamento7

void setup()
{
  size(400, 300);
  frameRate(30);
}
void draw()
{
  background(0);
  ellipse(2 * frameCount,
  height-5*frameCount+(0.5*0.06*frameCount*frameCount),
  10, 10);
}

Executar!

Movimentos periódicos podem ser implementados através do operador %. Tal operador retorna o resto inteiro da divisão. Conforme já explicado no capítulo anterior, a aplicação do operador % em uma variável que cresce constantemente cria uma expressão que volta a se repetir. O programa abaixo gera o gráfico de aplicação do operador % em relação ao frameCount. O intervalo de repetição da expressão se dá de zero até 99. Ao frameCount chegar em 100, já que 100 dividido por 100  dá resto 0, o intervalo recomeça.

deslocamento8

void setup()
{
  size(400, 200);
  textFont(createFont("Arial", 20));
  frameRate(10);
}
void draw()
{
  // Exibe o ponto
  point(frameCount,
  frameCount % 100);
  // Exibe os dados
  fill(255);
  rect(0, height/2, 400, 100);
  fill(0);
  text("frameCount = " + frameCount, 0, height - 60);
  text("frameCount % 100 = " + (frameCount % 100), 0, height - 40);
}

Executar!

Destaca-se, também, que qualquer um dos movimentos anteriores também pode ser implementado de maneira periódica usando o operador % como parâmetro do movimento. No exemplo abaixo, a expressão frameCount % 30 permite que se repita a animação de 30 em 30 frames.

deslocamento9

void setup()
{
  // Carrega a fonte Arial com tamanho 20
  textFont(createFont("Arial", 20));
  // Alinha o texto
  textAlign(CENTER);
  frameRate(5);
}
void draw()
{
  background(100);
  text("3", 10+(frameCount % 30)*0.5, 20+(frameCount % 30)*3);
  text("2", 45-(frameCount % 30)*2, 50+(frameCount % 30)*3);
  text("1", 90-(frameCount % 30)*7, 60-(frameCount % 30)*5);
}

Executar!

A aplicação da expressão (frameCount % width) ou (frameCount % height) retorna, respectivamente, um intervalo de repetição entre zero e a largura da tela e entre zero e a altura da tela. Esta expressão é útil para criar a ilusão que um objeto ao chegar em um limite da tela retorna à posição original. No caso da velocidade do objeto ser de valor constante igual a 1 frame por segundo, a expressão deve ser (frameCount % width) para uma repetição no eixo x e (frameCount % height) para uma repetição no eixo y.  Se a velocidade for diferente, o valor de velocidade deve dividir a largura ou altura, conforme exemplo abaixo:

deslocamento10

void setup()
{
  size(100, 300);
}
void draw()
{
  background(0);
  ellipse(0,
  (frameCount % height), 10, 10);
  // Velocidade: 2 px/frame
  ellipse(width/4,
          2*(frameCount % (height/2)), 10, 10);
  // Velocidade: 3 px/frame
  ellipse(width/2,
          3*(frameCount % (height/3)), 10, 10);
  // Velocidade: 4 px/frame
  ellipse(3*width/4,
          4*(frameCount % (height/4)), 10, 10);
}

Executar!

Desta forma, o operador “/”, ou seja, a divisão, é útil também na criação de funções que aumentam (ou diminuem) conforme a passagem do tempo. No programa seguinte, observa-se a evolução do gráfico de uma função conforme a aplicação de diferentes fatores de divisão. Salianta-se que ao invés da divisão, o mesmo efeito poderia ser conseguido através da multiplicação. Por exemplo, o gráfico de (frameCount/2) é o mesmo de (frameCount * 0.5). A divisão, porém, deixa mais claro ao leitor do código o objetivo do programador. No caso de (frameCount/2), por exemplo, o objetivo é obter uma função com a metade da velocidade imposta por frameCount.

deslocamento11

void setup()
{
  size(400, 200);
  textFont(createFont("Arial", 15));
  frameRate(10);
}
void draw()
{
  // Exibe os pontos
  stroke(0);
  point(frameCount,
  frameCount);
  stroke(255, 0, 0);
  point(frameCount,
  frameCount / 2);
  stroke(0, 255, 0);
  point(frameCount,
  frameCount / 3);
  // Exibe os dados
  noStroke();
  fill(255);
  rect(0, height/2, 400, 100);
  fill(0);
  text("frameCount  = "    + frameCount, 0, height - 45);
  fill(255,0,0);
  text("frameCount / 2 = " + (frameCount / 2), 0, height - 30);
  fill(0,255,0);
  text("frameCount / 3 = " + (frameCount / 3), 0, height - 15);
}

Executar!

Uma funcionalidade interessante é obtida através da combinação entre as expressões (frameCount % width) e (frameCount / width): percorrer os pixels da tela. A chamada à point(frameCount % width, frameCount / width), marca cada um dos pontos da tela da esquerda para a direita e de cima para baixo.

deslocamento12

void setup()
{
  size(256, 256);
  textFont(createFont("Arial", 20));
}
void draw()
{
  // Exibe os pontos
  stroke(frameCount % 256);
  point(frameCount % width,
        frameCount / width);

  // Exibe os dados
  fill(255);
  rect(0, width-60, width, 60);
  fill(0);
  text("frameCount % width = " + (frameCount % width), 0, height - 40);
  text("frameCount / width = " + (frameCount / width), 0, height - 20);
}

Executar!

Esta abordagem de desenho, embora permita compreender o instante de desenho de cada pixel é muito lenta para situações reais pois exige o redesenho de cada frame. Em um capítulo posterior, verificaremos como repetir o comando de desenho diversas vezes sem precisar redesenhar a tela utilizando a instrução for.

Variações nesta abordagem podem ser feitas, por exemplo, para gerar o desenho de um grid.

deslocamento13

void setup()
{
  size(300, 300);
  textFont(createFont("Arial", 15));
}
void draw()
{
  // Exibe os pontos
  stroke(128);
  fill(frameCount % 256);
  rect((frameCount % (width/10)) * 10,
       (frameCount / (width/10)) * 10, 10, 10);

  // Exibe os dados
  fill(255);
  rect(0, width-100, width, 100);
  fill(0);
  text("(frameCount % (width /10)) * 10 = " +
        (frameCount % (width /10)) * 10 , 0, height - 80);
  text("(frameCount / (width /10)) * 10 = " +
        (frameCount / (width /10)) * 10, 0, height - 60);
  text("frameCount % width = " + (frameCount % width), 0, height - 40);
  text("frameCount / width = " + (frameCount / width), 0, height - 20);
}

Executar!

 

A variável frameCount começa a se mostrar insuficiente para resolver todos os problemas: observe que a primeira posição do grid não é desenhada.  Se diminuíssemos de 1 o valor de frameCount, esta situação estaria resolvida. Aumentaria, porém a complexidade de escrever e compreender esta expressão. No próximo capítulo, veremos como resolver o mesmo problema com uma expressão menos complexa através da definição de variáveis.

Exercício de Fixação 02 – Programa

Faça um programa que simule o comportamento da formação de uma palavra de 4 letras que devem se encontrar no centro da tela e formar uma palavra. Após o encontro, a animação recomeça.

Exercício de Fixação 03 – Grid

Faça um programa que construa um grid quadrado de 5 x 5 casas. Cada casa deve ser construída em um frame.

Definiu-se nesta seção, os operadores aritméticos. Nosso próximo passo será a utilização de operadores que além de modificarem expressões também serão úteis na tomada de decisões.

Expressões relacionais

Uma expressão relacional é obtida quando dois valores são comparados. Esta comparação tem como resultado um valor lógico. Tal valor pode ser o valor verdadeiro (true) ou o falso (false). O Processing apresenta seis operadores relacionais:

  • ==: Operador de igualdade. Devolve verdadeiro quando os dois operandos são iguais;
  • !=: Operador de diferença. Devolve verdadeiro quando os dois operandos são diferentes;
  • >: Operador de maior. Devolve verdadeiro quando o operando da esquerda é maior que o da direita;
  • <: Operador de menor. Devolve verdadeiro quando o operando da esquerda é menor que o da direita;
  • >=: Operador maior ou igual. Devolve verdadeiro quando o operando da esquerda é maior ou igual que o da direita.
  • <=: Operador menor ou igual. Devolve verdadeiro quando o operando da esquerda é menor ou igual que o da direita.

A avaliação destes operadores é bastante intuitiva. Observe as expressões abaixo:

  • 3==3 é igual a verdadeiro;
  • 3==4 é igual a falso;
  • 30!=40 é igual a verdadeiro;
  • 40!=40 é igual a falso;
  • 10>5 é igual a verdadeiro;
  • 10<5 é igual a falso;
  • 10>10 é igual a falso;
  • 10>=10 é igual a true.

Para exibir o valor destas expressões, pode-se usar a função println:

relacionais1

println(3==3);
println(3==4);
println(30!=40);
println(40!=40);
println(10>5);
println(10<5);
println(10>10);
println(10>=10);

Observe que o operador == não deve ser substituído por apenas um sinal de igual. Apenas um sinal de igual é usado para a operação de atribuição, que veremos no capítulo sobre criação de variáveis.

Os valores true e false serão úteis na tomada de decisões, mas também podem ser usados como se fossem números.

Assim, para a conversão de um valor true para 1 e do valor false para zero é necessária a aplicação da função int:

relacionais2

println(int(3==3));
println(int(3==4));
println(int(30!=40));
println(int(40!=40));
println(int(10>5));
println(int(10<5));
println(int(10>10));
println(int(10>=10));

Importante! Este tipo de conversão será utilizado nos próximos exemplos para que possamos compreender como funcionam os operadores relacionais. Em situações reais, perceberemos posteriormente, que tal conversão deve ser evitada para que nosso código seja o mais claro possível.

No exemplo abaixo, a cor de fundo da tela é controlada conforme a posição em x do mouse. Se o mouse estiver antes da metade da tela, a cor de fundo é o branco (cor 255), pois 255 multiplicado por 1 é igual a 255. Caso contrário, a cor obtida é o preto (cor zero), pois 255 multiplicado por zero é igual a zero.

relacionais3

void draw()
{
  background(255 * int(mouseX < width/2));
  stroke(128);
  line(width/2, 0, width/2, height);
}

Executar!

Os operadores relacionais também podem ser usados para cortar um valor após um certo limite. Abaixo, quando o valor de mouseX passa da metade da tela, o valor de mouseX multiplicado por int(mouseX<width/2) passa a ser zero.

relacionais4

void setup()
{
  rectMode(CENTER);
}
void draw()
{
  background(0);
  fill(128);
  rect(0, 0, width, height);
  fill(255);
  rect(mouseX   * int(mouseX<width/2),
           mouseY   * int(mouseY<height/2),
           width/4  * int(mouseX<width/2),
           height/4 * int(mouseX<height/2));
}

Executar!

No exemplo abaixo, cada tecla de zero a cinco é associada à troca da cor de fundo de um retângulo diferente, como se estivessemos usando um teclado. Observe que quando a tecla é pressionada o valor é mantido até que outra tecla seja pressionada.

relacionais5

void draw()
{
  background(128);
  fill( 255 * int(key=='1') );
  rect(0, 0, width/5, height);
  fill( 255 * int(key=='2') );
  rect(width/5, 0, width/5, height);
  fill( 255 * int(key=='3') );
  rect(2*width/5, 0, width/5, height);
  fill( 255 * int(key=='4') );
  rect(3*width/5, 0, width/5, height);
  fill( 255 * int(key=='5') );
  rect(4*width/5, 0, width/5, height);
}

Executar!

Diferente do exemplo anterior, a cor de fundo no próximo exemplo só é trocada enquanto a tecla estiver pressionada. Observa-se, portanto, que o operador * quando aplicado ao resultado de uma operação relacional exige que todos os lados da operação de multiplicação tenham valor verdadeiro para que o resultado da multiplicação não dê zero.

relacionais6

void draw()
{
  background(128);
  fill( 255 * int(key=='1') * int(keyPressed));
  rect(0, 0, width/5, height);
  fill( 255 * int(key=='2') * int(keyPressed));
  rect(width/5, 0, width/5, height);
  fill( 255 * int(key=='3') * int(keyPressed));
  rect(2*width/5, 0, width/5, height);
  fill( 255 * int(key=='4') * int(keyPressed));
  rect(3*width/5, 0, width/5, height);
  fill( 255 * int(key=='5') * int(keyPressed));
  rect(4*width/5, 0, width/5, height);
}

Executar!

Usando estratégia semelhante à do exercício anterior, é possível criar uma ferramenta de desenho que mistura a interação com o mouse e teclado. No exemplo abaixo, a cor do pincel é escolhida a partir da tecla pressionada. O operador ‘+’ separando as operações de multiplicação, faz com que somente com uma condição verdadeira a cor seja escolhida.

relacionais7

void setup()
{
  background(0);
  strokeWeight(5);
}
void draw()
{
  stroke( 255 * int(key=='1') * int(mousePressed) +
          200 * int(key=='2') * int(mousePressed) +
          150 * int(key=='3') * int(mousePressed) +
          100 * int(key=='4') * int(mousePressed));
  point(mouseX, mouseY);
}

Executar!

Apesar de válida, a utilização dos operadores * e + em conjunto com os operadores relacionais deve ser evitada quando possível. Para combinar operadores relacionais, portanto, utilizaremos os operadores lógicos.

Expressões lógicas

Operadores lógicos são úteis para combinar duas ou mais expressões relacionais e para inverter valores lógicos. O operador E, representado pelo sinal &&, retorna o valor verdadeiro caso todos os operandos tenham valor verdadeiro. O operador OU, reprentado pelo sinal ||, retorna o valor verdadeiro caso qualquer um dos operandos tenha valor verdadeiro. O operador NÃO, representado pelo sinal ! é um operador unário, ou seja, só pode ser aplicado em um operando. Neste caso, a aplicação do operador inverte o verdadeiro como falso e vice-versa.

Na prática, todas as combinações possíveis para estes operadores são:

  • true && true é igual a true;
  • true && false é igual a false;
  • false && true é igual a false;
  • false && false é igual a false;
  • true || true é igual a true;
  • true || false é igual a true;
  • false || true é igual a true;
  • false || false é igual a false;
  • !true é igual a false;
  • !false é igual a true.

Em uma comparação com objetos do mundo real, o operador E funciona como se fosse uma combinação de duas válvulas em um mesmo cano. Apenas se as duas válvulas estiverem ligadas a água sai pelo cano e enche o pote. No programa abaixo, cada uma das válvulas é controlada por um operador lógico. A válvula da esquerda é controlada pelo teclado e a da direita pelo mouse. Só no caso de ambas as válvulas estiverem ligadas o cano deixa a água sair.

logicos1

void setup()
{
  size(200, 200);
  textFont(createFont("Arial", 45));
  textAlign(CENTER);
  smooth();
}

void draw()
{
  // Desenha o cano e as válvulas
  fill(0);
  rect(0, height/2, 2*width/3, 30);
  // Desenha a válvula associada ao teclado
  fill(255 * int(keyPressed));
  rect(width/4, height/2-10, 10, 40);
  // Desenha a válvula associada ao mouse
  fill(255 * int(mousePressed));
  rect(width/2, height/2-10, 10, 40);
  // Desenha o pote
  fill(255 * int(mousePressed && keyPressed));
  rect(3*width/5, height-60, 60, 60);
  text("E", width/2, height/3);
}

Executar!

No caso do operador OU, temos dois canos diferentes. Assim, tanto no caso de uma válvula ou outra estarem abertas o pote será preenchido com água. Novamente, a válvula da esquerda é controlada pelo teclado e a da direita pelo mouse.

logicos2

void setup()
{
  size(200, 200);
  textFont(createFont("Arial", 45));
  textAlign(CENTER);
  smooth();
}

void draw()
{
  // Desenha o cano e as válvulas
  fill(0);
  rect(0, height/2, width/3, 30);
  rect(width-width/3, height/2, width/3, 30);
  // Desenha a válvula associada ao teclado
  fill(255 * int(keyPressed));
  rect(width/4, height/2-10, 10, 40);
  // Desenha a válvula associada ao mouse
  fill(255 * int(mousePressed));
  rect(width-width/4, height/2-10, 10, 40);
  // Desenha o pote
  fill(255 * int(mousePressed || keyPressed));
  rect(width/5, height-60, 3*width/5, 60);
  text("OU", width/2, height/3);
}

Executar!

No exemplo abaixo, o retângulo faz o papel de um botão. Assim, o operador E é usado para determinar o intervalo no qual o mouse pode estar para que o botão seja habilitado. Desta forma, quando a posição em x do mouse estiver no intervalo entre 30 e 70 o retângulo é pintado na cor branca.

logicos3

void setup()
{
  textFont(createFont("Arial", 10));
  textAlign(CENTER);
}

void draw()
{
  background(128);
  // Preenche quando mouseX estiver entre 30 e 70
  fill( 255 * int((mouseX >= 30) && (mouseX <= 70)));
  rect(30, 0, 40, 100);
  // Mostra o valor de mouseX
  text(mouseX, 10, 10);
}

Executar!

No caso da habilitação do botão demandar do clique do mouse, somente se faz necessária a colocação de mais uma condição: mousePressed deve ser igual a true.

logicos4

void setup()
{
  textFont(createFont("Arial", 10));
  textAlign(CENTER);
}

void draw()
{
  background(128);
  // Preenche quando mouseX estiver entre 30 e 70
  // e o mouse for pressionado
  fill( 255 * int((mouseX >= 30) &&
                  (mouseX <= 70) &&
                   mousePressed));
  rect(30, 0, 40, 100);
  // Mostra o valor de mouseX
  text(mouseX, 10, 10);
}

Executar!

Nos exemplos anteriores considerou-se apenas a consideração de mouseX. Para a delimitação de um botão em uma área da tela, o valor de mouseY também deve ser considerado.

logicos5

void setup()
{
  textFont(createFont("Arial", 10));
}

void draw()
{
  background(128);
  // Preenche quando mouseX estiver entre 30 e 70
  // e quando mouseY estiver entre 10 e 40
  fill( 255 * int((mouseX >= 30) &&
                  (mouseX <= 70) &&
                  (mouseY >= 10) &&
                  (mouseY <= 40)));
  rect(30, 10, 40, 30);
  // Mostra o valor de mouseX e mouseY
  text("("+mouseX+","+mouseY+")", 10, 80);
}

Executar!


Exercício de Fixação 04 – Botões

Seu objetivo agora é gerar uma interface com 4 botões retangulares. Ao clicar em qualquer um destes botões, o botão deve indicar que foi clicado.


Os operadores E e OU também podem ser utilizados para na composição de funções com o objetivo de implementar movimentos mais complexos, formados por diferentes funções. Vamos codificar, inicialmente, o movimento de um objeto indo da esquerda para a direita e voltando ao ponto inicial.

Observe, por exemplo, o movimento periódico do objeto abaixo. A expressão, (frameCount % width) conforme vimos anteriormente, é responsável por este movimento periódico.

movimentologicos1

void setup()
{
  size(400, 200);
  textFont(createFont("Arial", 10));
  fill(0);
}

void draw()
{
  background(255);
  rect(frameCount % width, 2*height/3, 20, 20);
  text("frameCount % width = " + frameCount % width, 20, 20);
}

Executar!

Se fosse necessário o movimento contrário, ou seja, da esquerda para a direita, a expressão (width-frameCount % width) seria aplicável.

movimentologicos2

void setup()
{
  size(400, 200);
  textFont(createFont("Arial", 10));
  fill(0);
}

void draw()
{
  background(255);
  rect(width - frameCount % width, 2*height/3, 20, 20);
  text("width - frameCount % width = " + (width - frameCount % width), 20, 20);
}

Executar!

Plotando as duas funções de movimento em um mesmo gráfico, conforme o programa abaixo, observa-se que para a implementação de, por exemplo, um movimento periódico do objeto indo e voltando na tela poderíamos misturar as duas funções. Constata-se que a função em vermelho poderia ser usada para o início do movimento e a função em verde para a segunda parte do movimento, ou seja, para o retorno do objeto.

movimentologicos3

void setup()
{
  size(400, 200);
  textFont(createFont("Arial", 15));
  frameRate(20);
}
void draw()
{
  // Exibe os pontos
  stroke(0);
  point(frameCount, frameCount);
  stroke(255, 0, 0);
  point(frameCount, frameCount % 100);
  stroke(0, 255, 0);
  point(frameCount, 100 - frameCount % 100);
  // Exibe os dados
  noStroke();
  fill(255);
  rect(0, height/2, 400, 100);
  fill(0);
  text("frameCount  = "    + frameCount, 0, height - 45);
  fill(255,0,0);
  text("frameCount % 100 = " + frameCount % 100,
  0, height - 30);
  fill(0,255,0);
  text("100 - frameCount % 100 = " + (100 - frameCount % 100),
  0, height - 15);
}

Executar!

A combinação entre as duas expressões pode ser feita através da multiplicação de cada uma delas pelo intervalo que ela deve ser válida.

Ou seja: a expressão (frameCount % 100) no exemplo acima deve ser utilizada entre zero e 100 e a expressão (100-frameCount %100) é válida entre 100 e 200.

O intervalo entre 0 e 100 é representado por: (frameCount < 100) e (frameCount>=100 && frameCount<200) representa o intervalo seguinte.

A função final de deslocamento será:

(frameCount % 100) * int(frameCount<100) +
(100-frameCount%100) * int(frameCount>=100 && frameCount<200)

No resultado abaixo a função de deslocamento é representada em azul.

movimentologicos4

void setup()
{
  size(400, 200);
  textFont(createFont("Arial", 15));
  frameRate(20);
  strokeWeight(5);
}
void draw()
{
  // Exibe os pontos
  stroke(0);
  point(frameCount, frameCount);
  stroke(255, 0, 0);
  point(frameCount, frameCount % 100);
  stroke(0, 255, 0);
  point(frameCount, 100 - frameCount % 100);
  stroke(0, 0, 255);
  point(frameCount, (frameCount % 100) * int(frameCount<100) +
       (100-frameCount % 100) * int(frameCount>=100 && frameCount<200)
    );

  // Exibe os dados
  noStroke();
  fill(255);
  rect(0, height/2, 400, 100);
  fill(0);
  text("frameCount  = "    + frameCount, 0, height - 45);
  fill(255,0,0);
  text("frameCount % 100 = " + frameCount % 100,
  0, height - 30);
  fill(0,255,0);
  text("100 - frameCount % 100 = " + (100 - frameCount % 100),
        0, height - 15);
  fill(0,0,255);
  text("100 - frameCount % 100 = " + (100 - frameCount % 100),
        0, height - 15);
}

Executar!

Aplicando a fórmula no movimento do objeto:

movimentologicos5

void setup()
{
  size(400, 200);
  textFont(createFont("Arial", 10));
  fill(0);
}

void draw()
{
  background(255);
  // Função de movimento
  rect( (frameCount % width) * int(frameCount<width) +
    (width-frameCount % width) * int(frameCount>=width &&  frameCount<2*width)
    , 2*height/3, 20, 20);
  text("frameCount % width = " + (frameCount % width), 20, 20);
  text("width-frameCount % width = " + (width-frameCount % width), 20, 40);
}

Executar!

Em resumo, a composição de funções de movimento pode ser feita através da soma das expressões que geram cada intervalo desejado:

FUNÇÃO 1 * int(CONDIÇÃO QUE GERA O INTERVALO 1) +
FUNÇÃO 2 * int(CONDIÇÃO QUE GERA O INTERVALO 2) +
...

 

Exercício de Fixação 5 – Funções

Crie um programa que simule o comportamento a seguir:

Entre o frame 0 e 10: Posição inicial em x igual a 10 e velocidade 3.

Do frame 10 em diante: Posição 40.

Posição em y sempre de height/2

Ver exemplo.

No exemplo a seguir, o objetivo será deixar o próprio intervalo periódico. Ou seja, o objeto vai ficar oscilando entre duas posições.

Importante: sem a criação de variáveis auxiliares o código a seguir será de difícil compreensão! Faça a análise do código com calma, mas não se preocupe, voltaremos futuramente a ele!

O intervalo desejado pode ser implementado através das condições:

frameCount % (width*2) < width

e

frameCount % (width*2) >= width

A primeira condição é habilitada de 0 até width. A segunda fica com valor verdadeiro de width em diante. (frameCount % (width *2)) restringe o intervalo de frameCount de zero ao dobro de width.

Observe o desenho da função obtida em azul para uma largura de 100.

movimentologicos6

void setup()
{
  size(400, 200);
  textFont(createFont("Arial", 15));
  frameRate(20);
  strokeWeight(5);
}
void draw()
{
  // Exibe os pontos
  stroke(0);
  point(frameCount, frameCount);
  stroke(255, 0, 0);
  point(frameCount, frameCount % 100);
  stroke(0, 255, 0);
  point(frameCount, 100 - frameCount % 100);
  stroke(0, 0, 255);
  point(frameCount, (frameCount % 100)  * int(frameCount % 200 < 100 ) +
    (100-frameCount % 100) * int(frameCount % 200 >= 100)
    );

  // Exibe os dados
  noStroke();
  fill(255);
  rect(0, height/2, 400, 100);
  fill(0);
  text("frameCount  = "    + frameCount, 0, height - 45);
  fill(255,0,0);
  text("frameCount % 100 = " + frameCount % 100,
  0, height - 30);
  fill(0,255,0);
  text("100 - frameCount % 100 = " + (100 - frameCount % 100),
  0, height - 15);
  fill(0,0,255);
  text("combinação = " + (100 - frameCount % 100),
  0, height );
}

Executar!

Segue o resultado final, com o objeto movimentando-se do início ao fim e voltando:

movimentologicos7

void setup()
{
  size(400, 200);
  textFont(createFont("Arial", 10));
  fill(0);
}

void draw()
{
  background(255);
  // Função de movimento
  rect( (frameCount % width) * int(frameCount % (width*2) < width ) +
        (width-frameCount % width) * int(frameCount % (width*2) >= width)
        , 2*height/3, 20, 20);

  text("frameCount % width = " + (frameCount % width), 20, 20);
  text("width-frameCount % width = " + (width-frameCount % width), 20, 40);
}

Executar!

 

movimentologicos8

void setup()
{
  size(350, 230);
  textFont(createFont("Arial", 10));
  fill(0);
}

void draw()
{
  background(255);
  // Função de movimento
  rect( (frameCount % width) * int(frameCount % (width*2) < width ) +
        (width-frameCount % width) * int(frameCount % (width*2) >= width),   
        (frameCount % height) * int(frameCount % (height*2) < height ) +
        (height-frameCount % height) * int(frameCount % (height*2) >= height) , 20, 20);

  text("frameCount % width = " + (frameCount % width), 20, 20);
  text("width-frameCount % width = " + (width-frameCount % width), 20, 40);
  text("frameCount % height = " + (frameCount % height), 20, 60);
  text("width-frameCount % height = " + (width-frameCount % height), 20, 80);
}

Executar!

Em mais um exemplo, também pode-se aplicar operações lógicas com as variáveis de teclado. No exemplo abaixo, as setas LEFT e RIGHT podem controlar a posição de um objeto.

teclado1

void setup()
{
  size(300,100);
  rectMode(CENTER);
}

void draw()
{
  background(255);
  rect( width/3     * int(keyCode==LEFT) +
        2*width/3   * int(keyCode==RIGHT) +
        width/2     * int(keyCode!=LEFT &&
                          keyCode!=RIGHT)
        , height/2, 20, 20);
}

Executar!

Por fim, no exemplo a seguir a variável keyPressed é utilizada para que apenas quando o usuário esteja com a tecla pressionada o objeto seja deslocado.

teclado2

void setup()
{
  size(300,100);
  rectMode(CENTER);
}

void draw()
{
  background(255);
  rect( width/3 * int(keyPressed &&
        keyCode==LEFT) +
        2*width/3   * int(keyPressed &&
                          keyCode==RIGHT  ) +
        width/2     * int(!keyPressed || (keyCode!=LEFT &&
        keyCode!=RIGHT))
    , height/2, 20, 20);
}

Executar!

Para saber mais

Reas, Casey. Fry, Ben. Processing: A Programming Handbook for Visual Designers and Artists.

Páginas 223 a 227 – teclado

43 a 50 – aritmética e funções

51 a 59 – decisão.

Escreva um comentário