Parte 1 e Parte 2 exploram muitos dos recursos da linguagem Processing, de gráficos 2D básicos e texto a processamento de imagem. Esta parte final une as pontas soltas e analisa como o Processing lida com gráficos 3D, iluminação e redes. A discussão sobre rede fornece uma maneira de dar o seu aplicativo Processing alguns dados úteis para trabalhar com visualização.
Vamos começar com uma discussão das transformações e dos diversos recursos do Processing que as fornecem.
Se já desenvolveu aplicativos gráficos, você provavelmente está familiarizado com álgebra linear e operações de matriz necessárias para implementar coisas como conversão, escala e rotação. O Processing fornece funções para simplificar operações como essas com uma única chamada de função, ocultando a matemática de matriz que ocorre por baixo dos panos.
Coordenar conversão do sistema
A conversão simplesmente muda o sistema de coordenadas, de forma que o canto superior esquerdo exista em um novo deslocamento. É uma operação de adição na qual a conversão define o deslocamento para novas operações na janela do monitor. Por exemplo, o código da Listagem 1 desenha um quadrado com a função rect , depois especifica uma mudança no sistema de coordenadas usando translate. Qualquer nova forma desenhada após a função
translate aparece com o deslocamento; por isso, nesse caso, a função rect na verdade cria um quadrado em 100, 100, 150, 150, como mostrado na Figura 1, célula A.
Listagem 1. Usando
translate() para alterar o sistema de coordenadassize(200, 200); rect(0, 0, 50, 50); translate(100, 100); rect(0, 0, 50, 50); |
A função scale , como o nome indica, aumenta as operações de desenho. Por exemplo, se você solicitar uma escala de 2.0, seu objeto será 200 por cento maior do que o original (em cada dimensão).
A Listagem 2 ilustra esse conceito, com a saída resultante mostrada na Figura 1, célula B.
Listagem 2. Usando
scale() para ampliar as operações de desenho de formasize(200, 200); rect(0, 0, 50, 50); translate(75, 75); scale(2.0); rect(0, 0, 50, 50); |
A função rotate gira uma forma em torno do canto superior esquerdo (coordenada 0,0). Por essa razão, girar uma forma na verdade não gira no lugar. A Listagem 3 mostra a operação de rotação aplicada a um quadrado. Primeiro, desenha-se o quadrado, depois, especifica-se a operação de rotação que é aplicada às formas posteriores desenhadas na janela de exibição. O resultado é mostrado na Figura 1, célula C.
Listagem 3. Usando
rotate() para girar um objetosize(200, 200); rect(100, 100, 50, 50); rotate(PI/16); rect(100, 100, 50, 50); |
Se quisesse girar o objeto no lugar, seria preciso atualizar o sistema de coordenadas para dar conta disso. Para isso, use translate para ajustar o sistema de coordenadas e depois redesenhe seu quadrado rotacionado (com coordenadas x e y atualizadas), como mostrado na Listagem 4. O resultado é mostrado na Figura 1, célula D.
Listagem 4. Usando
translate()
e rotate()size(200, 200); rect(100, 100, 50, 50); translate(100, 100); rotate(PI/16); rect(0, 0, 50, 50); |
O código na Listagem 4 na verdade não girou o objeto no local em torno de seu centro, mas em torno do seu canto superior esquerdo. É possível girar seu quadrado, definindo como desenhar seu objeto. O modo padrão para desenhar retângulos é CORNERS, o que significa que o canto superior esquerdo é definido pelos parâmetros x e y de rect. Se o modo for alterado para CENTER (onde os parâmetros x e y
de rect definem o centro), pode-se desenhar um quadrado e girá-lo em torno de seu centro. Isso é mostrado na Listagem 5, com o resultado mostrado na Figura 1, célula E.
Listagem 5. Usando
translate()
e rotate() em centrossize(200, 200); rectMode(CENTER); rect(100, 100, 50, 50); translate(100, 100); rotate(PI/16); rect(0, 0, 50, 50); |
Agora, aplique esses conceitos para desenvolver uma imagem que lembre as do espirógrafo. Nesse exemplo, gire um quadrado no seu centro, mas não o preencha. O quadrado é girado 16 vezes em torno do centro, com o resultado mostrado na Figura 1, célula F (com o aplicativo simples mostrado na Listagem 6).
Listagem 6. Mais diversão com conversão e rotação
size(200, 200);
rectMode(CENTER);
noFill();
translate(100, 100);
for (int i = 1 ; i < 16 ; i++) {
rotate( (PI/16)*i );
rect(0, 0, 100, 100);
}
|
O Processing oculta a complexidade das operações de matriz e, em vez disso, apresenta um simples conjunto de funções com operações gráficas úteis, como escala e rotação.
Figura 1. Imagens resultantes do código nas Listagens 1-6
Após abranger as transformações básicas, vamos explorar mais um tópico que é útil ao desenhar diversas transformações e objetos. Lembre-se de que, depois que uma função de transformação é chamada, os desenhos de formas subsequentes usam essa transformação. Transformações adicionais podem ser realizadas, e elas são aplicadas ao conjunto atual de matrizes definido pelas operações anteriores. Por exemplo, chamar translate
no contexto de outro translate significa que o segundo translate se baseia no sistema de coordenadas do primeiro translate, não no original. Pode-se salvar as matrizes de coordenadas internas por meio de uma chamada para pushMatrix e restaurá-las usando popMatrix. Visto que essas funções usam pilha semântica, pode-se criar camadas de transformações para formas e depois removê-las para restaurar as matrizes originais.
Como exemplo, a Listagem 7 mostra pushMatrix e popMatrix. Depois de certa inicialização, empurra-se as matrizes de coordenadas atuais para a pilha. Então, pode-se criar um novo sistema de coordenadas centralizado na sua exibição utilizando translate e armazenar essa matriz. Em seguida, gire e armazene essa matriz; depois, desloque o sistema de coordenadas atual com translate (aplicado a partir das matrizes giradas e convertidas anteriormente) e desenhe uma elipse vermelha. Entre na pilha e desenhe um retângulo verde para remover a conversão e escala anteriores. Entre novamente e desenhe um retângulo azul (que não inclui mais a transformação de rotação). Depois, volte à matriz original e desenhe um quadrado cinza. Esse esquema permite a estratificação das transformações uma sobre a outra, proporcionando a capacidade de restaurar matrizes utilizadas anteriormente para outros objetos na tela. Note que a endentação mostrada na Listagem 7 está ali apenas para facilitar a leitura e não é necessária.
Listagem 7. Salvando e restaurando matrizes
size(200, 200);
rectMode(CENTER);
noFill();
smooth();
strokeWeight(2);
colorMode(RGB, 100);
pushMatrix();
translate(100, 100);
pushMatrix();
rotate(PI/4);
pushMatrix();
translate(20, 20);
scale(2.0);
stroke(255, 0, 0); // Red
ellipse(0, 0, 50, 10);
popMatrix();
stroke(0, 255, 0); // Green
rect(0, 0, 50, 25);
popMatrix();
stroke(0, 0, 255); // Blue
rect(0, 0, 75, 50);
popMatrix();
stroke(128, 128, 128); // Gray
rect(0, 0, 50, 50);
|
O resultado do código da Listagem 7 é mostrado na Figura 2.
Figura 2. Demonstrando
pushMatrix e popMatrix
Agora, vamos ampliar a visualização para a terceira dimensão (em um plano 2D), explorando algumas das APIs que o Processing fornece para 3D. Não deixe de acessar Recursos para se aprofundar nos detalhes mais tenebrosos.
O exemplo mostrado na Listagem 8 fornece um programa simples em 3D que usa a função box para criar um objeto na janela de exibição. Como o nome sugere, a função box
cria uma caixa na tela (como mostrado, com dimensões iguais).
É possível criar retângulos com box
apenas adicionando alguns parâmetros (altura, largura e profundidade).
Os únicos outros elementos novos aqui são as funções rotate
. Essas funções permitem girar em torno de determinado eixo. A primeira caixa gira em torno do eixo y, e a segunda caixa, em torno do eixo x. O parâmetro para a rotação é determinada a partir do mouse para o eixo específico. O valor do mouse é lido e mapeado (via função map ) no intervalo -PI a PI.
Listagem 8. Exemplo 3D simples com rotação baseada no mouse
void setup() {
size(200, 200, P3D);
noFill();
smooth();
}
void draw() {
background(0);
translate(width/2, height/2, -(width/2));
rotateY(map(mouseX, 0, width, -PI, PI));
stroke(100);
box(150);
rotateX(map(mouseY, 0, height, -PI, PI));
stroke(150);
box(75);
}
|
A saída da Listagem 8 (pelo menos uma parte estática dela) é mostrada na Figura 3.
Figura 3. Saída estática da Listagem 8
Também é possível adicionar iluminação, e o Processing fornece um conjunto de funções que simplificam isso. Demonstro um aqui com outra função de desenvolvimento de objeto 3D e, em seguida, analiso alguns dos outros. Como a função box , é possível usar sphere para criar uma esfera 3D na janela de exibição. O argumento para sphere representa o raio da esfera.
A Listagem 9 demonstra a função pointLight .
Essa função cria uma fonte de luz definida pelos três últimos argumentos de pointLight (sua posição no espaço). Os três primeiros argumentos definem a cor da luz.
Listagem 9. Esfera e luz
size(100, 100, P3D); background(0); noStroke(); pointLight(50, 100, 180, 80, 20, 40); translate(20, 50, 0); sphere(40); |
A Figura 4 fornece a saída do aplicativo simples de Processing na Listagem 9.
Figura 4. Demonstração de pointLight
A função pointLight fornece uma das mais simples funções de luz (com exceção, talvez, de ambientLight), mas o Processing fornece mais controle sobre a iluminação.
A função spotLight acrescenta um holofote com controles consideráveis. Por exemplo, além da localização, direção e cor da luz, é possível controlar o ângulo do cone do spotlight e o viés do centro do cone. A função directionalLight permite que a luz seja focalizada em determinada direção e proporciona mais iluminação natural que muda de acordo com a direção e o ângulo da luz.
O Processing oferece meios muito mais complexos de desenvolver objetos usando vértices. É possível desenvolver formas dessa maneira, com a capacidade de texturizá-las. Confira Recursos para obter detalhes sobre esse aspecto do Processing.
Os exemplos anteriores em 3D utilizaram modelos estáticos, mas se quiser um pouco de física de projéteis, poderá estender esse exemplo em tempo real. A Listagem 10 fornece uma simulação simples da caixa sendo lançada de um canhão. As equações da cinemática estão fora do escopo deste artigo, mas confira Recursos para obter mais informações.
A função setup cria a janela de exibição e inicializa a posição inicial da caixa (a origem). Ao desenhar, use background para limpar o monitor e, em seguida, calcular cossenos de direção para a orientação do canhão.
Em seguida, em vista da sua variável de tempo, calcule a posição da caixa de dentro da janela de exibição ao longo das três dimensões. Só para adicionar alguns efeitos interessantes, gire a caixa ao redor dos eixos x- e yem proporções diferentes.
Use a função pointLight para criar duas fontes de luz. À esquerda, coloque uma luz azul; à direita, uma luz vermelha. Então, emita o local atual da caixa no espaço 3D para o console de depuração.
Por fim, coloque a caixa no monitor. Comece empurrando o conjunto de matrizes atual para a pilha e chame translate para mudar o deslocamento da sua caixa. Fazer isso permite colocar x ligeiramente à direita da janela, inverter o eixo y (porque, no sistema de coordenadas do Processing, y cresce para baixo) e, finalmente, projetar z para fora (também invertendo-o). Lembre-se de que, no sistema de coordenadas do Processing, x cresce para a direita, y para baixo e z
para longe. Por fim, aplique suas rotações de x e y , coloque sua caixa no monitor e entre no conjunto atual de matrizes.
Listagem 10. Simulação de projéteis com Processing
float x, y, z; // Posição atual
float velocity = 120.0; // Velocidade no bocal;
float alpha = 30.0; // Ângulo do eixo y
float gamma = 60.0; // Ângulo do eixo x
float g = 9.8; // Aceleração devido à gravidade (m/s^2)
float time = 0.0;
float dt = 0.1;
float rotX = 0.0, rotY = 0.0;
void setup() {
size(300, 400, P3D);
smooth();
x = 0.0; y = 0.0; z = 0.0;
}
void draw() {
float b, Lx, Ly, Lz;
time += dt;
background(0);
// Calcula cossenos da orientação do canhão (estático)
b = cos((90.0-alpha) * 3.14/180.0);
Lx = b * cos(gamma * 3.14/180.0);
Ly = cos(alpha * 3.14/180.0);
Lz = b * sin(gamma * 3.14/180.0);
// Calcular a posição da caixa em determinado momento
x = velocity * Lx * time;
y = (cos(alpha*3.14/180.0)) + (velocity * Ly * time) -
(0.5 * g * time * time);
z = velocity * Lz * time;
// Girar a caixa ao redor dos eixos x e y.
rotX += PI/256.0;
rotY += PI/128.0;
// Criar duas fontes de luz (uma azul e outra vermelha)
pointLight(0, 100, 255, 0, 0, 0);
pointLight(255, 0, 0, 400, 400, 0);
println("x " + x + " y " + y + " z " + z );
// Colocar a caixa no monitor
pushMatrix();
translate(100, 400-y, -z);
rotateX(rotX); rotateY(rotY);
box(90);
popMatrix();
}
|
Com esta pequena quantidade de código, implementamos uma simulação simples de um projétil giratório. As etapas de tempo dessa simulação são mostradas na Figura 5, embora a visualização em tempo real seja muito mais interessante.
Figura 5. Trechos da saída da Listagem 10
O desenvolvimento de aplicativos de rede no Processing é similar às abordagens de Ruby e Python, em comparação com a API padrão em C (a API Berkeley Socket). As funções de rede são implementadas em uma biblioteca e apresentam duas classes para desenvolvimento de aplicativos. A primeira é a classe
Server , que pode ser usada para criar servidores; a segunda é a classe Client , que pode ser usada para criar clientes. Há também um conjunto de métodos de eventos que pode ser usado como auxílio para o desenvolvimento de aplicativos. Por exemplo, o evento de servidor é um método de retorno de chamada que é invocado quando um novo cliente se conecta ao servidor. O evento de cliente é um método de retorno de chamada que é invocado quando o servidor enviou bytes para o cliente.
A Listagem 11 mostra um cliente que se comunica com um servidor da Web do mesmo nível com a finalidade de identificar seu software de servidor. Isso é feito simplesmente com o método HTTP HEAD , que retorna metainformações sobre a solicitação do cliente. Por exemplo, os clientes costumam enviar uma solicitação HTTP GET para retornar um arquivo a partir de um servidor Web. A solicitação HEAD não retorna o corpo da mensagem HTTP, mas sim as metainformações do arquivo de solicitação (tamanho, etc.). Essa é a maneira mais simples de reunir as informações nas quais se tem interesse.
A Listagem 11 começa criando uma nova classe de cliente. Defina o servidor e a porta à qual deseja se conectar (nesse caso, a porta do servidor da Web da IBM). Quando se obtém uma conexão com o servidor da Web, é possível enviar a solicitação HTTP usando o método write . No método draw , verifique se há bytes disponíveis para leitura; se houver, execute tokenize neles para dentro de um array. Depois, procure o array para o campo na resposta HTTP para o tipo de servidor (que se parece com Server: Apache). Quando encontrar o campo do servidor, emita o próximo token, que será a própria cadeia de caracteres do servidor (nesse exemplo, IBM_HTTP_Server).
Listagem 11. Um exemplo simples de soquete de cliente
import processing.net.*;
Client myClient;
String inString;
void setup() {
myClient = new Client( this, "www.ibm.com", 80);
myClient.write("HEAD / HTTP/1.0\n\n");
}
void draw() {
if (myClient.available() > 0) {
inString = myClient.readString();
String[] tokens = splitTokens(inString, ":\n ");
for (int i = 0 ; i < tokens.length-1 ; i++) {
if (tokens[i].equals("Server")) println(tokens[i+1]);
}
}
}
|
A classe Server fornece um conjunto de métodos similares, além de disconnect, que desconecta determinado cliente, e stop, que desconecta todos os clientes e para o servidor. Consulte a referência do Processing (veja Recursos) para obter mais detalhes sobre essa API.
Desenvolvendo um mashup de rede
Vejamos outro exemplo de soquete de cliente que recebe dados da Internet e os apresenta. Para o exemplo, vamos incorporar ideias de outros tópicos que aprendemos neste artigo final para visualizar os dados a partir de um serviço da Web. Usaremos seu cliente de soquete para nos conectar à API Yahoo! Traffic REST e extrair as informações de tráfego para determinado local (veja a Listagem 12). Depois de ter se conectado ao servidor remoto (parte da instanciação da classe Client
), escreva sua solicitação. Essa é uma solicitação REST e especifica o serviço com o qual você deseja se comunicar (Yahoo!'s MapsService) juntamente com as informações de localização nas quais se interessa (Sunnyvale, Calif.).
Com sua solicitação concluída, é possível criar seu monitor e carregar sua fonte.
A função draw tem duas seções de código. A primeira é o recebimento da resposta HTTP (que aparece primeiro). Nessa seção, receberemos caracteres à medida que estiverem disponíveis, executaremos tokenize no resultado atual para procurar o token Title
(na verdade, <Title>, mas parte do processo de tokenize é retirar os sinais de maior e menor). Quando o token Title é localizado, a próxima cadeia de caractere será o título do incidente de tráfego.
A próxima seção emite uma cadeia de caracteres de incidente de tráfego para o monitor, se tiver sido recebida (é possível saber isso porque ela tem um comprimento diferente de zero). Como se deu no exemplo 3D anterior, aplique escala e rotação à saída para torná-la um pouco mais interessante. O resultado é uma cadeia de caracteres que gira lentamente, se afastando até o infinito.
Listagem 12. Cliente simples para extrair dados de incidentes de tráfego
import processing.net.*;
Client myClient;
String input="";
String displayString="";
float myScale=1.0, myRatioX = 0.0, myRatioY = 0.0;
void setup() {
myClient = new Client(this, "local.yahooapis.com", 80);
myClient.write("GET /MapsService/V1/trafficData"+
"?appid=YdnDemo&city=Sunnyvale&state=CA HTTP/1.1\n");
myClient.write("Accept: text/html, text/xml\n");
myClient.write("Host: mtjones.com\n\n");
size(900, 300, P3D);
PFont font = loadFont("AbadiMT-Condensed-48.vlw");
textFont(font, 48);
frameRate(20);
smooth();
}
void draw()
{
background(100);
if (myClient.available() > 0) {
input += myClient.readString();
String tokens[] = splitTokens(input, "<>\n");
for (int i = 0 ; i < tokens.length ; i++) {
if (tokens[i].equals("Title")) {
displayString = tokens[i+1];
}
}
}
if ((displayString.length() > 0) && (myScale > 0)) {
myRatioX += (PI/300);
myRatioY += (PI/460);
myScale -= 0.005;
scale(myScale);
pushMatrix();
translate( 50, height/2, 0);
rotateX(myRatioX);
rotateY(myRatioY);
rotateZ(myRatioY/4);
texto(displayString, 0, 0);
popMatrix();
}
}
|
O resultado da Listagem 12 é mostrado na Figura 6. Essa imagem foi obtida em 23 quadros, por isso, está apenas começando seu movimento.
Figura 6. Saída da Listagem 12
O Processing não é apenas uma ótima linguagem e ambiente para visualização, mas um ótimo exemplo do que é possível com a tecnologia de software livre. O Processing é utilizado não só por engenheiros e cientistas para visualização de dados, mas também por artistas e pessoas interessadas em aprender programação e design visual.
Aprender
- Para saber mais sobre o Processing e fazer o download da versão mais recente, ou localizar exemplos e tutoriais interessantes, verifique Processing.org.
Depois de desenvolver seu aplicativo, compartilhe-o no site OpenProcessing.
- O Processing oculta os detalhes mais tenebrosos do trabalho com gráficos 2D e 3D — em especial, a álgebra linear por traz das matrizes de transformação. Pode-se saber mais sobre 3D na referência à API de Processing
, e também nos tutoriais 3D.
- Uma das melhores referências do Processing
é Processing: A Programming Handbook for Visual Designers and
Artists (Casey Reas e Ben Fry, MIT Press, 2004). Esse é não apenas um livro fascinante, mas também belamente ilustrado, e abrange uma grande parte da linguagem, das bibliotecas e dos recursos. Também está disponível
Getting Started
with Processing (Casey Reas e Ben Fry, O'Reilly Media, 2010).
-
Physics for Game Developers (David M. Bourgis, O'Reilly Media, 2001) fornece uma boa olhada nos bastidores da matemática e da física por trás do desenvolvimento de games.
-
Para ouvir entrevistas e discussões interessantes para desenvolvedores de software, confira os Podcasts do developerWorks
.
-
Fique por dentro dos Eventos técnicos e webcasts do developerWorks.
-
Siga o DeveloperWorks no Twitter.
-
Confira conferências, feiras, webcasts e outros eventos que estão para acontecer no mundo todo e que são do interesse de desenvolvedores de software livre da IBM.
-
Visite a Zona de software livre para obter informações detalhadas sobre instruções, ferramentas e atualizações de projetos para ajudar você a se desenvolver com tecnologias de software livre e usá-las com produtos da IBM, bem como nossos artigos e tutoriais mais populares.
-
Assista e aprenda sobre a IBM e tecnologias de software livre e funções de produto com as demos gratuitas on demand do developerWorks.
Obter produtos e tecnologias
-
Inove seu próximo projeto de desenvolvimento de software livre com Versão de teste do software IBM, disponível para download ou em DVD.
- Faça o download das Versões de avaliação de produto IBM
ou explore as versões de teste on-line no IBM SOA Sandbox e entre em contato com as ferramentas de desenvolvimento de aplicativos e produtos de middleware do DB2 ®, Lotus®, Rational®, Tivoli®e WebSphere®.
Discutir
-
Participe dos Blogs do developerWorks
e participe da comunidade do developerWorks.
-
Participe da comunidade do developerWorks. Entre em contato com outros usuários do developerWorks, enquanto explora os blogs, fóruns, grupos e wikis orientados ao desenvolvedor.

M. Tim Jones é arquiteto de firmware integrado e autor de Artificial Intelligence: A Systems Approach, GNU/Linux Application Programming (atualmente em sua segunda edição), AI Application Programming (em sua segunda edição) e BSD Sockets Programming from a Multilanguage Perspective. Seu conhecimento em engenharia varia do desenvolvimento de kernels para naves espaciais geossincrônicas até a arquitetura de sistemas embarcados e o desenvolvimento de protocolos de rede. Tim é arquiteto senior da Emulex Corp. em Longmont, Colorado.