7/Movimiento

Como un libro animado, la animación en la pantalla in creada dibujando una imagen, luego dibujando una imagen ligeramente diferente, luego otra, y así sucesivamente. La ilusión de movimiento fluido se crea por la persistencia de la visión. Cuando un set de imágenes similares es presentado a un ritmo lo suficientemente rápido, nuestros cerebros traducen esas imágenes en movimiento.
Ejemplo 7-1: Velocidad de los fotogramas Para crear un movimiento suave, Processing trata de ejecutar el código dentro de draw() a 6 fotogramas por segundo. Para confirmar la velocidad de los fotogramas, ejecuta el programa y mira los valores que se imprimen en la consola. Las variables del frameRate mantienen un registro de la velocidad del programa. void draw() { println(frameRate); }

Ejemplo 7-2: Setea el frameRate La función frameRate() cambia la velocidad a la que se ejecuta el programa. Para ver el resultado, descomenta diferente versiones del frameRate() en este ejemplo: void setup() { frameRate(30); // Thirty frames each second //frameRate(12); // Twelve frames each second //frameRate(2); // Two frames each second //frameRate(0.5); // One frame every two seconds } void draw() { println(frameRate); }

------------------------------------------------------------------------------------------------NOTA: Processing trata de ejecutar el código a 60 fotogramas por segundo, pero si toma más que 1/60th de segundo ejecutar el método draw(), entonces los fotogramas disminuirán.La función frameRate() solamente especifica la cantidad máxima de fotogramas, y la cantidad actual de fotogramas para cualquier programa depende del computador en el cual se esta ejecutando el código. --------------------------------------------------------------------------------------------------

Velocidad y Dirección:
Para crear ejemplo de movimiento fluido, nosotros usamos un tipo de dato llamado float (decimal). Este tipo de variable almacena los números con decimal, lo que provee mas resolución para trabajar con movimiento. Por ejemplo, cuando se utilizan enteros, lo más lento que puedes mover cada fotograma es de uno en uno (un pixel y un pixel) (1,2,3,4,.....), pero con decimales, puedes mover tan lento como quieras (1.01, 1.02, 1.03, 1.04,......). Ejemplo 7-3: Mover una figura: El siguiente ejemplo mueve una figura de izquierda a derecha mediante la actualización de la variable x:

int radius = 40; float x = -radius; float speed = 0.5; void setup() { size(240, 120); smooth(); ellipseMode(RADIUS); } void draw() { background(0); x += speed; // Increase the value of x arc(x, 60, radius, radius, 0.52, 5.76); }

Cuando ejecutas este código, notaras que la figura desaparece en la parte derecha de la pantalla cuando el valor de la variable x es mayor que el ancho de la pantalla. El valor de x continua incrementandose, pero la figura ya no es visible.

Ejemplo 7-4: Example 7-4: Volver
Existe muchas alternativas a este comportamiento, que puedes elegir de acuerdo a tus preferencias. Primero, vamos a ampliar el código para mostrar como mover la figura de nuevo al borde izquierdo de la pantalla después de que desapareciera en el borde derecho. En este caso, la imagen en la pantalla como cilindro aplanado, con la figura se moverá hasta el borde derecho de la pantalla y luego volverá al principio (borde izquierdo):

int radius = 40; float x = -radius; float speed = 0.5; void setup() { size(240, 120); smooth(); ellipseMode(RADIUS); } void draw() { background(0); x += speed; // Increase the value of x if (x > width+radius) { // If the shape is off screen, x = -radius; // move to the left edge } arc(x, 60, radius, radius, 0.52, 5.76); }

En cada viaje a través de draw(), el código prueba para ver si el valor de x a aumentado más allá del ancho de la pantalla (más el radio de la figura). S i lo ha hecho, seteamos el valor de x a un valor negativo, de modo que a medida que continúa aumentando, entrara la pantalla por la derecha. ver figura 7-1 para una diagrama de como funciona.

Figura 7-1. Figure 7-1. La pruebas para el borde izquierdo de la pantalla.

Example 7-5: Rebotan en la pared.
En este ejemplo, extenderemos el ejemplo 7-3 para tener una figura que cambia de dirección cuando golpee el borde, en vez de volver a la izquierda. Para hacer que esto pase, añadimos una nueva variable para almacenar la dirección de la figura. Un valor de dirección de 1 mueve la figura hacia la derecha, y un valor de -1 mueve la figura haca la izquierda:

int radius = 40; float x = 110; float speed = 0.5; int direction = 1; void setup() { size(240, 120); smooth(); ellipseMode(RADIUS); } void draw() { background(0); x += speed * direction; if ((x > width-radius) || (x < radius)) { direction = -direction; // Flip direction } if (direction == 1) { arc(x, 60, radius, radius, 0.52, 5.76); // Face right } else { arc(x, 60, radius, radius, 3.67, 8.9); // Face left } } Cuando la figura alcanza un borde, este código da vuelta la dirección de la figura cambiando el signo de la variable de dirección . Por ejemplo, si la variable de dirección es positiva cuando la figura alcanza el borde, el código da vuelta el valor a negativo.

Interpolación:
A veces quieres animar una figura para que valla de un punto de la pantalla hasta otro. Con un poco más de lineas de código, puedes setear la posición de inicio y la posición de termino, luego calcular las posiciones que están entre medio del inicio y termino de cada fotograma.

Ejemplo 7-6: Calcular las posición entremedias
Para que este ejemplo de código sea modular, creamos un grupo de variables en la parte superior. Ejecuta el código un par de veces y cambia los valores para ver como este código puede mover una figura desde cualquier lugar a cualquier otro en un rango de velocidades. Cambia la variable de paso (step) para cambiar la velocidad: int startX = 20; // Initial x-coordinate int stopX = 160; // Final x-coordinate int startY = 30; // Initial y-coordinate int stopY = 80; // Final y-coordinate float x = startX; // Current x-coordinate float y = startY; // Current y-coordinate float step = 0.005; // Size of each step (0.0 to 1.0) float pct = 0.0; // Percentage traveled (0.0 to 1.0) void setup() { size(240, 120); smooth(); } void draw() { background(0); if (pct < 1.0) { x = startX + ((stopX-startX) * pct); y = startY + ((stopY-startX) * pct); pct += step; } ellipse(x, y, 20, 20); }

AZAR
A diferencia del movimiento suave y lineal que es común de gráficas de computador, el movimiento en el mundo físico es usualmente idiosincrásico. Por ejemplo, piensa en un hoja flotando en el suelo, o una hormiga caminando sobre el terreno duro. Podemos simular las cualidades impredecibles del mundo mediante la generación aleatoria de números. La función random() calcula esos valores; podemos establecer un rango para optimizar la cantidad de desorden en un programa. Ejemplo 7.7: Generando valores aleatorios El siguiente ejemplo imprime valores aleatorios en la consola, con un rango limitado por la posición del mouse. La función random() siempre devuelve un valor de punto flotante, entonces, hay que asegurarse de que la variable a la izquierda del operador de asignación es un decimal (float) como es aquí: void draw() { float r = random(0, mouseX); println(r); } Ejempli 7-8: Example 7-8: Dibujar al azar El siguiente ejemplo construido en el ejemplo 7-7, usa los valores de random() para cambiar la posición de las lineas en la pantalla. Cuando el mouse esta en la izquierda de la pantalla, el cambio es pequeño, cuando se mueve hacia la derecha, lo valores de random() aumentan y el movimiento se hace mas exagerado. Debido a que la función random() esta dentro del loop, un nuevo valor es calculado por cada punto de cada linea:

void setup() { size(240, 120); smooth(); } void draw() { background(204); for (int x = 20; x < width; x += 20) { float mx = mouseX / 10; float offsetA = random(-mx, mx); float offsetB = random(-mx, mx); line(x + offsetA, 20, x - offsetB, 100); } }

Ejemplo 7-9: Mover las figuras al azar.
Cuando se utiliza para mover figuras alrededor de la pantalla, valores aleatorios puede generar imágenes que son más natural en apariencia. En el siguiente ejemplo, la posición del círculo es modificada por valores aleatorios en cada viaje a través de draw(). Dado que la función de background() no se utiliza, los lugares por donde pasó la figura se mantienen en la pantalla.

float speed = 2.5; int diameter = 20; float x; float y; void setup() { size(240, 120); smooth(); x = width/2; y = height/2; } void draw() { x += random(-speed, speed); y += random(-speed, speed); ellipse(x, y, diameter, diameter); }

Si ves este ejemplo por suficiente tiempo, probablemente veas un circulo dejando la pantalla y volviendo. Esto se deja al azar, pero podríamos añadir un par de estructuras o usar la función constrain() para mantener el circulo en la pantalla. La función constrain() limita el valor de un rango específico, el cual puede ser usado para mantener x e y dentro de los límites de la pantalla. Remplazando draw() en el código anterior por lo siguiente,se asegurará de que la elipse permanecerá en la pantalla: void draw() { x += random(-speed, speed); y += random(-speed, speed); x = constrain(x, 0, width); y = constrain(y, 0, height); ellipse(x, y, diameter, diameter); } ---------------------------------------------------------------------------------------------------NOTA: La función randomSeed() puede ser usada para forzar random() a producir la misma secuencia de números cada vez que el programa se ejecute. Esto se describe con más detalle en las referencias de Procesing. ---------------------------------------------------------------------------------------------------

Temporizadores:
Cada programa de Processing cuenta la cantidad de tiempo que ha pasado desde que el programa ha empezado. Se cuenta en milisegundos ( milésimas de segundo), entonces después de 1 segundo, el contador esta a 1,000; después de 5 segundos, esta a 5,000, y después de 1 minuto, esta a 60,000. Podemos usar este contador para disparar animaciones en momentos específicos. La función millis () devuelve el valor del contador.

Ejemplo 7-10: El tiempo pasa
Puedes ver el tiempo pasar cuando ejecutas el programa: void draw() { int timer = millis(); println(timer); }

Ejemplo 7-11: Provocando eventos de tiempo Triggering Timed Events
Cuando se combina un bloque if (si es A ocurre b), el valor de millis() puede ser usado para una secuencia de animación y eventos dentro de un programa. Por ejemplo, después de dos segundos transcurridos, el código dentro del bloque if puede provocar un cambio. En este ejemplo, las variables llamadas tiempo 1 y tiempo 2 determinan cuando cambiar el valor de la variable x: int time1 = 2000; int time2 = 4000; float x = 0; void setup() { size(480, 120); smooth(); } void draw() { int currentTime = millis(); background(204); if (currentTime > time2) { x -= 0.5; } else if (currentTime > time1) { x += 2; } ellipse(x, 60, 90, 90); }

Circular
Si eres un as de la trigonometría, ya sabes lo asombrosas que son las funciones seno y coseno. Si no lo eres, esperamos que los siguientes ejemplos llamen tu atención. No discutiremos el detalle matemático aquí, pero mostraremos algunas aplicaciones para generar un movimiento fluido. Figura 7-2 muestra una visualización de los valores de una onda sinusoidal y como se relacionan con los ángulos. En la parte superior e inferior de la onda. Observa como el rango de cambio (el cambio del eje vertical) se hace más lento, para, y luego cambia de dirección. Es esta cualidad en la curva que genera un movimiento interesante.

Las funciones sin() y cos() en Processing devuelven los valores entre -1 y 1 para el seno y coseno del ángulo especificado. Como arc(), el valor los angulos deben ser dados radianes ( ver ejemplo 3-7 y 3-8 para recordar como trabajar con radianes). Para que sean útiles para dibujar, los valores decimales (float) retornados por sin() y cos() son generalmente multiplicados por un número más grande.

Figura 7-2. Figure 7-2. Valores de seno y coseno.

Ejemplo 7.12: Example 7-12: Valores de onda sinusoidal
Este ejemplo muestra como los valores para sin() van desde -1 a 1 mientras el ángulo aumenta. Con la función map(), With the map() function, la variable sinval es convertida desde este rango a valores desde 0 a 255. Este nuevo valor es usado para establecer el color del fondo de la pantalla (ventana): float angle = 0.0; void draw() { float sinval = sin(angle); println(sinval); float gray = map(sinval, -1, 1, 0, 255); background(gray); angle += 0.1; }

Ejemplo 7-13: Movimiento de onda sinusoidal
Este ejemplo muestra como estos valores pueden ser convertidos en movimiento:

float angle = 0.0; float offset = 60; float scalar = 40; float speed = 0.05; void setup() { size(240, 120); smooth(); } void draw() { background(0); float y1 = offset + sin(angle) * scalar; float y2 = offset + sin(angle + 0.4) * scalar; float y3 = offset + sin(angle + 0.8) * scalar; ellipse( 80, y1, 40, 40); ellipse(120, y2, 40, 40); ellipse(160, y3, 40, 40); angle += speed; }

Ejemplo 7-14: Movimiento circular Cuando sin() y cos() son usados juntos, pueden provocar movimiento circular. El valor de cos() proveen las coordenadas x, y el valor de sin() las coordenadas y. Ambas son multiplicadas por una variable llamada scalar para cambiar el radio del movimiento y se suma con un valor de desplazamiento para establecer el centro del movimiento circular:

float angle = 0.0; float offset = 60; float scalar = 30; float speed = 0.05; void setup() { size(120, 120); smooth(); } void draw() { float x = offset + cos(angle) * scalar; float y = offset + sin(angle) * scalar; ellipse( x, y, 40, 40); angle += speed; }

Ejemplo 7-15: Espirales
Un ligero cambio hecho para incrementar el valor scalar de cada cuadro produce un espiral, en lugar de un círculo:

float angle = 0.0; float offset = 60; float scalar = 2; float speed = 0.05; void setup() { size(120, 120); fill(0); smooth(); } void draw() { float x = offset + cos(angle) * scalar; float y = offset + sin(angle) * scalar; ellipse( x, y, 2, 2); angle += speed; scalar += speed; }

Trasladar, rotar, ampliar
Cambiar las coordenadas de la pantalla es una técnica alternativa para crear movimiento. Por ejemplo, puedes mover una figura 50 pixeles a la derecha, o puedes mover la locación de la coordenada(0,0) 50 pixeles a la derecha— el resultado visual en la pantalla será el mismo. Mediante la modificación del sistema de coordenadas predeterminado, podemos crear diferente transformaciones incluyendo traslación, rotación, y ampliación. La figura 7-3 demuestra esto gráficamente.

Figura 7-3. Trasladando, rotando y ampliando las coordenadas.

Trabajar con transformaciones puede ser difícil, pero la función translate(), es la más sencilla, entonces empezaremos con ella. Esta función puede cambiar el sistema de coordenadas de izquierda, derecha, arriba y abajo con sus dos parámetro. Ejemplo 7-16: Trasladando la locación En este ejemplo, tenga en cuanta que cada rectángulo es dibujado en la coordenada (0,0), pero son movidos alrededor de la pantalla, porque están siendo afectados por translate():

void setup() { size(120, 120); } void draw() { translate(mouseX, mouseY); rect(0, 0, 30, 30); } La función translate() establece la coordenada (0,0) de la pantalla a la ubicación del mouse.En la siguiente linea, rect() dibuja a la nueva coordenada (0,0) que es en realidad dibujada en la ubicación del mouse. Ejemplo 7-17: Múltiples traslación Después que una transformación es hecha, es aplicada a todas las funciones de dibujos posteriores. Tenga en cuenta lo que pasa cuando un segundo comando de traslación es adherido para controlar un segundo rectángulo:

void setup() { size(120, 120); } void draw() { translate(mouseX, mouseY); rect(0, 0, 30, 30); translate(35, 10); rect(0, 0, 15, 15); } El rectángulo más pequeño tradujo la cantidad de mouseX+35 y mouseY+10.

Ejemplo 7.18: Aislando transformaciones
Para aislar los efectos de una transformación así no afectaran después los comandos, usa las funciones pushMatrix() y popMatrix().Cuando la función pushMatrix() es ejecutada, guarda una copia de las coordenadas actuales del sistema y luego restaura el sistema después de popMatrix(): void setup() { size(120, 120); } void draw() { pushMatrix(); translate(mouseX, mouseY); rect(0, 0, 30, 30); popMatrix(); translate(35, 10); rect(0, 0, 15, 15); }

En este ejemplo, el rectángulo más pequeño siempre es dibujado en la esquiña superior izquierda porque la translación (mouseX, mouseY) es cancelada por popMatrix(). ----------------------------------------------------------------------------------------------NOTA: Las funciones pushMatrix() y popMatrix() son siempre usadas en pares. Por cada pushMatrix(), tienes que tener un popMatrix(). -----------------------------------------------------------------------------------------------

Ejemplo 7-19: Rotación
La función rotate() rota (gira) el sistema de coordenadas. Tiene un parámetro, el cual es el ángulo (en radianes) para rotar (girar). Siempre rota (gira) en relación con (0,0) que es conocido como la rotación en torno al origen. Para girar una figura alrededor de su centro, primero usa translate() para mover la locación donde quieres la figura, luego llama a rotate(), y dibuja la figura con su centro en la coordenada (0,0):

float angle = 0.0; void setup() { size(120, 120); smooth(); } void draw() { translate(mouseX, mouseY); rotate(angle); rect(-15, -15, 30, 30); angle += 0.1; }

Ejemplo 7-20: Combinando transformaciones Cuando translate() rotate() son combinados, el orden en el cual aparecen afecta el resultado. El siguiente ejemploes identico al ejemplo 7-19, excepto que translate() y rotate() están are invertidos. Ahora la figura gira alrededor de la esquina superior izquierda de la pantalla, con la distancia desde la esquina establecida por translate(): float angle = 0.0; void setup() { size(120, 120); smooth(); } void draw() { rotate(angle); translate(mouseX, mouseY); rect(-15, -15, 30, 30); angle += 0.1; } --------------------------------------------------------------------------------------------------------------NOTA: Tambien puedes usar las funciones rectMode(), ellipseMode(), imageMode (), y shapeMode() para dibujar más facil las formas desde su centro. ---------------------------------------------------------------------------------------------------------------

Ejemplo 7-21: Ajustando (traducción: escala) La función scale() extiende las coordenadas en la pantalla. Como rotate(), transforma desde el origen. Por lo tanto, al igual que rotate(), para ajustar una forma desde su centro,trasladada desde su locación, escale, y luego dibuja con el centro en la coordenada (0,0):

float angle = 0.0; void setup() { size(120, 120); smooth(); } void draw() { translate(mouseX, mouseY); scale(sin(angle) + 2); rect(-15, -15, 30, 30); angle += 0.1; }

Ejemplo 7-22: Manteniendo las lineas (trazos) consistentes: Desde las líneas gruesas en el ejemplo 7-21, puedes ver como la función scale() afecta el grosor de la linea (stroke weight).Para mantener un grosor de la linea consistente (stroke weight) como las escalas de la figura, divide as a shape scales, divide el grosor de la linea deseado por el valor escalar: float angle = 0.0; void setup() { size(120, 120); smooth(); } void draw() { translate(mouseX, mouseY); float scalar = sin(angle) + 2; scale(scalar); strokeWeight(1.0 / scalar); rect(-15, -15, 30, 30); angle += 0.1; } Ejemplo 7-23: Un brazo articulado En este ultimo y largo ejemplo de transformación, pondremos juntos una serie de funciones translate() y rotate() para un brazo que le mueve hacia adelante y hacia atrás. Cada translate() mueve la posición de la lineas, y cada rotate() se suma a la rotación anterior para doblar más el brazo:

float angle = 0.0;

float angleDirection = 1; float speed = 0.005; void setup() { size(120, 120); smooth(); } void draw() { background(204); translate(20, 25); // Move to start position rotate(angle); strokeWeight(12); line(0, 0, 40, 0); translate(40, 0); // Move to next joint rotate(angle * 2.0); strokeWeight(6); line(0, 0, 30, 0); translate(30, 0); // Move to next joint rotate(angle * 2.5); strokeWeight(3); line(0, 0, 20, 0); angle += speed * angleDirection; if ((angle > QUARTER_PI) || (angle < 0)) { angleDirection *= -1; } } Aquí, no usamos un pushMatrix() o popMatrix(),porque queremos que las transformaciones se propaguen—por cada transformación para construir la última. El sistema de coordenadas es automáticamente reseteado al valor original cuando draw() comienza cada fotograma.

Robot 5: Movimiento

En ente ejemplo, las técnicas de movimiento aleatorio y circular son aplicadas al robot. El fondo background() fue removido para ver más fácil como la posición y el cuerpo del robot cambian. En cada cuadro, un número aleatorio entre -4 y 4 es adherido a la coordenada x, y un número aleatorio entre -1 y 1 es adherido a las coordenada y. Esto causa que el robot se mueva más de izquierda a derecha que de arriba a abajo. Números calculados desde la función sin() cambian la altura del cuello que oscila entre 50 y 110 pixles high (arriba o de alto): float x = 180; // X-coordinate float y = 400; // Y-coordinate float bodyHeight = 153; // Body height float neckHeight = 56; // Neck height float radius = 45; // Head radius float angle = 0.0; // Angle for motion void setup() { size(360, 480); smooth(); ellipseMode(RADIUS); background(204); }

void draw() {

// Change position by a small random amount x += random(-4, 4); y += random(-1, 1); // Change height of neck neckHeight = 80 + sin(angle) * 30; angle += 0.05; // Adjust the height of the head float ny = y - bodyHeight - neckHeight - radius; // Neck stroke(102); line(x+2, y-bodyHeight, x+2, ny); line(x+12, y-bodyHeight, x+12, ny); line(x+22, y-bodyHeight, x+22, ny); // Antennae line(x+12, ny, x-18, ny-43); line(x+12, ny, x+42, ny-99); line(x+12, ny, x+78, ny+15); // Body noStroke(); fill(102); ellipse(x, y-33, 33, 33); fill(0); rect(x-45, y-bodyHeight, 90, bodyHeight-33); fill(102); rect(x-45, y-bodyHeight+17, 90, 6); // Head fill(0); ellipse(x+12, ny, radius, radius); fill(255); ellipse(x+24, ny-6, 14, 14); fill(0); ellipse(x+24, ny-6, 3, 3); }