La descarga está en progreso. Por favor, espere

La descarga está en progreso. Por favor, espere

Introducción a Processing v1.2.1 Raúl Lacabanne - 2011 Versión 41 Resumen de los tópicos básicos del libro: “Processing: A Programming Handbook for Visual.

Presentaciones similares


Presentación del tema: "Introducción a Processing v1.2.1 Raúl Lacabanne - 2011 Versión 41 Resumen de los tópicos básicos del libro: “Processing: A Programming Handbook for Visual."— Transcripción de la presentación:

1 Introducción a Processing v1.2.1 Raúl Lacabanne - 2011 Versión 41 Resumen de los tópicos básicos del libro: “Processing: A Programming Handbook for Visual Designers and Artists” de Casey Reas y Ben Fry. MIT Press, 2007.

2 Design By Numbers Design By Numbers (DBN) fue creado como una plataforma de introducción al diseño asistido por ordenador para diseñadores visuales y artistas. Conceptualizado por John Maeda en el ACG (Aesthetics + Computation Group) del MIT, quien cree que la calidad del diseño y arte de medios sólo puede mejorar a través del establecimiento de infraestructuras educativas en escuelas de arte y tecnología que fomenten la formación de individuos transdiciplinarios competentes. John Maeda. DBN. 1999-2001.

3 Processing Processing es un lenguaje y entorno de programación de código abierto basado en Java, de fácil utilización, y que sirve como instrumento didáctico para la enseñanza y producción de proyectos multimedia e interactivos de diseño digital. Fue iniciado por Ben Fry y Casey Reas. Processing es desarrollado por artistas y diseñadores como una herramienta alternativa al software propietario. Puede ser utilizado tanto para aplicaciones locales así como aplicaciones para la web. Casey Reas & Ben Fry. 2001.

4 Parte 1 Elementos de sintaxis

5 Descarga y descompresión Dirigirse a la siguiente URL: http://www.processing.org/download/ y descargar la versión apropiada a su sistema operativo. Se recomienda a los usuarios de Windows descargar la versión Windows (a secas) y no la versión Windows (Without Java).http://www.processing.org/download/ Una vez descargado el archivo, descomprimirlo en alguna carpeta del disco rígido, ej: el escritorio. Esto quiere decir que el entorno de desarrollo Processing no necesita instalación. A continuación abrirá la carpeta descomprimida y ejecutará el archivo correspondiente al icono:

6 Comentarios En Processing tenemos dos formas de realizar comentarios. El primero es un comentario simple: //esto es un comentario... //...y esto también lo es el segundo es un bloque de comentario: /* esto también es un comentario */

7 Funciones (I) Las funciones permiten ejecutar algoritmos, es decir, obtener un resultado a partir de un o una serie de pasos lógicos y estructurados. En general, el nombre de una función comienza en minúsculas y es seguido por un par de paréntesis. Los elementos que se encuentran entre los paréntesis se llaman parámetros.

8 Funciones (II) Ejemplo de funciones size(200, 200) background(102) noCursor()

9 Sensibilidad a mayúsculas y minúsculas b =/= B size() =/= Size()

10 Espaciado Processing es flexible a los espaciados. Sin embargo se recomienda ser prudente con los mismos para que la lectura del código sea cómoda.

11 Instrucciones (I) Si utilizamos una metáfora del lenguaje humano, podemos entender a la instrucción como si fuera una oración. Las instrucciones siempre se deben terminan con el signo punto y coma (;). Dentro de una instrucción se pueden realizar las siguientes acciones: definir una variable o array, asignar un valor a una variable (o valores a un array), ejecutar una función, o construir un objeto

12 Instrucciones (II) Ejemplo de un boceto que contiene un conjunto de instrucciones: size(200, 200); // Ejecuta la función size() con dos parámetros background(102); // Ejecuta la función background() con un parámetro noCursor(); // Ejecuta la función noCursor() con ningún parámetro

13 Lectura recomendada Capítulo “Structure 1: Code Elements” (pag. 17). Reas, C. & Fry, B. "Processing: A Programming Handbook for Visual Designers and Artists”, MIT Press, 2007.

14 Información complementaria

15 Utilización de la consola // Para imprimir un valor – Uso de la función print() print(10); // Imprime 10 en la consola // Para imprimir un texto, coloque el mismo entre comillas print(“processing"); // Imprime processing a continuación del dato anterior // Para imprimir en líneas separadas – Uso de la función println() println(10); // Imprime 10 en la consola y salta a una nueva línea println(“processing"); // Imprime processing y salta a una nueva línea

16 Función cursor() y noCursor() cursor() Sintaxis  cursor()  cursor(MODE)  cursor(image, x, y) Parámetros  MODE: Puede optar por ARROW, CROSS, HAND, MOVE, TEXT, WAIT  image PImage: cualquier variable del tipo PImage  x int: el punto activo horizontal del cursor  y int: el punto activo vertical del cursor noCursor()

17 Parte 2 Coordenadas y figuras primitivas

18 Coordenadas En Processing, el origen se encuentra en el margen superior izquierdo. 0, 0 +100

19 Primitivas: punto point(20, 20); point(30, 30); point(40, 40); point(50, 50); point(60, 60);

20 Primitivas: línea line(25, 90, 80, 60); line(50, 12, 42, 90); line(45, 30, 18, 36);

21 Primitivas: elipse ellipse(40, 40, 60, 60); // círculo grande ellipse(75, 75, 32, 32); // círculo pequeño

22 Primitivas: rectángulo rect(15, 15, 40, 40); // cuadrado grande rect(55, 55, 25, 25); // cuadrado pequeño

23 Orden de dibujo En Processing, el orden de dibujo siempre es secuencial, es decir, la primer instrucción se representa primero, las siguientes por encima, y la última por sobre todas estas. rect(15, 15, 60, 60); // cuadrado inferior rect(20, 20, 40, 40); // cuadrado intermedio rect(25, 25, 20, 20); // cuadrado superior

24 Suavizado smooth() noSmooth()

25 Atributos: strokeWeight() tamaño de contorno smooth(); strokeWeight(1); // por defecto line(20, 20, 80, 20); strokeWeight(4); // 4 pixeles line(20, 40, 80, 40); strokeWeight(10); // 10 pixeles line(20, 70, 80, 70);

26 Atributos: strokeCap() extremo de contorno smooth(); strokeWeight(12.0); strokeCap(ROUND); // redondeado line(20, 30, 80, 30); strokeCap(SQUARE); // plano line(20, 50, 80, 50); strokeCap(PROJECT); // proyección line(20, 70, 80, 70);

27 Atributos: strokeJoin() extremo de contorno smooth(); strokeWeight(10); strokeJoin(MITER); // mitra triangle(50, 20, 80, 80, 20, 80); smooth(); strokeWeight(10); strokeJoin(BEVEL); // bisel triangle(50, 20, 80, 80, 20, 80); smooth(); strokeWeight(10); strokeJoin(ROUND); // redondeada triangle(50, 20, 80, 80, 20, 80);

28 Lectura recomendada Capítulo “Shape 1: Coordinates, Primitives” (pag. 23).

29 Información complementaria

30 Primitivas: triángulo triangle(60, 10, 25, 60, 75, 65);

31 Primitivas: cuadrilátero quad(38, 31, 86, 20, 69, 63, 30, 76);

32 Primitivas: curva bezier bezier(32, 20, 80, 5, 80, 75, 30, 75); // Dibujo de puntos de control line(32, 20, 80, 5); ellipse(80, 5, 4, 4); line(80, 75, 30, 75); ellipse(80, 75, 4, 4);

33 Ejercicio 1 EJ01: Escribir un boceto con la finalidad de componer un cuadro, con una resolución de 300x300 píxeles, que contenga las siguientes figuras: – dos elipses, cuatro líneas y un rectángulo. Comentar todas las instrucciones. De aquí en más adaptar el siguiente comentario (a modo de título) e ingresarlo en el lugar de la primera instrucción: // *********************************************** // * Alumno: Nombre y apellido del alumno * // * Legajo: xxxxx * // * Ejercicio Nro: 01 * // * Asignatura: xxxx * // * Carrera: xxxx * // * Institución: UNQ * // * Año: XXXX Cuatrimestre: x * // ***********************************************

34 Ejercicio 2 EJ02: Componer un cuadro con una resolución de 300x300 que contenga las siguientes figuras: – dos elipses, cuatro líneas y un rectángulo. Generar las figuras con cambios de atributos. Comentar todas las instrucciones.

35 Parte 3 Modos de color. Fondo, contorno y relleno

36 colorMode() - Modo de color En general trabajamos con RGB pero es más recomendable trabajar con HSB ya que se ajusta más a un modelo de color plástico. Para esto utilizamos la función colorMode(). Veremos que para utilizar la función colorMode() podremos escoger cuatros versiones distintas de dicha función: colorMode(modo)HSB o RGB colorMode(modo, rango)int o float colorMode(modo, rango1, rango2, rango3)int o float colorMode(modo, rango1, rango2, rango3, alpha)int o float

37 Sintaxis de colorMode() Sintaxis colorMode(modo); colorMode(mode, rango); colorMode(modo, rango1, rango2, rango3); colorMode(modo, rango1, rango2, rango3, alpha); Parámetros modoRGB o HSB:correspondientes a Red/Green/Blue y Hue/Saturation/Brightness. range int o float:rango para todos los elementos de color rango1 int o float:rango para el Rojo o Tono dependiendo del modo de color actual. rango2int o float:rango para el Verde o Saturación dependiendo del modo de color actual. rango3 int o float:rango para el Azul o Brillo dependiendo del modo de color actual. alphaint o float:rango para Alpha (0 transparencia total, máximo opacidad total).

38 Ejemplo de colorMode() colorMode(HSB, 360, 100, 100);

39 Fondo, contorno y relleno Fondo = background() Valor por defecto = 204 (gris claro) Contorno = stroke() Valor por defecto = 0 (negro) Sin contorno = noStroke() Relleno = fill() Valor por defecto = 255 (blanco) Sin relleno = noFill()

40 Sintaxis de background() background(gray) background(gray, alpha) background(value1, value2, value3) background(value1, value2, value3, alpha) background(color) background(color, alpha) background(hex) background(hex, alpha)

41 Parámetros de las versiones de background() grayint o float: valores entre blanco y negro. alpha int o float: valor de opacidad (0 = transparencia – 255 = opacidad). value1 int o float: rojo o valor de matiz (depende del modo de color). value2 int o float: verde o valor de saturación (depende del modo de color). value3 int o float: azul o valor de brillo (depende del modo de color). color color: cualquier valor del tipo de dato color. hex int: valor de color en notación hexadecimal (ej.: #FFCC00 ó 0xFFFFCC00).

42 Ejemplos de background() colorMode(HSB, 360, 100, 100); background(51); o bien: colorMode(HSB, 360, 100, 100); background(255, 204, 0);

43 Parámetros de versiones de background(), stroke() y fill() grayint o float: valores entre blanco y negro. alpha int o float: valor de opacidad (0 = transparencia – 255 = opacidad). value1 int o float: rojo o valor de matiz (depende del modo de color). value2 int o float: verde o valor de saturación (depende del modo de color). value3 int o float: azul o valor de brillo (depende del modo de color). color color: cualquier valor del tipo de dato color. hex int: valor de color en notación hexadecimal (ej.: #FFCC00 or 0xFFFFCC00).

44 Sintaxis de stroke() stroke(gray) stroke(gray, alpha) stroke(value1, value2, value3) stroke(value1, value2, value3, alpha) stroke(color) stroke(color, alpha) stroke(hex) stroke(hex, alpha)

45 Ejemplos de stroke() stroke(153); rect(30, 20, 55, 55); o bien: stroke(204, 102, 0); rect(30, 20, 55, 55);

46 Sintaxis de fill() fill(gray) fill(gray, alpha) fill(value1, value2, value3) fill(value1, value2, value3, alpha) fill(color) fill(color, alpha) fill(hex) fill(hex, alpha)

47 Ejemplos de fill() fill(153); rect(30, 20, 55, 55); o bien: fill(204, 102, 0); rect(30, 20, 55, 55);

48 Lectura recomendada Capítulo “Color 1: Color by Numbers” (pag. 85).

49 Ejercicio 3 EJ03: Componer un cuadro con una resolución de 300x300 que contenga las siguientes figuras: – dos elipses, cuatro líneas y un rectángulo. Las figuras podrán tener sólo dos colores y el fondo de la composición (background) otro distinto. Comentar todas las instrucciones.

50 Parte 4 Tipos de datos. Variables.

51 Datos En general consisten de mediciones de características físicas. Processing puede administrar distintos tipos de datos: números, letras, colores, imágenes, tipografías y valores booleanos.

52 Tipos de datos básicos NombreTamañoRango de valores boolean1 bittrue o false byte8 bits-128 a 127 char16 bits0 a 65535 int32 bits -2,147,483,648 a 2,147,483,647 float32 bits -3.40282347E+38 a 3.40282347E+38 color32 bits16,777,216

53 Variables (I) Podemos entender una variable como un contenedor que nos permite almacenar un tipo de dato. Las variables permiten la reutilización de datos en un programa tantas veces como se necesite. Las variables constan de tres partes: tipo de dato,=>float nombre de la variable=>altura y valor=>“1.72”

54 Variables (II) En Processing, cuando trabajamos con variables, primero debemos declararla y luego asignar el valor que corresponda: int x; // declaración de la variable x de tipo int float y; // declaración de la variable y de tipo float boolean b;// declaración de la variable b de tipo boolean x = 50;// asignación del valor 50 a la variable x y = 12.6;// asignación del valor 12.6 a la variable y b = true;// asignación del valor true a la variable b Durante la asignación utilizamos el signo “=“, el cual es llamado operador de asignación. El sentido de asignación se da siempre de derecha a izquierda de dicho signo.

55 Variables (III) Los pasos de declaración y asignación pueden ser resumidos en una sola línea de código: int x = 50; // declaración y asignación float y = 12.6; // declaración y asignación boolean b = true; // declaración y asignación

56 Instrucciones (III) Otro ejemplo de boceto que contiene un conjunto de instrucciones: size(200, 200);// Ejecuta la función size() int x;// Declara una nueva variable x x = 102;// Asigna el valor 102 a la variable x background(x);// Ejecuta la función background()

57 Lectura recomendada Capítulo “Data 1: Variables” (pag. 37).

58 Parte 5 Aritmética

59 Operadores aritméticos básicos en Processing +Suma -Resta *Multiplicación /División %Módulo

60 Orden de ejecución de operaciones aritméticas ??? 3 + 4 * 5 1) * / % 2) + - 3) =

61 Expresiones Podemos pensar en una expresión como si fuera una frase. Las expresiones contienen frecuentemente o bien un solo valor, o combinaciones de valores y operadores matemáticos y/o relacionales. Una expresión siempre tiene un valor determinado por la evaluación de su contenido: ExpresiónValor5 122.3 + 3.1125.4 ((3 + 2) * -10) + 1-49 6 > 3true 54 < 50false Muchas veces un conjunto de expresiones conforma una instrucción.

62 Uso de una variable como parámetro de función Ejemplo: int a = 30; line(a, 0, a, height);

63 Uso de una variable como componen-te de una expresión de asignación Ejemplo: int a = 30; int b = a + 40; line(b, 0, b, height);

64 Uso de variables como componentes de una expresión perteneciente a un parámetro de función Ejemplo: int a = 30; int b = 40; line(b - a, 0, b - a, height);

65 Lectura recomendada Capítulo “Math 1: Arithmetic, Functions” (pag. 43).

66 Información complementaria

67 Operador Módulo % El operador % calcula el resto de un cociente. A menudo se lo utiliza para mantener los números dentro de un rango deseado. Por ej.: si partimos de un contador ascendente desde el número 0 y queremos obtener cuatro valores que se reiteren (un ciclo de valores), usamos la siguiente expresión: x0 1 2 3 4 5 6 7 8 9 10 11 12 … x % 40 1 2 3 0 1 2 3 0 1 2 3 0 …

68 Atajos aritméticos Operador incremental ++ int x = 1; println(x); // Imprime "1" en la consola x++; // Equivale a x = x + 1 println(x); // Imprime "2" en la consola Operador decremental -- int y = 1; println(y); // Imprime "1" en la consola y--; // Equivale a y = y - 1 println(y); // Imprime “0" en la consola

69 x++ ó ++x x++ En este caso el valor es incrementado LUEGO de que se evalúa la expresión. int x = 1; println(x++); // Imprime "1" en la consola println(x); // Imprime “2" en la consola ++x En este segundo caso se actualiza el valor ANTES de evaluar la expresión. int x = 1; println(++x); // Imprime “2" en la consola println(x); // Imprime “2" en la consola

70 Operadores de asignación de suma y substracción Se utilizan para realizar saltos de más de un paso (una unidad de valor). Suma += int x = 1; println(x); // Imprime "1" en la consola x += 5; // Equivalente a x = x + 5 println(x); // Imprime “6" en la consola Resta -= int y = 1; println(y); // Imprime "1" en la consola y -= 5; // Equivalente a y = y - 5 println(y); // Imprime “-4" en la consola

71 Operadores de asignación de multiplicación y división Se utilizan para realizar saltos de más de un paso (una unidad de valor). Multiplicación *= int x = 4; println(x); // Imprime "4" en la consola x *= 2; // Equivalente a x = x * 2 println(x); // Imprime "8" en la consola División /= int y = 4; println(y); // Imprime "4" en la consola y /= 2; // Equivalente a y = y / 2 println(y); // Imprime “2" en la consola

72 Operadores de negación Cambia el signo del valor. Negación – int x = 5; // Asigna 5 a x x = -x; // Equivalente a x = x * -1 println(x); // Imprime "-5"

73 Ejercicio 4 EJ04: Tomar el EJ03, declarar tres variables y utilizarlas en tres contextos distintos: 1) como parámetro de función. 2) como componente de una expresión de asignación. Utilizar operadores aritméticos de suma o resta. 3) como componente de una expresión perteneciente a un parámetro de función. Utilizar operadores aritméticos de suma o resta. Comentar todas las instrucciones.

74 Parte 6 Control: Decisiones

75 Expresiones relacionales Las expresiones relacionales nos informan la condición de verdad de dicha expresión. Una expresión relacional compara dos valores y evalúa si el resultado es verdadero o falso. ExpresiónEvaluación 3 > 5 false 3 < 5 true 5 < 3 false 5 > 3 true

76 Operadores relacionales OperadorSignificado > mayor a < menor a >= mayor o equivalente a <= menor o equivalente a == equivalente a != no equivalente a

77 Condicional: if

78 Ejemplo: if boolean dibujoCirculo = true; if (dibujoCirculo == true) { ellipse(50, 50, 50, 50); } rect(30, 45, 40, 10);

79 Condicional: if/else

80 Ejemplo: if/else boolean dibujoCirculo = true; if (dibujoCirculo == true) { ellipse(50, 50, 50, 50); } else { line(25, 25, 75, 75); line(75, 25, 25, 75); } rect(30, 45, 40, 10);

81 Condicional: if/else if

82 Ejemplo: if/else if boolean dibujoCirculo = false; boolean dibujoElipse = false; if (dibujoCirculo == true) { ellipse(50, 50, 50, 50); } else if (dibujoElipse == true){ ellipse(50, 50, 50, 25);} rect(30, 45, 40, 10);

83 Condicional: switch() Funciona como una estructura “if”, sin embargo es más conveniente utilizar switch() cuando usted necesita seleccionar entre tres o más alternativas. El control del programa se dirige al caso (case) que contenga el mismo valor de la expresión. Todas las demás instrucciones del switch() serán ejecutadas a menos que sean redirigidas mediante un corte (break). Sólo los tipos de datos primitivos que pueden ser convertidos a un entero (byte, char e int) pueden ser usados como parámetro de expresión. El caso por defecto (default) es opcional. Sintaxis switch(expresión) { case etiqueta1: instrucciones case etiqueta2: // Opcional instrucciones // Opcional default: // Opcional instrucciones // Opcional } Parámetros expresión byte, char o int etiqueta byte, char o int instrucciones una o más

84 Ejemplo 1/3: switch() int num = 1; switch(num) { case 0: println("Cero"); // No se ejecuta break; case 1: println("Uno"); // Imprime "Uno" break; } println("¡Listo!"); // Imprime "¡Listo!"

85 Ejemplo 2/3: switch() char letra = 'N'; switch(letra) { case 'A': println("Alfa"); // No se ejecuta break; case 'B': println("Bravo"); // No se ejecuta break; default: // El caso por defecto se ejecuta si println("Ninguna"); // ninguna etiqueta coincide con el break; // parámetro de switch(). } println("¡Listo!"); // Imprime "¡Listo!"

86 Ejemplo 3/3: switch() // La remoción de un "break" permite la // evaluación de más de un valor a la vez char letra = 'b'; switch(letra) { case 'a': case 'A': println("Alfa"); // No se ejecuta break; case 'b': case 'B': println("Bravo"); // Imprime "Bravo" break; } println("¡Listo!"); // Imprime "¡Listo!"

87 Operadores lógicos (I) En la lógica proposicional se utilizan conectivas lógicas, también llamadas operadores lógicos. En programación se los utilizan para combinar valores de verdad y obtener nuevos valores que determinen el flujo de control de un algoritmo o programa. Los operadores lógicos presentes en Processing se usan para combinar dos o más expresiones relacionales y/o para invertir los valores lógicos. Permiten considerar más de una condición simultáneamente. Operador Significado && AND (Y – conjunción) || OR (o – disyunción (disyunción inclusiva)) ! NOT (no - negación)

88 Operadores lógicos (II) Tabla de funciones de verdad: Expresión Evaluación true && true true true && false false false && true false false && false false true || true true true || false true false || true true false || false false !true false !false true

89 Operador lógico AND El operador AND hace que una expresión relacional sea verdadera si AMBAS partes son verdaderas. int a = 10; int b = 20; // La expresión "a > 5" debe ser verdadera // y "b < 30“ tambiçen debe serlo. // Debido a que ambas son verdaderas, se ejecutará el código del bloque. if ((a > 5) && (b < 30)) { line(20, 50, 80, 50); } // La expresión "a > 15" es falsa, pero "b < 30" es verdadera. // Debido a que el operador AND requiere que ambas sean // verdaderas, no se ejecutará el código del bloque. if ((a > 15) && (b < 30)) { ellipse(50, 50, 36, 36); }

90 Operador lógico OR El operador OR hace que una expresión relacional sea verdadera si SÓLO una parte es verdadera. int a = 10; int b = 20; // Cualquiera de las dos expresiones pueden ser verdaderas. // Debido a que ambas son verdaderas, se ejecutará el código del bloque. if ((a > 5) || (b < 30)) { line(20, 50, 80, 50); } // La expresión "a > 15" es falsa, pero "b < 30" es verdadera. // Debido a que el operador OR requiere sólo que una parte sea verdadera // en toda la expresión, se ejecutará el código del bloque. if ((a > 15) || (b < 30)) { ellipse(50, 50, 36, 36); }

91 Operador lógico NOT El operador NOT se nota mediante un signo de exclamación (!). Invierte el valor lógico de las variables booleanas asociadas. Es decir cambia los valores TRUE a FALSE y viceversa. El operador lógico NOT se aplica sólo a variables booleanas. boolean b = true;// Asigna true a b println(b); // Imprime "true" println(!b); // Imprime "false" b = !b; // Asigna false a b println(b); // Imprime "false" println(!b); // Imprime "true" println(5 > 3); // Imprime "true" println(!(5 > 3));// Imprime "false" int x = 5;// Declara y asigna 5 a x println(!x); // ¡ERROR! Sólo es posible trabajar con // variables booleanas

92 Lectura recomendada Capítulo “Control 1: Decisions” (pag. 51).

93 Ejercicio 5 EJ05: Realizar un boceto donde se declare una variable que, en función del valor dibuje: – TRUE: un círculo con relleno negro. – FALSE: un círculo con relleno blanco. Comentar todas las instrucciones.

94 Ejercicio 6 EJ06: Realizar un boceto donde se dibuje una línea horizontal a mitad de pantalla, y que además a partir del valor de la declaración de una variable dibuje: – TRUE: un círculo con relleno negro. – FALSE: un círculo con relleno blanco. Comentar todas las instrucciones.

95 Ejercicio 7 EJ7: Realizar un boceto donde se declare una variable que en función del valor dibuje: – un círculo sin relleno. – un círculo con relleno blanco. – un círculo con relleno negro. Comentar todas las instrucciones.

96 Parte 7 Control: Iteraciones

97 Las estructuras iterativas para simplificar instrucciones repetitivas. Ejemplo código original size(200, 200); line(20, 20, 20, 180); line(30, 20, 30, 180); line(40, 20, 40, 180); line(50, 20, 50, 180); line(60, 20, 60, 180); line(70, 20, 70, 180); line(80, 20, 80, 180); line(90, 20, 90, 180); line(100, 20, 100, 180); line(110, 20, 110, 180); line(120, 20, 120, 180); line(130, 20, 130, 180); line(140, 20, 140, 180); Ej. código optimizado size(200, 200); for (int i = 20; i < 150; i += 10) { line(i, 20, i, 180); }

98 Estructura y funcionamiento general de for for (init; test; update) { statements } 1.Se ejecuta la instrucción init. 2.Se evalua la condición true o false de test. 3.Si test es true, continúa al paso 4. Si el test es false salta al paso 6. 4.Ejecuta las instrucciones del bloque. 5.Ejecuta la instrucción update y salta al paso 2. 6.Sale de la estructura de iteración y continúa ejecutando el programa.

99 Iteración: for

100 Ejemplo: for for (int i = 10; i <= 90; i += 5) { line(i, 10, i, 90); }

101 Iteraciones anidadas La estructura for produce repeticiones en una dimensión. Si anidamos esta estructura DENTRO de otra, combinando su efecto, crearemos iteraciones en dos dimensiones. Ej. 1 for (int y = 10; y < 100; y += 10) { point(10, y); } Ej. 2 for (int x = 10; x < 100; x += 10) { point(x, 10); } Ej. 1 y 2 anidados for (int y = 10; y < 100; y += 10) { for (int x = 10; x < 100; x += 10) { point(x, y); } Por cada punto dibujado en la estructura externa, se dibujan 9 puntos en la estructura interna.

102 Iteración: while La estructura while ejecuta una serie de instrucciones de manera continua mientras que la expresión sea verdadera. La expresión debe ser actualizada durante la iteración, de lo contrario nunca saldremos del while. Esta función puede resultar peligrosa ya que el código dentro del bucle while() no se detendrá hasta que la expresión dentro del mismo resulte falsa. Bloqueará cualquier otro código a ser utilizado (los eventos de ratón no serán actualizados, etc.). Por lo tanto debemos ser cautelosos ya que podemos llegar a inmovilizar el código (y hasta a veces el entorno Processing mismo) si se usa de manera incorrecta. Sintaxis while (expresión) { instrucciones } Parámetros expresión una expresión válida instrucciones una o más

103 Ejemplo: while int i=0; while(i < 80) { line(30, i, 80, i); i = i + 5; }

104 Lectura recomendada Capítulo “Control 2: Repetition” (pag. 61).

105 Información complementaria

106 Ejemplo 1/16: for for (int x = -16; x < 100; x += 10) { line(x, 0, x+15, 50); } strokeWeight(4); for (int x = -8; x < 100; x += 10) { line(x, 50, x+15, 100); }

107 Ejemplo 2/16: for noFill(); for (int d = 150; d > 0; d -= 10) { ellipse(50, 50, d, d); }

108 Ejemplo 3/16: for /* Cambio de matiz, mientras la saturación y el brillo se mantienen constantes */ colorMode(HSB); for (int i = 0; i < 100; i++) { stroke(i*2.5, 255, 255); line(i, 0, i, 100); }

109 Ejemplo 4/16: for /* Cambio de saturación, mientras el matiz y el brillo se mantienen constantes */ colorMode(HSB); for (int i = 0; i < 100; i++) { stroke(132, i*2.5, 204); line(i, 0, i, 100); }

110 Ejemplo 5/16: for /* Cambio de brillo, mientras el matiz y la saturación se mantienen constantes */ colorMode(HSB); for (int i = 0; i < 100; i++) { stroke(132, 108, i*2.5); line(i, 0, i, 100); }

111 Ejemplo 6/16: for /* Cambio de saturación y brillo, mientras el matiz se mantiene constante */ colorMode(HSB); for (int i = 0; i < 100; i++) { for (int j = 0; j < 100; j++) { stroke(132, j*2.5, i*2.5); point(i, j); }

112 Ejemplo 7/16: for // Cambio del azul al verde en modo RGB colorMode(RGB); for (int i = 0; i < 100; i++) { float r = 61 + (i*0.92); float g = 156 + (i*0.48); float b = 204 - (i*1.43); stroke(r, g, b); line(i, 0, i, 100); }

113 Ejemplo 8/16: for // Cambio del azul al verde en modo HSB colorMode(HSB, 360, 100, 100); for (int i = 0; i < 100; i++) { float newHue = 200 - (i*1.2); stroke(newHue, 70, 80); line(i, 0, i, 100); }

114 Ejemplos 9 a 12/16: for

115 Ejemplos 13 a 16/16: for

116 Ejemplos 1/8: iteraciones anidadas for (int y = 1; y < 100; y += 10) { for (int x = 1; x < y; x += 10) { line(x, y, x+6, y+6); line(x+6, y, x, y+6); }

117 Ejemplos 2/8: iteraciones anidadas noStroke(); for (int y = 0; y < 100; y += 10) { for (int x = 0; x < 100; x += 10) { fill((x+y) * 1.4); rect(x, y, 10, 10); }

118 Ejemplos 3 a 4/8: iteraciones anidadas

119 Ejemplos 5 a 6/8: iteraciones anidadas

120 Ejemplos 7 a 8/8: iteraciones anidadas

121 Ejercicio 8 EJ08: Utilizar dos estructuras for() para generar: – un fondo con cambio de matiz; – y por sobre este, una serie de ocho cuadrados negros concéntricos (el menor deberá tener 10 px de lado y el mayor 80 px), separados por una distancia de 5 px en cada uno de sus lados. Comentar todas las instrucciones.

122 Parte 8 Aleatoriedad

123 Valores inesperados La función random() es utilizada para crear valores impredecibles dentro de un rango especificado por sus parámetros. Los números devueltos por la función random() son siempre de punto flotante. random(valorAlto) random(valorBajo, valorAlto) random(5);// regresa valores entre 0.0 y 5.0 random(5.0);// regresa valores entre 0.0 y 5.0 random(-5.0, 10.2);// regresa valores entre -5.0 y 10.2

124 Ejemplos (I) smooth(); strokeWeight(10); stroke(0, 130); line(0, random(100), 100, random(100));

125 Ejemplos (II) smooth(); strokeWeight(20); float r = random(5, 45); stroke(r * 5.6, 230); line(0, r, 100, random(55, 95)); r = random(5, 45); stroke(r * 5.6, 230); line(0, r, 100, random(55, 95)); r = random(5, 45); stroke(r * 5.6, 230); line(0, r, 100, random(55, 95));

126 Ejemplos (III) background(0); stroke(255, 60); for (int i = 0; i < 100; i++) { float r = random(10); strokeWeight(r); float desplazamiento = r * 5.0; line(i-20, 100, i+desplazamiento, 0); }

127 randomSeed() - Semillas Ya que el ordenador no puede “inventar numeros al azar”, estos se obtienen con más o menos complejas ecuaciones. Por lo tanto son repetibles. Para obtener cadenas de valores reiterados, utilizamos una semilla mediante la función randomSeed(). randomSeed(valor) El valor debe ser siempre un int.

128 Ejemplo int s = 6; // Valor de semilla, probar: s=6 o s=12 background(0); stroke(255, 60); randomSeed(s); // Produce los mismos nros cada vez for (int i = 0; i < 100; i++) { float r = random(10); strokeWeight(r); float desplazamiento = r * 5; line(i-20, 100, i+ desplazamiento, 0); }

129 noise() – Ruido (I) La función noise() es utilizada para crear valores inesperados de una manera más controlada. Utiliza la técnica de ruido Perlin, fue desarrollada por el matemático Ken Perlin en 1985. Funciona interpolando valores aleatorios para crear transiciones más suaves que las obtenidas mediante la función random(). Siempre devuelve valores de punto flotante entre 0.0 y 1.0.

130 noise() – Ruido (II) noise(x) noise(x, y) noise(x, y, z) La versión con un solo parámetro es utilizada para crear una secuencia única de números aleatorios, Los parámetros adicionales producen ruido en más dimensiones (2D: texturas, 3D: formas, texturas 3D o texturas animadas en 2D).

131 noise() – Ruido (III) Los números devueltos por noise() pueden resultar más cercanos o más lejanos del anterior mediante cambios en el parámetro de frecuencia de incremento. Como regla general, mientras más pequeña sea la diferencia, más suave resultará la secuencia de ruido. Entonces resulta que un incremento pequeño genera números más cercanos al valor anterior que un incremento mayor. Se estila nombrar la variable de incremento como inc.

132 noiseSeed() – semilla de ruido La función noise() suele ser utilizada en conjunto con la función noiseSeed(), siendo esta similar a randomSeed(). Generalmente se utilizan valores de incremento que se sitúan en el rango 0.005 a 0.03.

133 Ejemplo con un parámetro size(600, 100); float v = 0.0; float inc = 0.1; // Probar 0.1 o 0.01 noStroke(); fill(0); noiseSeed(0); for (int i = 0; i < width; i = i+4) { float n = noise(v) * 70.0; rect(i, 10 + n, 3, 20); v = v + inc; }

134 Ejemplo con dos parámetros float xnoise = 0.0; float ynoise = 0.0; float inc = 0.04; //Probar 0.04, 0.02 o 0.1 for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { float gray = noise(xnoise, ynoise) * 255; stroke(gray); point(x, y); xnoise = xnoise + inc; } xnoise = 0; ynoise = ynoise + inc; }

135 Lectura recomendada Capítulo “Math 4: Random” (pag. 127).

136 Ejercicio 9 EJ09: Utilizar una estructura for() para generar: – diez círculos sin relleno, con posición, diámetro y color aleatorios. Usar función random(). Comentar todas las instrucciones.

137 Ejercicio 10 EJ10: Utilizar una estructura for() para generar: – diez rectángulos sin relleno, con posición, diámetro y color aleatorios pero cuyos valores resulten cercanos entre sí. Usar función noise(). Comentar todas las instrucciones.

138 Parte 9 Continuidad: de la imagen estática a la dinámica

139 Ejecución continua Todos los bocetos programados hasta el momento son ejecutados por única vez y luego se detienen. Programas animados o sensibles a información en vivo deben ejecutarse continuamente.

140 Función draw() Los programas que se ejecutan continuamente DEBEN incluir una función llamada draw(). El código incluido en el bloque de la función draw() se ejecuta en forma de BUCLE hasta que el usuario presione el botón de detención o cierre la ventana. Un programa puede tener un único draw(). Cada vez que el bloque draw() finaliza, dibuja un nuevo cuadro (frame) y comienza nuevamente el código del bloque desde su primera línea.

141 Bloque de función draw() Por defecto, el ordenador intenta dibujar 60 cuadros por segundo. Si utilizamos la variable de sistema frameRate podremos saber cuantos cuadros máximos por segundo puede renderizar nuestro ordenador. void draw() { println(frameRate); }

142 Función frameRate() La función frameRate() determina el número máximo de cuadros por segundo que renderizará el programa siempre y cuando no exceda los valores posibles a realizar por el sistema. Si utilizamos la variable de sistema frameCount podremos llevar un registro de la cantidad de cuadros renderizados hasta el momento: void draw() { frameRate(1); println(frameCount); }

143 Animación (I) Creamos animaciones cuando cambiamos atributos visuales de un cuadro al otro: float y = 0.0; void draw() { frameRate(30); line(0, y, 100, y); y = y + 0.5; }

144 Animación (II) Debe ser observado que se define la variable y por fuera del bloque draw(). ¿Qué hubiera pasado si la incluíamos dentro? void draw() { frameRate(30); float y = 0.0; line(0, y, 100, y); y = y + 0.5; }

145 Animación (III) Para que tengamos una animación la variable y debe ser declarada fuera del bloque, ya que de lo contrario esta se redefiniría y reasignaría al MISMO valor cada vez que se vuelve a iniciar el bucle.

146 Animación (IV) Como podemos observar, el fondo del lienzo va cambiando de color de acuerdo a cada nueva línea que aparece. SI deseamos poder ver el avance de la línea sobre el fondo, debemos “refrescar” el fondo cada vez que reiniciemos el draw() mediante la función background(). float y = 0.0; void draw() { frameRate(30); background(204); line(0, y, 100, y); y = y + 0.5; }

147 Animación (V) Incorporando una pequeña expresión en los parámetros de background(), la animación va ganando interés! float y = 0.0; void draw() { frameRate(30); background(y * 2.5); line(0, y, 100, y); y = y + 0.5; }

148 Animación (VI) Y si incorporamos una pequeña estructura de condicional generamos ciclos visibles! float y = 0.0; void draw() { frameRate(30); background(204); line(0, y, 100, y); y = y + 0.5; if (y > height) { y = 0; }

149 Animación (VII) O bien podemos volver a tomar la animación IV y modificarla para obtener otro resultado visual: int y = 0; int direccion = 1; void draw() { frameRate(30); background(204); if (y > 100) { direccion = -1; } if (y < 0) { direccion = 1; } y = y + (1 * direccion); line(0, y, 100, y); }

150 Animación (VIII) Esta es otra manera de resolver la animación anterior. No existen formas fijas de resolver un problema, por suerte podemos abordar diferentes enfoques para la resolución del mismo: int y = 0; int direccion = 1; void draw() { frameRate(30); background(204); if (y > 100 || y < 0) { direccion *= -1; } y = y + (1 * direccion); line(0, y, 100, y); }

151 Lectura recomendada Capítulo “Structure 2: Continuous” (pag. 173).

152 Ejercicio 11 EJ11: Utilizar la estructura draw() y dos variables para animar: – una elipse y tres líneas. Comentar todas las instrucciones.

153 Parte 10 Anatomía de un programa estructurado

154 Función setup() Para optimizar la programación, nos damos cuenta que ciertas funciones sólo deben ser ejecutadas una vez. Exponemos como ejemplo la función frameRate() de los bocetos anteriores. float y = 0.0; void setup() { frameRate(30); } void draw() { background(y * 2.5); y = y + 0.5; line(0, y, 100, y); if (y > 100) { y = 0; }

155 Bloque de Función setup() Cuando en Processing se ejecuta un programa, el código que se encuentra fuera de los bloques setup() y draw() es contemplado en primera instancia. Luego, se ejecuta por única vez el código dentro del bloque setup(). Por último, se ejecuta continuamente el contenido del bloque draw().

156 Diagrama de estructura formal standard de un programa Sección de variables globales Sección de inicialización Sección de dibujo

157 Ejemplo float y = 0.0; void setup() { size(100, 100); smooth(); fill(0); } void draw() { background(204); ellipse(50, y, 70, 70); y += 0.5; if (y > 150) { y = -50.0; }

158 Observaciones (I) Las variables que cambian en cada repetición del bloque draw() DEBEN ser declaradas fuera de los bloques de setup() y draw(). Si su programa dibuja un solo cuadro, puede escribirlo por completo dentro del bloque setup(): void setup() { size(100, 100); smooth(); fill(0); ellipse(50, 50, 66, 66); }

159 Observaciones (II) Otra manera de realizar un solo cuadro, es utilizar la función noLoop() dentro del bloque setup(). void setup() { size(100, 100); smooth(); fill(0); noLoop(); } void draw() { ellipse(50, 50, 66, 66); }

160 Ámbito de las variables int d = 51; // d variable global void setup() { size(100, 100); int val = d * 2;// val variable local en setup() fill(val); } void draw() { int y = 60; // y variable local en draw() line(0, y, d, y); y -= 25; line(0, y, d, y); }

161 Lectura recomendada Capítulo “Structure 2: Continuous” (pag. 173).

162 Parte 11 Interactividad: ratón y teclado

163 Ratón: variables de sistema El mouse no es más que un indicador XY de posición de pantalla. Las variables de sistema más usadas: mouseX mouseY pmouseX pmouseY mousePressed mouseButton

164 Ejemplo de variables de sistema mouseX y mouseY 1/6 void draw() { frameRate(12); println(mouseX + " : " + mouseY); }

165 Ejemplo de variables de sistema mouseX y mouseY 2/6 void setup() { size(200, 200); smooth(); noStroke(); } void draw() { background(126); ellipseMode(CENTER); ellipse(mouseX, mouseY, 33, 33); }

166 Ejemplo de variables de sistema mouseX y mouseY 3/6 void setup() { size(200, 200); smooth(); noStroke(); } void draw() { background(126); ellipse(mouseX, 16, 33, 33); ellipse(mouseX + 20, 50, 33, 33); ellipse(mouseX - 20, 84, 33, 33); }

167 Ejemplo de variables de sistema mouseX y mouseY 4/6 void setup() { size(200, 200); smooth(); noStroke(); } void draw() { background(126); ellipse(mouseX, 16, 33, 33); ellipse(mouseX / 2, 50, 33, 33); ellipse(mouseX * 2, 84, 33, 33); }

168 Ejemplo de variables de sistema mouseX y mouseY 5/6 void setup() { size(200, 200); smooth(); noStroke(); } void draw() { float x = mouseX; float y = mouseY; float ix = width - mouseX; // Inverso de X float iy = mouseY - height; // Inverso de Y background(126); fill(255, 150); ellipse(x, height/2, y, y); fill(0, 159); ellipse(ix, height/2, iy, iy); }

169 Ejemplo de variables de sistema mouseX y mouseY 6/6 void setup() { size(200, 200); smooth(); noStroke(); } void draw() { background(126); float normX = mouseX / float(width); ellipse(mouseX, 16, 33, 33); ellipse(pow(normX, 4) * width, 50, 33, 33); ellipse(pow(normX, 8) * width, 84, 33, 33); }

170 Ejemplo de variables de sistema pmouseX y pmouseY 1/2 void draw() { frameRate(12); println(pmouseX - mouseX); }

171 Ejemplo de variables de sistema pmouseX y pmouseY 2/2 void setup() { size(100, 100); strokeWeight(8); smooth(); } void draw() { background(204); line(mouseX, mouseY, pmouseX, pmouseY); }

172 Ejemplo de if y variable de sistema mousePressed y otros void setup() { size(640, 200); background(102); } void draw() { stroke(255); if(mousePressed == true) { line(mouseX, mouseY, pmouseX, pmouseY); }

173 Ejemplo de estructura if y variable de sistema mousePressed // Pinta el relleno a negro // cuando se presiona el botón del ratón. void draw() { if (mousePressed == true) { fill(0); } else { fill(255); } rect(25, 25, 50, 50); }

174 Ejemplo de estructura if y variable de sistema mouseButton 1/2 // Pinta el relleno a negro si presiono el botón izquierdo, // a blanco si es el derecho y a gris si es el medio. // mouseButton acepta las constantes: LEFT, RIGHT y CENTER. void setup() { size(100, 100); } void draw() { if (mouseButton == LEFT) { fill(0); // Negro } else if (mouseButton == RIGHT) { fill(255); // Blanco } else { fill(126); // Gris } rect(25, 25, 50, 50); }

175 Ejemplo de estructura if y variable de sistema mouseButton 2/2 // Versión alternativa del ejemplo anterior. void setup() { size(100, 100); } void draw() { if (mousePressed == true) { if (mouseButton == LEFT) { fill(0); // Negro } else if (mouseButton == RIGHT) { fill(255); // Blanco } else { fill(126); // Gris } rect(25, 25, 50, 50); }

176 Teclado: variables de sistema Las variables de sistema más usadas: keyPressed key keyCode

177 Ejemplo de variable de sistema keyPressed 1/2 // Dibuja un rectángulo mientras se // mantiene presionada una tecla. void setup() { size(100, 100); smooth(); strokeWeight(4); } void draw() { background(204); if (keyPressed == true) { rect(40, 40, 20, 20); } else { line(20, 20, 80, 80); }

178 Ejemplo de variable de sistema keyPressed 2/2 // Mueve una línea si se // mantiene presionada una tecla. int x = 20; void setup() { size(100, 100); smooth(); strokeWeight(4); } void draw() { background(204); if (keyPressed == true) { x++; } line(x, 20, x-60, 80); }

179 Ejemplo de variables de sistema keyPressed y key // Dibuja una línea si se mantiene // presionada la tecla 'a' o 'A'. void setup() { size(100, 100); smooth(); strokeWeight(4); } void draw() { background(204); if ((keyPressed == true) && ((key == 'a') || (key == 'A'))) { line(50, 25, 50, 75); } else { ellipse(50, 50, 50, 50); }

180 Ejemplo de variables de sistema keyPressed, key y keyCode // Uso de variable de sistema keyCode. // keyCode acepta como constantes: // las teclas de dirección UP, DOWN, LEFT, RIGHT, // y ALT, CONTROL y SHIFT. int y = 35; void setup() { size(100, 100); } void draw() { background(204); line(10, 50, 90, 50); if (key == CODED) { if (keyCode == UP) { y = 20; } else if (keyCode == DOWN) { y = 50; } else { y = 35; } rect(25, y, 50, 30); }

181 Ratón: Funciones de evento Las funciones de evento más usadas: mousePressed()  El código dentro de esta función se ejecuta una vez cuando se presiona un botón de ratón. mouseReleased()  El código dentro de esta función se ejecuta una vez cuando se libera un botón de ratón. mouseMoved()  El código dentro de esta función se ejecuta una vez cuando se mueve un ratón. mouseDragged()  El código dentro de esta función se ejecuta una vez cuando se mueve un ratón mientras se encuentra presionado un botón de ratón.

182 Ejemplo de if...else y función de evento mouseReleased int valor = 0; void draw() { fill(valor); rect(25, 25, 50, 50); } void mouseReleased() { if(valor == 0) { valor = 255; } else { valor = 0; }

183 Teclado: Funciones de evento Las funciones de evento más usadas: keyPressed()  El código dentro de esta función se ejecuta una vez cuando se presiona cualquier tecla. keyReleased()  El código dentro de esta función se ejecuta una vez cuando se libera cualquier tecla.

184 Ejemplo de función de evento keyPressed() y keyReleased() boolean drawT = false; void setup() { size(100, 100); noStroke(); } void draw() { background(204); if (drawT == true) { rect(20, 20, 60, 20); rect(39, 40, 22, 45); } void keyPressed() { if ((key == 'T') || (key == 't')) { drawT = true; } void keyReleased() { drawT = false; }

185 Ejemplo de función de evento mousePressed() y keyPressed() void setup() { size(200, 200); background(255); fill(0, 102); } void draw() { } void mousePressed() { rectMode(CENTER); rect(mouseX, mouseY, 32, 32); } void keyPressed() { background(255); }

186 Lectura recomendada Capítulo “Input 1: Mouse I” (pag. 205). Capítulo “Drawing 1: Static Forms” (pag. 217). Capítulo “Input 2: Keyboard” (pag. 223). Capítulo “Input 3: Events” (pag. 229).

187 Información complementaria

188 Ejemplo de variables de sistema mouseX y mouseY y estructura if 1/4 // La posición del cursor pinta la mitad izquierda // o derecha de la ventana de visualización. void setup() { size(100, 100); noStroke(); fill(0); } void draw() { background(204); if (mouseX < 50) { rect(0, 0, 50, 100); // Izquierda } else { rect(50, 0, 50, 100); // Derecha }

189 Ejemplo de variables de sistema mouseX y mouseY y estructura if 2/4 // La posición del cursor pinta el tercio izquierdo // central o derecho de la ventana de visualización. void setup() { size(100, 100); noStroke(); fill(0); } void draw() { background(204); if (mouseX < 33) { rect(0, 0, 33, 100); // Izquierda } else if ((mouseX >= 33) && (mouseX <= 66)) { rect(33, 0, 33, 100); // Centro } else { rect(66, 0, 33, 100); // Derecha }

190 Ejemplo de variables de sistema mouseX y mouseY y estructura if 3/4 // La posición del cursor pinta un cuadrante // de la ventana de visualización. void setup() { size(100, 100); noStroke(); fill(0); } void draw() { background(204); if ((mouseX <= 50) && (mouseY <= 50)) { rect(0, 0, 50, 50); // Superior-izquierdo } else if ((mouseX 50)) { rect(0, 50, 50, 50); // Inferior-izquierdo } else if ((mouseX > 50) && (mouseY < 50)) { rect(50, 0, 50, 50); // Superior-derecho } else { rect(50, 50, 50, 50); // Inferior-derecho }

191 Ejemplo de if...else… if, variables de sistema mouseX, mouseY y operadores lógicos void setup() { size(200, 200); } void draw() { background(255); stroke(0); line(100, 0, 100, 200); line(0, 100, 200, 100); // Relleno de color negro noStroke(); fill(0); if (mouseX < 100 && mouseY < 100) { rect(0, 0, 100, 100); } else if (mouseX > 100 && mouseY < 100) { rect(100, 0, 100, 100); } else if (mouseX 100) { rect(0, 100, 100, 100); } else if (mouseX > 100 && mouseY > 100) { rect(100, 100, 100, 100); }

192 Ejemplo de variables de sistema mouseX y mouseY y estructura if 4/4 // La posición del cursor cambia el color // de relleno de un área rectangular. void setup() { size(100, 100); noStroke(); fill(0); } void draw() { background(204); if ((mouseX > 40) && (mouseX 20) && (mouseY < 80)) { fill(255); } else { fill(0); } rect(40, 20, 40, 60); }

193 Ejercicio 12 EJ12: Animar tres círculos de acuerdo a los datos ingresados mediante el ratón. Comentar todas las instrucciones.

194 Parte 12 Funciones de usuario

195 Introducción 1/2 En Processing cualquier usuario puede programar sus propias funciones. Llamamos a esto función de usuario. Una función es un módulo de programación autocontenido. Las funciones de usuario hacen más conciso el código redundante al extraer los elementos comunes e incluirlos en bloques de código para que puedan ejecutarse tantas veces se quiera dentro del programa. Esto permite una lectura más fácil del código y reduce las probabilidades de error al actualizar el código. Las funciones generalmente tienen parámetros que definen sus acciones. Las funciones pueden operar de forma diferente dependiendo del número de parámetros usados. Una función puede ser imaginada como una caja con mecanismos dentro que actúan sobre los datos ingresados y devuelven un resultado.

196 Introducción 2/2 Convencionalmente posee una o varias entradas, un bloque de código que procesa dichas entradas, y finalmente una salida. Algunos ejemplos de diagramas de función de usuario:

197 Abstracción 1/2 En terminología de software se llama abstracción al proceso que permite esconder los detalles de realización y concentrarnos en el resultado. En realidad todas las funciones de sistema que hemos visto hasta el momento son, técnicamente, abstracciones: los autores han escondido los detalles de implementación para que el programador se concentre en los resultados. Cuando construimos funciones estas podrán devolver un resultado o no. Depende evidentemente de qué querramos hacer con ella. Pero en el caso de optar por la no devolución de un resultado deberemos comenzar por construir el bloque con la palabra clave void. Es por esto que todos los ejemplos que veremos a continuación comienzan a construirse con dicha palabra clave.

198 Introducción a las funciones 1/12 // Programación basada en primitivas // usando funciones personalizadas o funciones definidas por el usuario // Autor: Ariel Malka | www.chronotext.org // URL: http://processing.org/discourse/yabb/YaBB.cgi?board=TheoryandPractice;action=display;num=10 78263461 // Traducción: Raúl Lacabanne - 2009 void setup() { // esta función será llamada automáticamente por Processing cuando el programa se ejecute size(200, 200); // configura el tamaño de pantalla } void draw() { // esta función también será llamada automáticamente luego de setup() rect(100, 30, 90, 160); // crea un rectángulo } // El "flujo de ejecución" será el siguiente: // 1) setup() // 2) size(200, 200) // 3) draw() // 4) rect(10, 10, 90, 160);

199 Introducción a las funciones 2/12 // Programación basada en primitivas // usando funciones personalizadas o funciones definidas por el usuario // Autor: Ariel Malka | www.chronotext.org // URL: http://processing.org/discourse/yabb/YaBB.cgi?board=TheoryandPractice;action=display;num=1078263461 // Traducción: Raúl Lacabanne - 2009 void setup() { size(200, 200); background(255); // establece el color de fondo en blanco } void draw() { // cuatro llamadas a la función definida por el usuario cross() // el "origen del sistema de coordenadas" por defecto se encuentra en la esquina superior izquierda de la pantalla cruz(); // esquina superior izquierda de la cruz en 0,0 translate(50, 50); // el "origen del sistema de coordenadas" se mueve 50 px a la derecha y 50 px abajo cruz(); // esquina superior izquierda de la cruz en 50,50 translate(50, 50); // el "origen del sistema de coordenadas" se mueve otros 50 px a la derecha y 50 px abajo cruz(); // esquina superior izquierda de la cruz en 100,100 translate(50, 50); // el "origen del sistema de coordenadas" se mueve otros 50 px a la derecha y 50 px abajo cruz(); // esquina superior izquierda de la cruz en 150,150 } void cruz() { // nuestra función definida por el usuario (podemos nombrarla como querramos) noStroke(); fill(255, 0, 0); // rojo rect(0, 10, 30, 10); rect(10, 0, 10, 30); }

200 Introducción a las funciones 3/12 // Programación basada en primitivas // usando funciones personalizadas o funciones definidas por el usuario // Autor: Ariel Malka | www.chronotext.org // URL: http://processing.org/discourse/yabb/YaBB.cgi?board=TheoryandPractice;action=display;num=1078263461 // Traducción: Raúl Lacabanne - 2009 // cuando dibujamos usando funciones es importante poder tener la posibilidad de dibujar una forma desde el centro... void setup() { size(200, 200); background(255); } void draw() { cruz(); // esquina superior izquierda de la cruz en 0,0 translate(50, 50); // el "origen del sistema de coordenadas" se mueve 50 px a la derecha y 50 px abajo cruz(); // esquina superior izquierda de la cruz en 50,50 translate(50, 50); // el "origen del sistema de coordenadas" se mueve otros 50 px a la derecha y 50 px abajo cruz(); // esquina superior izquierda de la cruz en 100,100 translate(50, 50); // el "origen del sistema de coordenadas" se mueve otros 50 px a la derecha y 50 px abajo cruz(); // esquina superior izquierda de la cruz en 150,150 } void cruz() { noStroke(); fill(255, 0, 0); rectMode(CENTER); // los rectángulos serán dibujados desde el centro rect(0, 0, 30, 10); rect(0, 0, 10, 30); }

201 Introducción a las funciones 4/12 // Programación basada en primitivas // usando funciones personalizadas o funciones definidas por el usuario // Autor: Ariel Malka | www.chronotext.org // URL: http://processing.org/discourse/yabb/YaBB.cgi?board=TheoryandPractice;action=display;num=1078263461 // Traducción: Raúl Lacabanne - 2009 // cuando creamos funciones personalizadas es posible agregar un número arbitrario de "parámetros" // que actuarán como variables dentro del bloque de la función void setup() { size(200, 200); background(255); } void draw() { // cuando llamamos a nuestra función, estamos "pasando" 2 parámetros que // afectarán la posición de la cruz cruz(0, 0); // el centro de la cruz se encuentra en 0,0 cruz(50, 50); // el centro de la cruz se encuentra en 50,50 cruz(100, 100); // el centro de la cruz se encuentra en 100,100 cruz(150, 150); // el centro de la cruz se encuentra en 150,150 } void cruz(float ejeX, float ejeY) { // estamos usando dos parámetros (los nombramos como querramos) noStroke(); fill(255, 0, 0); rectMode(CENTER); // ejeX y ejeY están actuando como variables y han sido declaradas por fuera del bloque de la función rect(ejeX, ejeY, 30, 10); rect(ejeX, ejeY, 10, 30); }

202 Introducción a las funciones 5/12 // Programación basada en primitivas // usando funciones personalizadas o funciones definidas por el usuario // Autor: Ariel Malka | www.chronotext.org // URL: http://processing.org/discourse/yabb/YaBB.cgi?board=TheoryandPractice;action=display;num=1078263461 // Traducción: Raúl Lacabanne - 2009 // introducción de parámetros adicionales void setup() { size(200, 200); background(255); } void draw() { // definición de variables que contienen información de color color rojo = color(255, 0, 0); color azul = color(51, 153, 255); color gris = color(128, 128, 128); color verde = color(153, 255, 51); // estamos pasando un tercer parámetro que afectará el tamaño de la cruz // y un cuarto parámetro que permitirá en control de color de la cruz cruz(0, 0, 1, rojo); cruz(50, 50, 3, azul); cruz(100, 100, 0.5, gris); cruz(150, 150, 5.5, verde); } void cruz(float x, float y, float tamanio, color colorCruz) { // 2 nuevos parámetros han sido agregados a la función noStroke(); fill(colorCruz); // esto controla el color de la cruz rectMode(CENTER); rect(x, y, 30 * tamanio, 10 * tamanio); rect(x, y, 10 * tamanio, 30 * tamanio); }

203 Introducción a las funciones 6/12 // Programación basada en primitivas // usando funciones personalizadas o funciones definidas por el usuario // Autor: Ariel Malka | www.chronotext.org // URL: http://processing.org/discourse/yabb/YaBB.cgi?board=TheoryandPractice;action=display;num=1078263461 // Traducción: Raúl Lacabanne - 2009 // ¡cuando se programa es posible alcanzar los mismos resultados utilizando diferentes acercamientos! void setup() { size(200, 200); background(255); } void draw() { fill(255, 0, 0); // rojo cruz(0, 0, 1); fill(51, 153, 255); // azul cruz(50, 50, 3); fill(128, 128, 128); // gris cruz(100, 100, 0.5); fill(153, 255, 51); // verde cruz(150, 150, 5.5); } void cruz(float x, float y, float tamanio) { noStroke(); rectMode(CENTER); rect(x, y, 30 * tamanio, 10 * tamanio); rect(x, y, 10 * tamanio, 30 * tamanio); }

204 Introducción a las funciones 7/12 // Programación basada en primitivas // usando funciones personalizadas o funciones definidas por el usuario // Autor: Ariel Malka | www.chronotext.org // URL: http://processing.org/discourse/yabb/YaBB.cgi?board=TheoryandPractice;action=display;num=1078263461 // Traducción: Raúl Lacabanne - 2009 // usar funciones que usan otras funciones... // primero hagamos dos figuras diferentes y examinémoslas por separado void setup() { size(200, 200); background(255); fill(0, 0, 0); // todas las figuras con relleno negro } void draw() { bubbles(50, 100); tube(150, 100); } void tube(float x, float y) { noStroke(); rectMode(CENTER); ellipseMode(RADIUS); rect(x, y, 40, 100); rect(x, y - 50, 60, 10); ellipse(x, y + 50, 20, 20); } void bubbles(float x, float y) { noStroke(); ellipseMode(RADIUS); ellipse(x + 4, y - 24, 10, 10); ellipse(x - 4, y, 9, 9); ellipse(x + 4, y + 24, 8, 8); ellipse(x - 4, y + 48, 7, 7); }

205 Introducción a las funciones 8/12 // Programación basada en primitivas // usando funciones personalizadas o funciones definidas por el usuario // Autor: Ariel Malka | www.chronotext.org // URL: http://processing.org/discourse/yabb/YaBB.cgi?board =TheoryandPractice;action=display;num=1078263461 // Traducción: Raúl Lacabanne - 2009 // usar funciones que usan otras funciones... // ahora hagamos una función que usa juntas y de manera compuesta nuestras dos funciones anteriores void setup() { size(200, 200); background(255); } void draw() { peligro(50, 100); peligro(75, 80); peligro(100, 100); peligro(125, 120); peligro(150, 100); } void peligro(float x, float y) { fill(0, 0, 0); // negro tubo(x, y); fill(255, 255, 255); // blanco burbujas(x, y); } void tubo(float x, float y) { noStroke(); rectMode(DIAMETER); ellipseMode(RADIUS); rect(x, y, 40, 100); rect(x, y - 50, 60, 10); ellipse(x, y + 50, 20, 20); } void burbujas(float x, float y) { noStroke(); ellipseMode(RADIUS); ellipse(x + 4, y - 24, 10, 10); ellipse(x - 4, y, 9, 9); ellipse(x + 4, y + 24, 8, 8); ellipse(x - 4, y + 48, 7, 7); }

206 Introducción a las funciones 9/12 // Programación basada en primitivas // usando funciones personalizadas o funciones definidas por el usuario // Autor: Ariel Malka | www.chronotext.org // URL: http://processing.org/discourse/yabb/YaBB.cgi?board =TheoryandPractice;action=display;num=1078263461 // Traducción: Raúl Lacabanne - 2009 // usar funciones que usan otras funciones... // generar una función que utiliza nuestras 2 piezas compuestas juntas // más la introducción de escalado (usando un parámetro adicional) void setup() { size(200, 200); background(255); } void draw() { peligro(50, 100, 13); // más grande peligro(150, 100, 5); // más pequeño } void peligro(float x, float y, float tamanio) { fill(0, 0, 0); // negro tubo(x, y, tamanio); fill(255, 255, 255); // blanco burbujas(x, y, tamanio); } void tubo(float x, float y, float tamanio) { noStroke(); rectMode(DIAMETER); ellipseMode(RADIUS); rect(x, y, 4 * tamanio, 10 * tamanio); rect(x, y - 5 * tamanio, 6 * tamanio, 1 * tamanio); ellipse(x, y + 5 * tamanio, 2 * tamanio, 2 * tamanio); } void burbujas(float x, float y, float tamanio) { noStroke(); ellipseMode(RADIUS); ellipse(x + 0.4 * tamanio, y - 2.4 * tamanio, 1 * tamanio, 1 * tamanio); ellipse(x - 0.4 * tamanio, y, 0.9 * tamanio, 0.9 * tamanio); ellipse(x + 0.4 * tamanio, y + 2.4 * tamanio, 0.8 * tamanio, 0.8 * tamanio); ellipse(x - 0.4 * tamanio, y + 4.8 * tamanio, 0.7 * tamanio, 0.7 * tamanio); }

207 Introducción a las funciones 10/12 // Programación basada en primitivas // usando funciones personalizadas // Autor: Ariel Malka | www.chronotext.org // URL: http://processing.org/discourse/yabb/YaBB.cgi?board =TheoryandPractice;action=display;num=1078263461 // Traducción: Raúl Lacabanne - 2009 // usar funciones que usan otras funciones... // generar una función que utiliza nuestras 2 piezas compuestas juntas // más la introducción de escalado basado en una matriz de transformación void setup() { size(200, 200); background(255); } void draw() { peligro(50, 100, 1.3); peligro(150, 100, 0.5); } void peligro(float x, float y, float sz) { fill(0, 0, 0); // negro pushMatrix(); translate(x, y); scale(sz); tubo(); popMatrix(); fill(255, 255, 255); // blanco pushMatrix(); translate(x, y); scale(sz); burbujas(); popMatrix(); } void tubo() { noStroke(); rectMode(DIAMETER); ellipseMode(RADIUS); rect(0, 0, 40, 100); rect(0, - 50, 60, 10); ellipse(0, 50, 20, 20); } void burbujas() { noStroke(); ellipseMode(RADIUS); ellipse(4, - 24, 10, 10); ellipse(-4, 0, 9, 9); ellipse(4, 24, 8, 8); ellipse(-4, 48, 7, 7); }

208 Introducción a las funciones 11/12 // Programación basada en primitivas // usando funciones personalizadas o funciones definidas por el usuario // Autor: Ariel Malka | www.chronotext.org // URL: http://processing.org/discourse/yabb/YaBB.cgi?board =TheoryandPractice;action=display;num=1078263461 // Traducción: Raúl Lacabanne - 2009 // usar funciones que usan otras funciones... // generar una función que utiliza nuestras 2 piezas compuestas juntas // más la introducción de escalado basado en una matriz de transformación void setup() { size(200, 200); background(255); } void draw() { peligro(50, 100, 1.3); peligro(150, 100, 0.5); } void peligro(float x, float y, float tamanio) { fill(255, 0, 0); // rojo pushMatrix(); translate(x, y); scale(tamanio); tubo(); fill(255, 255, 255); // blanco burbujas(); popMatrix(); } void tubo() { noStroke(); rectMode(DIAMETER); ellipseMode(RADIUS); rect(0, 0, 40, 100); rect(0, - 50, 60, 10); ellipse(0, 50, 20, 20); } void burbujas() { noStroke(); ellipseMode(RADIUS); ellipse(4, - 24, 10, 10); ellipse(-4, 0, 9, 9); ellipse(4, 24, 8, 8); ellipse(-4, 48, 7, 7); }

209 Introducción a las funciones 12/12 void setup() { size(640, 360); background(102); smooth(); } void draw() { elipseVariable(mouseX, mouseY, pmouseX, pmouseY); } // elipseVariable() calcula la velocidad del ratón. // Si el ratón se mueve lentamente: dibuja una elipse pequeña, // si el ratón se mueve rápidamente: dibuja una elipse mayor si void elipseVariable(int x, int y, int px, int py) { float speed = abs(x-px) + abs(y-py); stroke(speed); ellipse(x, y, speed, speed); }

210 Valor de retorno 1/2 En todos los ejemplos vistos hasta ahora, hemos visto que la salida, por ejemplo, de una funcion de primitivas ha sido en forma de dibujo en el área de representación. Sin embargo a veces preferiremos que la salida sea un número u otro tipo de dato. La salida de una función se llama valor de retorno. Se espera que todas las funciones regresen un valor, tal como un entero o un decimal. Si la función no regresa un valor, se utiliza la palabra especial void. El tipo de dato regresado por una función se encuentra a la izquierda del nombre de función. El comando clave return es usado para salir de una función y regresar al lugar desde el cual fue llamado. Cuando una función regresa un valor, return es usado para especificar qué valor debe ser regresado. La instrucción que incluye return es típicamente la última de una función, ya que la misma finaliza inmediatamente después de un retorno. Ya hemos usado funciones que devuelven valores: por ej.: random() regresa un decimal, color() regresa un tipo de dato de color, etc. Si una función regresa un valor, dicha función casi siempre aparece a la derecha de un operador de asignación o como parte de una expresión mayor. Una función que no regresa un valor es frecuentemente usada como una instrucción completa.

211 Valor de retorno 2/2 Las funciones no están limitadas a regresar números: pueden regresar boolean, String, PImage o cualquier otro tipo de dato. Para escribir una función de usuario que regrese un valor, reemplace void con el tipo de dato que necesite regresar, e incluya dentro de la función la palabra clave return seguido de la variable que contenga el valor que desee regresar para habilitar la salida del mismo. A continuación veremos un ejemplo: void setup() { size(100, 100); float f = promedio(12.0, 6.0); // Asigna 9.0 a f println(f); } float promedio(float num1, float num2) { float av = (num1 + num2) / 2.0; return av; }

212 Sobrecarga de funciones (Function overloading) 1/2 Se llama sobrecarga de funciones al procedimiento de crear diferentes versiones de una misma función. Las distintas versiones pueden compartir el mismo nombre de función siempre y cuando tengan diferentes números de parámetros o tipos de datos de los mismos. Es decir, un programa puede tener dos funciones con el mismo número de parámetros, pero sólo si el tipo de dato de uno de sus parámetros es diferente. Processing identifica qué versión de función debe ejecutar al comparar el número y el tipo de dato de sus parámetros. Veamos el próximo ejemplo:

213 Sobrecarga de funciones (Function overloading) 2/2 void setup() { size(100, 100); smooth(); } void draw() { dibujoX(255); // Ejecuta primer dibujoX() dibujoX(5.5); // Ejecuta segundo dibujoX() dibujoX(0, 2, 44, 48, 36); // Ejecuta tercer dibujoX() } // dibujoX con el valor de gris determinado por el parámetro void dibujoX(int gris) { stroke(gris); strokeWeight(20); line(0, 5, 60, 65); line(60, 5, 0, 65); } // dibujoX negro con el valor ancho de contorno determinado por el parámetro void dibujoX(float ancho) { stroke(0); strokeWeight(ancho); line(0, 5, 60, 65); line(60, 5, 0, 65); } // dibujoX con la posición, el valor de gris, tamaño y el ancho de // contorno determinados por sus correspondientes parámetros void dibujoX(int gris, int ancho, int x, int y, int s) { stroke(gris); strokeWeight(ancho); line(x, y, x+s, y+s); line(x+s, y, x, y+s); }

214 Lectura recomendada Capítulo “Structure 3: Functions” (pag. 181).

215 Ejercicio 13 EJ13: Crear una forma animada autónoma que comunique la idea de "orden". Utilizar funciones de usuario. Comentar todas las instrucciones.

216 Ejercicio 14 EJ14: Crear una forma animada autónoma que comunique la idea de "caos". Utilizar funciones de usuario. Comentar todas las instrucciones.

217 Ejercicio 15 EJ15: Crear una forma interactiva que comunique la idea de "orden y caos". Utilizar funciones de usuario. Comentar todas las instrucciones.

218 Parte 13 Otras funciones matemáticas

219 Función sq() – Cuadrado Para calcular el cuadrado de un número usamos la función sq(). La misma nos regresa el resultado, el cual será siempre un número positivo aunque usemos un valor negativo. sq(valor) float x = sq(1); // Asigna 1.0 a x: equivalente a 1 * 1 float y = sq(-5); // Asigna 25.0 a y: equivalente a -5 * -5 float z = sq(9); // Asigna 81.0 a z: equivalente a 9 * 9

220 Función sqrt() – Raíz cuadrada La función sqrt() es usada para calcular la raíz cuadrada de un número. La misma regresa el resultado. sqrt(valor) Recordemos que también lo podemos expresar de la siguiente manera: ( √a ) == (a 1/2 ) float r = sqrt(6561); // Asigna 81.0 a r float s = sqrt(625); // Asigna 25.0 a s float t = sqrt(1); // Asigna 1.0 a t

221 Función pow() – Potenciación La función pow() calcula la potencia en función de dos términos: base y exponente. La misma regresa el resultado. pow(base, exponente) Recordemos que, cuando el exponente es una fracción irreducible, también lo podemos expresar de la siguiente manera: ( m √a n ) == (a n/m ) float d = pow(1, 3); // Asigna 1.0 a d: equivalente a 1*1*1 float e = pow(3, 4); // Asigna 81.0 a e: equivalente a 3*3*3*3 float f = pow(3, -2); // Asigna 0.11 a f: equivalente a 1 / (3*3) float g = pow(-3, 3); // Asigna -27.0 a g: equivalente a -3*-3*-3

222 Función norm() – Normalización 1/2 Muchas veces se vuelve conveniente convertir un rango de números dados al rango 0.0 a 1.0. A este procedimiento se lo llama normalización. Cuando multiplicamos números entre 0.0 y 1.0, el resultado nunca será menor a 0.0 ni mayor a 1.0. Esto permite no salir de un rango determinado. Desde luego que todas las operaciones de normalización deben ser realizadas con el tipo de dato float. Para normalizar un número debemos dividirlo por el valor máximo que este represente. Por ejemplo: para normalizar una serie de valores entre 0.0 y 255.0, divida cada uno por 255.0: Valor inicialCálculoValor normalizado 0.00.0 / 255.00.0 102.0102.0 / 255.00.4 255.0255.0 / 255.01.0

223 Función norm() – Normalización 2/2 Para simplificar esta tarea podemos utilizar la función norm(): norm(valor_a_convertir, valor_mínimo, valor_máximo) Si el valor a convertir se encuentra fuera del rango, el resultado podrá ser menor a 0.0 o mayor a 1.0 de acuerdo al caso. float x = norm(0.0, 0.0, 255.0); // Asigna 0.0 a x float y = norm(102.0, 0.0, 255.0); // Asigna 0.4 a y float z = norm(255.0, 0.0, 255.0); // Asigna 1.0 a z

224 Función lerp() – Interpolación lineal 1/2 Luego de la normalización, podemos convertir el número a otro rango mediante operaciones aritméticas. Por ejemplo, para convertir desde un rango entre 0.0 y 1.0 al rango entre 0.0 y 500.0, simplemente los multiplicamos por 500.0. Para convertir números entre 0.0 y 1.0 al rango que se extiende entre 200.0 y 500.0, multiplicamos por 300 y luego sumamos 200. Veamos a continuación algunos ejemplos de conversión: Rango inicial de xRango de destino de xConversión 0.0 a 1.00.0 a 255.0x * 255.0 0.0 a 1.0-1.0 a 1.0(x * 2.0) - 1.0 0.0 a 1.0-20.0 a 60.0(x * 80.0) - 20.0

225 Función lerp() – Interpolación lineal 2/2 Nuevamente, para simplificar esta tarea podemos utilizar la función lerp(). La misma presenta tres parámetros lerp(valor_mínimo_del_rango_a_interpolar, valor_máximo_del_rango_a_interpolar, valor_a_interpolar) El tercer parámetro (valor a interpolar) debe ser siempre un valor entre 0.0 y 1.0. Veamos algunos ejemplos: float r = lerp(-20.0, 60.0, 0.0); // Asigna -20.0 a r float s = lerp(-20.0, 60.0, 0.5); // Asigna 20.0 a s float t = lerp(-20.0, 60.0, 1.0); // Asigna 60.0 a t

226 Función map() – Mapeo 1/2 Existe otra función que nos permite aplicar las operaciones de normalización e interpolación lineal en una: hablamos de la función map(). Con dicha función podemos convertir directamente un valor correspondiente a un rango de números a otro correspondiente a otro rango de números. Posee cinco parámetros: map(valor, mínimo1, máximo1, mínimo2, máximo2) Donde valor corresponde al número de origen a mapear, mínimo1 y máximo1 a los números del rango origen, y mínimo2 y máximo2 a los números del rango de destino.

227 Función map() – Mapeo 2/2 El próximo ejemplo muestra el uso de map() para convertir valores del rango de origen 0 a 255 al rango de destino -1 a 1. Este proceso equivale a, primero, normalizar el valor, y luego a multiplicar y sumar para desplazar el rango de 0 a 1 al rango -1 a 1: float x = map(20.0, 0.0, 255.0, -1.0, 1.0); // Asigna -0.84 a x float y = map(0.0, 0.0, 255.0, -1.0, 1.0); // Asigna -1.0 a y float z = map(255.0, 0.0, 255.0, -1.0, 1.0); // Asigna 1.0 a z

228 Función constrain() – Limitación de rangos 1/2 La función constrain() permite limitar un número a un rango determinado. Trabaja con enteros o decimales. Posee tres parámetros: constrain(valor, mínimo, máximo) Donde valor corresponde al número a limitar, mínimo al valor mínimo posible y máximo al valor máximo posible. Esta función regresa el número mínimo si el parámetro valor es menor o equivalente al antedicho, regresa el número máximo si el mismo es mayor o equivalente, y regresa valor si se encuentra en el rango previsto. int x = constrain(35, 15, 90); // Asigna 35 a x int y = constrain(10, 15, 90); // Asigna 15 a y int z = constrain(91, 15, 90); // Asigna 90 a z

229 Función constrain() – Limitación de rangos 2/2 // Limitar la posición de una elipse a una región determinada void setup() { size(100, 100); smooth(); noStroke(); } void draw() { background(0); // Limita mx entre 35 y 65 float mx = constrain(mouseX, 35, 65); // Limita my entre 40 y 60 float my = constrain(mouseY, 40, 60); fill(102); rect(20, 25, 60, 50); fill(255); ellipse(mx, my, 30, 30); }

230 Función dist() – Distancia entre coordenadas 1/3 La función dist() calcula la distancia entre dos coordenadas. Trabaja con enteros o decimales, pero regresa decimales. Posee cuatro parámetros: dist(x1, y1, x2, y2) El primer par de parámetros corresponde a la primera coordenada y el segundo par a la segunda. float r = dist(0, 0, 50, 0); // Asigna 50.0 a r float s = dist(50, 0, 50, 90); // Asigna 90.0 a s float t = dist(30, 20, 80, 90); // Asigna 86.023254 a t

231 Función dist() – Distancia entre coordenadas 2/3 // La distancia entre el centro de la ventana de representación // y el puntero determina el díametro del círculo void setup() { size(100, 100); smooth(); } void draw() { background(0); float d = dist(width/2, height/2, mouseX, mouseY); ellipse(width/2, height/2, d*2, d*2); }

232 Función dist() – Distancia entre coordenadas 3/3 // Dibujo de una grilla de círculos y cálculo de la // distancia de cada uno de ellos para determinar el tamaño float distanciaMax; void setup() { size(100, 100); noStroke(); smooth(); fill(0); distanciaMax = dist(0, 0, width, height); } void draw() { background(204); for (int i = 0; i <= width; i += 20) { for (int j = 0; j <= height; j += 20) { float distanciaMouse = dist(mouseX, mouseY, i, j); float diametro = (distanciaMouse / distanciaMax) * 66.0; ellipse(i, j, diametro, diametro); }

233 Técnica Easing – Aligeramiento 1/7 La técnica de animación llamada Easing, es en realidad una técnica de interpolación entre dos puntos. Al mover en cada cuadro una fracción de la distancia total de una figura, el movimiento de esta parece desacelerarse (o acelerarse) al acercarse a la ubicación de destino. El siguiente diagrama muestra qué ocurre cuando un punto siempre se mueve la mitad del recorrido entre su posición actual y la posición de destino: A medida que la figura se acerca a la posición de destino, la distancia recorrida disminuye en cada fotograma, por lo tanto el movimiento de la misma parece ralentizarse.

234 Técnica Easing – Aligeramiento 2/7 En el siguiente ejemplo la variable x corresponde a la posición horizontal actual del círculo y la varible destinoX corresponde a la posición de destino. La variable easing dispone la fracción de la distancia entre la posición actual del círculo y la posición del ratón que el círculo se mueve en cada cuadro. El valor de esta variable cambia la rapidez con que el círculo llega al destino. El valor de easing debe estar siempre entre 0.0 y 1.0, y los números cercanos a 0.0 causan que el movimiento se ralentice más. Un valor de easing de 0.5 hará que el círculo se mueva la mitad de la distancia en cada cuadro, mientras que un valor de 0.01 hará que el círculo se mueva una centésima de la distancia en cada cuadro. El círculo superior es dibujado de acuerdo a la posición destinoX, mientras que el círculo inferior es dibujado de acuerdo a la posición interpolada. Ahora sí veamos el ejemplo:

235 Técnica Easing – Aligeramiento 3/7 float x = 0.0; // Distancia actual en x float easing = 0.05; // Números 0.0 a 1.0 void setup() { size(100, 100); smooth(); } void draw() { background(0); float destinoX = mouseX; x += (destinoX - x) * easing; ellipse(mouseX, 30, 40, 40); ellipse(x, 70, 40, 40); }

236 Técnica Easing – Aligeramiento 4/7 En el siguiente ejemplo utilizamos dos variables para controlar la técnica easing en las dos dimensiones. Observe que estructuralmente es igual al anterior. El ejemplo se encuentra en la siguiente página:

237 Técnica Easing – Aligeramiento 5/7 float x = 0; // Distancia actual en x float y = 0; // Distancia actual en y float easing = 0.05; // Números 0.0 a 1.0 void setup() { size(100, 100); smooth(); noStroke(); } void draw() { background(0); float destinoX = mouseX; float destinoY = mouseY; x += (destinoX - x) * easing; y += (destinoY - y) * easing; fill(255); ellipse(x, y, 40, 40); //Círculo blanco grande interpolado fill(153); ellipse(mouseX, mouseY, 20, 20); //Círculo gris pequeño destino }

238 Técnica Easing – Aligeramiento 6/7 Los dos ejemplo previos continúan realizando el cálculo para la posición del círculo incluso luego de haber alcanzado su destino. Desde el punto de vista informático resulta ineficiente, y si hubieran cientos de círculos todos aligerando las posiciones, esto ralentizaría el programa en general. Para detener los cálculos cuando estos no son necesarios, evalúe que la posición de destino y la posición actual sean equivalentes y detenga el cálculo si esta condición resulta verdadera. El siguiente ejemplo presenta el uso de la función abs() la cual devuelve el valor absoluto de un número. Esta es necesaria ya que los valores resultantes de la técnica Easing pueden ser tanto negativos como positivos dependiendo de si la posición se encuentra a la izquierda o a la derecha del destino.

239 Técnica Easing – Aligeramiento 7/7 float x = 0.0; // Distancia actual en x float easing = 0.05; // Números 0.0 a 1.0 void setup() { size(100, 100); smooth(); } void draw() { background(0); float destinoX = mouseX; // Distancia desde l posición hasta el destino float dx = destinoX - x; // Si la distancia entre la posición actual y el destino // es mayor a 1.0, actualizo la posición if (abs(dx) > 1.0) { x += dx * easing; println(dx); } ellipse(mouseX, 30, 40, 40); ellipse(x, 70, 40, 40); }

240 Cálculo de velocidad 1/3 A continuación calcularemos la velocidad del ratón mediante la comparación de la posición actual con la posición anterior. Esto lo haremos usando la función dist() con los valores de parámetros de las variables mouseX, mouseY, pmouseX y pmouseY. Entonces, el siguiente ejemplo calcula la velocidad del ratón y convierte este valor en el diámetro de una elipse: void setup() { size(100, 100); noStroke(); smooth(); } void draw() { background(0); float velocidad = dist(mouseX, mouseY, pmouseX, pmouseY); float diametro = velocidad * 3.0; ellipse(50, 50, diametro, diametro); }

241 Cálculo de velocidad 2/3 El ejemplo previo muestra la velocidad instanténea del ratón. Los números producidos son extremos –saltan constantemente entre cero y otros valores más grandes de un cuadro al otro. Se puede utilizar la técnica Easing para incrementar o mermar la velocidad de forma suavizada. El siguiente ejemplo muestra cómo aplicar la técnica Easing en dicho contexto. La barra superior representa la velocidad instantánea mientras que la inferior representa la velocidad aligerada:

242 Cálculo de velocidad 3/3 float velocidad = 0.0; float easing = 0.05; // Números 0.0 al 1.0 void setup() { size(400, 400); noStroke(); smooth(); } void draw() { background(0); float destino = dist(mouseX, mouseY, pmouseX, pmouseY); velocidad += (destino - velocidad) * easing; rect(0, 33, destino, 17); rect(0, 50, velocidad, 17); }

243 Orientación 1/4 La función atan2() es usada para calcular el ángulo desde cualquier punto del área de representación a la coordenada de origen (0, 0). Tiene dos parámetros: atan2(y, x) Donde los parámetro x e y corresponden a la coordenada de interés. Note que la posición de ambos parámetros se presentan en reverso comparado a cómo las usa otras funciones tales como point(). Los valores de ángulo son regresados en radianes dentro del rango π a –π. Recordemos que un circulo mide 2*π radianes, lo cual equivale en grados a 360º. Por lo tanto, un ángulo de 90º corresponde a π/2 (1.5707964). Para convertir una medida de radianes a grados utilizamos la función degrees(). Veamos un ejemplo donde aplicamos estas dos funciones:

244 Orientación 2/4 // El ángulo se incrementa a medida de que el ratón // se mueve desde la esquina superior-derecha de la // pantalla a la esquina inferior-izquierda void setup() { size(100, 100); frameRate(15); fill(0); } void draw() { float angulo = atan2(mouseY, mouseX); float grados = degrees(angulo); println(grados + "º"); background(204); ellipse(mouseX, mouseY, 8, 8); rotate(angulo); line(0, 0, 150, 0); }

245 Orientación 3/4 Por último, para calcular la orientación relativa a otro punto de referencia que no sea la coordenada de origen (0, 0), utilizamos la función atan2() cuyos parámetros son substraídos por los valores correspondientes al otro punto de referencia que se desea fijar. Veamos lo antedicho en el siguiente ejemplo:

246 Orientación 4/4 // Rota el triángulo apuntando siempre // a la posición del puntero float x = 50; float y = 50; void setup() { size(100, 100); noStroke(); smooth(); } void draw() { background(0); float angulo = atan2(mouseY - y, mouseX - x); pushMatrix(); translate(x, y); rotate(angulo); triangle(-20, -8, 20, 0, -20, 8); popMatrix(); }

247 Lectura recomendada Capítulo “Shape 2: Vertices” (pag. 69). Capítulo “Math 2: Curves” (pag. 79). Capítulo “Math 3: Trigonometry” (pag. 117). Capítulo “Transform 1: Translate, Matrices ” (pag. 133). Capítulo “Transform 2: Rotate, Scale” (pag. 137). Capítulo “Shape 3: Parameters, Recursion” (pag. 197). Capítulo “Input 4: Mouse II” (pag. 237).

248 Información complementaria

249 Reducción de rango Disponemos de cinco funciones básicas: ceil() floor() round() min() max().

250 ceil() - techo Calcula el valor entero más cercano que el valor más grande o igual del de su parámetro. int w = ceil(2.0); // Asigna 2 a w int x = ceil(2.1); // Asigna 3 a x int y = ceil(2.5); // Asigna 3 a y int z = ceil(2.9); // Asigna 3 a z

251 floor() - piso Calcula el valor entero más cercano que el valor más pequeño o igual del de su parámetro. int w = floor(2.0); // Asigna 2 a w int x = floor(2.1); // Asigna 2 a x int y = floor(2.5); // Asigna 2 a y int z = floor(2.9); // Asigna 2 a z

252 round() - redondeo Calcula el valor entero más cercano al valor de la media de su parámetro. int w = round(2.0);// Asigna 2 a w int x = round(2.1);// Asigna 2 a x int y = round(2.5); // Asigna 3 a y int z = round(2.9); // Asigna 3 a z

253 min() - mínimo int u = min(5, 9); // Asigna 5 a u int v = min(-4, -12, -9); // Asigna -12 a v float w = min(12.3, 230.24);// Asigna 12.3 a w

254 max() - máximo int x = max(5, 9); // Asigna 9 a x int y = max(-4, -12, -9); // Asigna -4 a y float z = max(12.3, 230.24); // Asigna 230.24 a z

255 Parte 14 Movimientos simples

256 Movimiento En esta sección revisaremos tres clases de movimiento: Movimiento implícito Movimiento explícito Movimiento mediante transformación

257 Movimiento implícito en una dirección 1/2 Para poner en movimiento una figura, necesitamos al menos usar una variable para cambiar un atributo. El siguiente ejemplo presenta un movimiento implícito, es decir un movimiento rectilíneo uniforme que no contempla posición de origen ni de destino:

258 Movimiento implícito en una dirección 2/2 float y = 50.0; float velocidad = 1.0; float radio = 15.0; void setup() { size(100, 100); smooth(); noStroke(); ellipseMode(RADIUS); } void draw() { background(0); ellipse(33, y, radio, radio); y = y + velocidad; if (y > height+radio) { y = -radio; }

259 Efecto de desenfoque 1/2 Puede crear un efecto de desenfoque utilizando un rectángulo semitransparente dentro del bloque draw(). La cantidad de desenfoque es controlado por el valor del parámetro de transparencia usado para pintar el relleno del rectángulo (la función fill() ). Los números cercanos a 255 refrescarán rápidamente la pantalla, mientras que los cercanos a 0 crearán un fundido lento. Veamos su aplicación en el siguiente ejemplo:

260 Efecto de desenfoque 2/2 float y = 50.0; float velocidad = 1.0; float radio = 15.0; int direccion = 1; void setup() { size(100, 100); smooth(); noStroke(); ellipseMode(RADIUS); } void draw() { fill(0, 12); // Valores funcionales entre 10 y 100 rect(0, 0, width, height); fill(255); ellipse(33, y, radio, radio); y += velocidad * direccion; if ((y > height-radio) || (y < radio)) { direccion = -direccion; }

261 Movimiento implícito en dos direcciones 1/2 También podemos utilizar un segundo conjunto de variables para aprovechar el movimiento en el eje X: float x = 50.0; // coordenada en X float y = 50.0; // coordenada en Y float radio = 15.0; // Radio del círculo float velocidadX = 1.0; // Velocidad de mov. en eje X float velocidadY = 0.4; // Velocidad de mov. en eje Y int direccionX = 1; // Dirección de mov. en eje X int direccionY = -1; // Dirección de mov. en eje X void setup() { size(100, 100); smooth(); noStroke(); ellipseMode(RADIUS); } // ***continúa***

262 Movimiento implícito en dos direcciones 2/2 void draw() { fill(0, 12); rect(0, 0, width, height); fill(255); ellipse(x, y, radio, radio); x += velocidadX * direccionX; if ((x > width-radio) || (x < radio)) { direccionX = -direccionX; // Cambia direccion } y += velocidadY * direccionY; if ((y > height-radio) || (y < radio)) { direccionY = -direccionY; // Cambia direccion }

263 Movimiento explícito en dos direcciones 1/2 Si deseamos mover una figura desde y hasta una posición específica, debemos incorporar algunas variables de control más: float origenX = 20.0; // Coordenada de origen en X float origenY = 10.0; // Coordenada de origen en Y float destinoX = 70.0; // Coordenada de destino en X float destinoY = 80.0; // Coordenada de destino en Y float distX; // Distancia a mover en eje-X float distY; // Distancia a mover en eje-Y float x = 0.0; // Coordenada actual en X float y = 0.0; // Coordenada actual en Y float paso = 0.02; // Tamaño de cada paso (0.0 a 0.4) float pct = 0.0; // Porcentaje recorrido (0.0 a 1.0) // ***continúa***

264 Movimiento explícito en dos direcciones 2/2 void setup() { size(100, 100); noStroke(); smooth(); distX = destinoX - origenX; distY = destinoY - origenY; } void draw() { fill(0, 12); rect(0, 0, width, height); pct += paso; if (pct < 1.0) { x = origenX + (pct * distX); y = origenY + (pct * distY); } fill(255); ellipse(x, y, 20, 20); }

265 Movimiento explícito + easing + desenfoque float origenX = 20.0; // Coordenada de origen en X float origenY = 10.0; // Coordenada de origen en Y float destinoX = 70.0; // Coordenada de destino en X float destinoY = 80.0; // Coordenada de destino en Y float easing = 0.05; // Tamaño de cada para a lo largo del recorrido void setup() { size(100, 100); noStroke(); smooth(); } void draw() { fill(0, 12); rect(0, 0, width, height); float d = dist(origenX, origenY, destinoX, destinoY); if (d > 1.0) { origenX += (destinoX - origenX) * easing; origenY += (destinoY - origenY) * easing; } fill(255); ellipse(origenX, origenY, 20, 20); }

266 Curvas simples 1/5 Las funciones exponenciales son muy útiles a la hora de crear curvas simples. En general se utilizan valores normalizados en conjunción con la función pow() para producir incrementos y decrementos exponenciales de números que nunca exceden el rango de 0.0 a 1.0. Estas ecuaciones tienen la siguiente forma: y = x e donde x (base) corresponde a un valor decimal entre 0.0 y 1.0 y e (exponente) corresponde a un valor entero o decimal.

267 Curvas simples 2/5 Ejemplo 1: for (int x = 0; x < 100; x++) { float n = norm(x, 0.0, 100.0); // Rango 0.0 a 1.0 float y = pow(n, 4); // Cálculo de curva y *= 100; // Rango 0.0 a 100.0 point(x, y); } Ejemplo 2: for (int x = 0; x < 100; x++) { float n = norm(x, 0.0, 100.0); // Rango 0.0 a 1.0 float y = pow(n, 0.4); // Cálculo de curva y *= 100; // Rango 0.0 a 100.0 point(x, y); }

268 Curvas simples 3/5

269 Curvas simples 4/5

270 Curvas simples 5/5

271 Movimiento explícito curvo 1/5 Entonces podemos utilizar ecuaciones exponenciales para generar recorridos curvos en vez de rectilíneos: float origenX = 20.0; // Coordenada de origen en X float origenY = 10.0; // Coordenada de origen en Y float destinoX = 70.0; // Coordenada de destino en X float destinoY = 80.0; // Coordenada de destino en Y float distX; // Distancia a mover en eje-X float distY; // Distancia a mover en eje-X float exponente = 0.5; // Determina el tipo de curva float x = 0.0; // Coordenada actual en X float y = 0.0; // Coordenada actual en Y float paso = 0.01; // Tamaño de cada paso (0.0 a 1.0) float pct = 0.0; // Porcentaje recorrido (0.0 a 1.0) // *** continúa ***

272 Movimiento explícito curvo 2/5 void setup() { size(100, 100); noStroke(); smooth(); distX = destinoX - origenX; distY = destinoY - origenY; } void draw() { fill(0, 2); rect(0, 0, width, height); pct += paso; if (pct < 1.0) { x = origenX + (pct * distX); y = origenY + (pow(pct, exponente) * distY); } fill(255); ellipse(x, y, 20, 20); }

273 Movimiento explícito curvo 3/5 Todas las curvas simples presentadas anteriormente pueden ser escaladas y combinadas para generar recorridos únicos de movimiento. Una vez que se haya calculado un paso de una curva, el programa puede calcular otras posiciones basadas en una curva diferente: float origenX = 20.0; // Coordenada de origen en X float origenY = 10.0; // Coordenada de origen en Y float destinoX = 70.0; // Coordenada de destino en X float destinoY = 80.0; // Coordenada de destino en Y float distX; // Distancia a mover en eje-X float distY; // Distancia a mover en eje-X float exponente = 3.0; // Determina el tipo de curva float x = 0.0; // Coordenada actual en X float y = 0.0; // Coordenada actual en Y float paso = 0.01; // Tamaño de cada paso (0.0 a 1.0) float pct = 0.0; // Porcentaje recorrido (0.0 a 1.0) int direccion = 1; // *** continúa ***

274 Movimiento explícito curvo 4/5 void setup() { size(100, 100); noStroke(); smooth(); distX = destinoX - origenX; distY = destinoY - origenY; } void draw() { fill(0, 2); rect(0, 0, width, height); pct += paso * direccion; if ((pct > 1.0) || (pct < 0.0)) { direccion = direccion * -1; } // *** continúa ***

275 Movimiento explícito curvo 5/5 if (direccion == 1) { x = origenX + (pct * distX); float e = pow(pct, exponente); y = origenY + (e * distY); } else { x = origenX + (pct * distX); float e = pow(1.0-pct, exponente*2); y = origenY + (e * -distY) + distY; } fill(255); ellipse(x, y, 20, 20); }

276 Cambio de velocidad mediante ecuación exponencial 1/3 El siguiente ejemplo muestra el cambio de velocidad del movimiento de una figura mediante una ecuación exponencial. El círculo comienza a moverse muy lentamente y luego se detiene en el márgen inferior del área de representación. La variable exponente describe la pendiente de la curva, la cual cambia la velocidad del movimiento. Utilice el botón del ratón para seleccionar un nuevo punto de origen: float origenX = 20.0; // Coordenada de origen en X float origenY = 10.0; // Coordenada de origen en Y float destinoX = 70.0; // Coordenada de destino en X float destinoY = 80.0; // Coordenada de destino en Y float distX; // Distancia a mover en eje-X float distY; // Distancia a mover en eje-X float exponente = 3.0; // Determina el tipo de curva float x = 0.0; // Coordenada actual en X float y = 0.0; // Coordenada actual en Y float paso = 0.01; // Tamaño de cada paso (0.0 a 1.0) float pct = 0.0; // Porcentaje recorrido (0.0 a 1.0) // *** continúa ***

277 Cambio de velocidad mediante ecuación exponencial 2/3 void setup() { size(100, 100); noStroke(); smooth(); distX = destinoX - origenX; distY = destinoY - origenY; } // *** continúa ***

278 Cambio de velocidad mediante ecuación exponencial 3/3 void draw() { fill(0, 2); rect(0, 0, width, height); if (pct < 1.0) { pct = pct + paso; float velocidad = pow(pct, exponente); x = origenX + (velocidad * distX); y = origenY + (velocidad * distY); } fill(255); ellipse(x, y, 20, 20); } void mousePressed() { pct = 0.0; origenX = x; origenY = y; distX = mouseX - x; distY = mouseY - y; }

279 Movimiento mediante transformación 1/3 Las funciones de transformación ( translate(), rotate(), y scale() ) también pueden crear movimiento al cambiar los valores de sus parámetros. Antes de usar las transformaciones para el movimiento, es importante remarcar que las transformaciones se reinicializan al comienzo de cada bloque draw(). Por lo tanto, cuando se ejecuta translate(50, 0) dentro del bloque draw(), el sistema de coordenadas se ajusta 50 pixeles a la derecha por única vez hasta que se detenga el programa. void setup() { size(100, 100); smooth(); } void draw() { background(0); translate(50, 0); // Se reinicia a 50 px cada vez que entra en draw ellipse(0, 50, 60, 60); }

280 Movimiento mediante transformación 2/3 Del mismo modo, las translaciones dentro del bloque setup() no tienen efecto en las figuras producidas en el bloque draw(). void setup() { size(100, 100); smooth(); translate(50, 0); // No tiene efecto } void draw() { background(0); ellipse(0, 50, 60, 60); }

281 Movimiento mediante transformación 3/3 Las funciones de transformación pueden ser utilizadas para generar movimiento, sin embargo su uso puede ser un poco engorroso. float angulo = 0.0; void setup() { size(100, 100); smooth(); noStroke(); } void draw() { fill(0, 12); rect(0, 0, width, height); fill(255); angulo = angulo + 0.02; translate(70, 40); rotate(angulo); rect(-30, -30, 60, 60); }

282 Lectura recomendada Capítulo “Motion 1: Lines, Curves” (pag. 279).

283 Parte 15 Movimientos mecánico y orgánico

284 Movimiento mecánico 1/4 Generalmente asociamos al movimiento mecánico con aquel producido por diversos dispositivos: el péndulo, el metrónomo, el reloj, el pistón, etc. Todos se caracterizan por el ritmo regular, la repetición y la eficiencia. La función sin() se utiliza regularmente para producir un movimiento elegante. La misma puede generar un movimiento de velocidad variable (más lento en los extremos y más rápido en el centro) como podemos ver en la figura siguiente:

285 Movimiento mecánico 2/4 El siguiente ejemplo utiliza la función sin() para poner en movimiento un círculo: float angulo = 0.0; // Ángulo actual float velocidad = 0.1; // Velocidad del movimiento float rango = 30.0; // Rango del movimiento void setup() { size(100, 100); noStroke(); smooth(); } void draw() { fill(0, 20); rect(0, 0, width, height); fill(255); angulo += velocidad; float sinval = sin(angulo); float yoffset = sinval * rango; ellipse(50, 50 + yoffset, 40, 40); }

286 Movimiento mecánico 3/4 A continuación veremos otro ejemplo que utiliza una combinación de funciones sin() y cos() para generar, mediante ecuaciones más elaboradas, movimientos visualmente más complejos: float angulo = 0.0; // Ángulo actual float velocidad = 0.05; // Velocidad del movimiento float rango = 30.0; // Rango del movimiento float sx = 1.0; float sy = 2.0; void setup() { size(100, 100); noStroke(); smooth(); } // *** continúa ***

287 Movimiento mecánico 4/4 void draw() { fill(0, 4); rect(0, 0, width, height); angulo += velocidad; // Actualiza ángulo float sinVal = sin(angulo); float cosVal = cos(angulo); // Configuro la posición de círculo pequeño basado // en los valores nuevos de sin() y cos() float x = 50 + (cosVal * rango); float y = 50 + (sinVal * rango); fill(255); ellipse(x, y, 2, 2); // Dibujo círculo pequeño // Configuro la posición de círculo grande basado // en la nueva posición de círculo pequeño float x2 = x + cos(angulo * sx) * rango/2; float y2 = y + sin(angulo * sy) * rango/2; ellipse(x2, y2, 6, 6); // Dibujo círculo grande }

288 Fase 1/3 La fase de una función corresponde a una iteración completa a través de todos sus valores posibles. El desplazamiento de fase ocurre cuando se comienza a recorrer una función desde un lugar diferente al inicial: El desplazamiento de fase del ángulo usado para generar valores de la función sin() provee la misma secuencia de números, pero compensados en distintos cuadros de la animación.

289 Fase 2/3 float angulo = 0.0; float velocidad = 0.1; void setup() { size(100, 100); noStroke(); smooth(); } void draw() { background(0); angulo += velocidad; ellipse(50 + (sin(angulo + PI) * 5), 25, 30, 30); ellipse(50 + (sin(angulo + HALF_PI) * 5), 55, 30, 30); ellipse(50 + (sin(angulo + QUARTER_PI) * 5), 85, 30, 30); }

290 Fase 3/3 float angulo = 0.0; // Ángulo float velocidad = 0.05; // Velocidad de crecimiento void setup() { size(100, 100); noStroke(); smooth(); fill(255, 180); } void draw() { background(0); circuloFase(0.0); circuloFase(QUARTER_PI); circuloFase(HALF_PI); angulo += velocidad; } void circuloFase(float fase) { float diameter = 65 + (sin(angulo + fase) * 45); ellipse(50, 50, diameter, diameter); }

291 Movimiento orgánico 1/12 Algunas exploraciones a través de software hechas en los últimos veinte años han servido para modelar diversos tipos de comportamientos de movimientos orgánicos. Ejemplos de movimiento orgánico incluyen: la caída de una hoja, el camino recorrido por un insecto, el vuelo de un pájaro, la respiración de una persona, el fluir de un río, el desplazamiento del humo en el aire, etc. Este tipo de movimiento es considerado idiosincrático y estocástico. The Boids, software creado por Craig Reynolds en 1986, simula el comportamiento de movimiento colectivo de aves y peces posibilitando así nuevas comprensiones de estos tipos de movimientos emergentes. Evolved Virtual Creatures, realizado por Karl Sims en 1994, presenta un trabajo donde criaturas virtuales construidas a partir de bloques rectangulares se disponen en abierta competencia entre sí, a partir de movimientos de tipo orgánico. Es notable apreciar cómo diversos sujetos de observación describen sensaciones de cualidades emotivas al percibir sus acciones.

292 Movimiento orgánico 2/12 A continuación veremos un ejemplo de movimiento browniano, llamado así en honor al reconocido botánico Robert Brown. Es un tipo de movimiento estocástico, muy variable, que originalmente fue relacionado al tipo de movimiento realizado por las diminutas partículas suspendidas en el aire. Este movimiento puede ser simulado mediante software determinando, en cada cuadro, una nueva posición para una partícula, sin preferencia determinada de la dirección del movimiento. Si dejamos el rastro de las posiciones anteriores nos permitirá ver el recorrido realizado en el espacio:

293 Movimiento orgánico 3/12 float x = 50.0; // coordenada X float y = 80.0; // coordenada Y void setup() { size(100, 100); randomSeed(0); // Fuerza los mismos valores aleatorios background(0); stroke(255); } void draw() { x += random(-2, 2); // Asigna nueva coordenada X y += random(-2, 2); // Asigna nueva coordenada Y point(x, y); }

294 Movimiento orgánico 4/12 Las funciones sin() y cos() pueden ser usadas para crear un movimiento impredecible cuando se emplean junto a la función random(). El siguiente ejemplo presenta una línea –con una posición y dirección determinadas– que en cada cuadro cambia levemente su dirección mediante un rango aleatorio pequeño entre -0.3 y 0.3:

295 Movimiento orgánico 5/12 float x = 0.0; // coordenada X float y = 50.0; // coordenada Y float angulo = 0.0; // Dirección de movimiento float velocidad = 0.5; // Velocidad de movimiento void setup() { size(100, 100); background(0); stroke(255, 130); randomSeed(121); // Fuerza los mismos valores aleatorios } void draw() { angulo += random(-0.3, 0.3); x += cos(angulo) * velocidad; // Asigna nueva coordenada X y += sin(angulo) * velocidad; // Asigna nueva coordenada Y translate(x, y); rotate(angulo); line(0, -10, 0, 10); }

296 Movimiento orgánico 6/12 En este otro ejemplo la variable angulo cambia para producir un movimiento de balanceo. Dado que los ángulos para cada figura se acumulan con cada unidad, las figuras más grandes –las que tienen más unidades– se balancean de lado a lado generando una curvatura mayor:

297 Movimiento orgánico 7/12 float inc = 0.0; void setup() { size(100, 100); stroke(255, 204); smooth(); } void draw() { background(0); inc += 0.01; float angulo = sin(inc)/10.0 + sin(inc*1.2)/20.0; alga(18, 9, angulo/1.3); alga(33, 12, angulo); alga(44, 10, angulo/1.3); alga(62, 5, angulo); alga(88, 7, angulo*2); } // ***continúa***

298 Movimiento orgánico 8/12 void alga(int x, int unidades, float angulo) { pushMatrix(); translate(x, 100); for (int i = unidades; i > 0; i--) { strokeWeight(i); line(0, 0, 0, -8); translate(0, -8); rotate(angulo); } popMatrix(); }

299 Movimiento orgánico 9/12 La función noise() es otro buen recurso para producir un movimiento orgánico. Ya que los números producidos con noise() son fáciles de controlar, se presentan como una buena forma para agregar sutiles irregularidades al movimiento. Veamos un ejemplo:

300 Movimiento orgánico 10/12 float inc1 = 0.1; float n1 = 0.0; float inc2 = 0.09; float n2 = 0.0; void setup() { size(100, 100); stroke(255); strokeWeight(20); smooth(); } void draw() { background(0); float y1 = (noise(n1) - 0.5) * 30.0; // Valores -15 a 15 float y2 = (noise(n2) - 0.5) * 30.0; // Valores -15 a 15 line(0, 50, 40, 50 + y1); line(100, 50, 60, 50 + y2); n1 += inc1; n2 += inc2; }

301 Movimiento orgánico 11/12 La función noise() también es útil para producir texturas dinámicas. En el siguiente ejemplo los dos primeros parámetros son usados para producir una textura bidimensional mientras que el tercero incrementa su valor en cada cuadro para variar la textura. Los cambios en la variable densidad modifican la resolución de la imagen, mientras que los cambios en la variable inc modifican la resolución de la textura:

302 Movimiento orgánico 12/12 float inc = 0.06; int densidad = 4; float zRuido = 0.0; void setup() { size(100, 100); noStroke(); } void draw() { float xRuido = 0.0; float yRuido = 0.0; for (int y = 0; y < height; y += densidad) { for (int x = 0; x < width; x += densidad) { float n = noise(xRuido, yRuido, zRuido) * 256; fill(n); rect(y, x, densidad, densidad); xRuido += inc; } xRuido = 0; yRuido += inc; } zRuido += inc; }

303 Lectura recomendada Capítulo “Motion 2: Machine, Organism” (pag. 291).

304 Parte 16 Arrays

305 Introducción 1/5 El término array hace referencia a una agrupación sistemática de objetos. Es posible encontrar en la bibliografía en español diversas traducciones del antedicho término tales como arreglo, vector o matriz, siendo la primera una traducción literal, la segunda haciendo referencia al vector algebraico (generalmente de una dimensión), y la tercera a una matriz algebraica (generalmente de dos). Pocas veces es traducido como el objeto matemático tensor (multidimensional). En informática llamamos array a un conjunto de elementos de datos, todos ellos almacenados bajo un mismo nombre. Los arrays pueden almacenar números, caracteres, cadenas de texto, valores booleanos, datos de posición de vértices correspondientes a una figura compleja, teclas pulsadas, clics de botones de ratón, datos leidos de un archivo de texto, etc.

306 Introducción 2/5 Veamos un ejemplo: queremos almacenar cinco datos (elementos), en este caso cinco números enteros, correspondientes a un conjunto ordenado de datos que llamaremos fechas : Los elementos de un array son numerados a partir del número cero. El primer elemento se encuentra en la posición [0], el segundo en la posición [1], etc. La posición de cada elemento es determinada por el desplazamiento desde el inicio del array. El primer elemento se encuentra en la posición [0] ya que existe desplazamiento; el segundo elemento se encuentra en la posición [1] ya que su lugar se encuentra desplazado un espacio desde el inicio del array. La última posición se calcula mediante la sustracción de 1 a la longitud (cantidad total de elementos) del array. En este ejemplo el último elemento se encuentra en la posición [4] ya que tenemos un total de cinco elementos en el array.

307 Introducción 3/5 Los arrays pueden facilitar mucho la programación. Si bien su uso no es obligatorio, son estructuras valiosas y eficaces para la administración de datos. A continuación veremos, en un ejemplo concreto de aplicación, algunos de los beneficios de utilizar arrays en lugar de un gran número de variables: Partimos de un conjunto de datos que determinan las coordenadas de posición de los vértices que dibujan una figura “estrella”. La “estrella” está conformada por 10 puntos vértice, cada uno con 2 valores (pos. x e y). Si quisieramos utilizar variables deberíamos declarar 20 variables, cantidad bastante considerable.

308 Introducción 4/5 Si utilizásemos arrays, existen dos maneras de resolverlo: implementar 10 arrays que integren los valores de las coordenadas xy para cada punto, o bien implementar 2 arrays que integren todos los valores de los puntos para cada eje (x e y).

309 Introducción 5/5 Si bien el código en el medio mejora notablemente la situación, es posible optimizarlo aún más. El código en la derecha muestra cómo los elementos de datos pueden ser agrupados de manera lógica en 2 arrays, uno para la abscisa y el otro para la ordenada. A continuación veremos cómo es posible implementar el ejemplo y cómo acceder a cada valor de los arrays mediante el uso de una estructura for : void setup() { int[] x = {50, 61, 83, 69, 71, 50, 29, 31, 17, 39}; int[] y = {18, 37, 43, 60, 82, 73, 82, 60, 43, 37}; beginShape(); // Lee un elemento de cada array por vez mediante el for() for (int i = 0; i < x.length; i++) { vertex(x[i], y[i]); } endShape(CLOSE); }

310 Uso de arrays 1/3 En Processing, cuando trabajamos con un array, en primer lugar debemos declararlo, en segundo crearlo y por último asignarlo: Los arrays deben ser declarados de forma similar a las variables pero se distinguen por el uso de los corchetes [ y ]. Además se debe especificar el tipo de dato que almacena. Luego de la declaración se debe crear el array mediante la palabra clave new. Este paso adicional designa un espacio en la memoria del ordenador para almacenar los datos del array. Una vez creado el array, se puede asignar los valores al mismo.

311 Uso de arrays 2/3 Existen diversas formas de declarar, crear y asignar arrays: Estos tres ejemplos asumen que los tres arrays están siendo usados junto a estructuras setup() y draw(). int[] datos = new int[5]; // Declaración y creación void setup() { datos[0] = 19; // Asignación datos[1] = 40; datos[2] = 75; datos[3] = 76; datos[4] = 90; println(datos[1]); } int[] datos; // Declaración void setup() { datos = new int[5]; // Creación datos[0] = 19; // Asignación datos[1] = 40; datos[2] = 75; datos[3] = 76; datos[4] = 90; println(datos[1]); } int[] datos = {19, 40, 75, 76, 90}; // Declaración, creación y asignación void setup() { println(datos[1]); }

312 Uso de arrays 3/3 Los pasos de declaración, creación y asignación nos habilitan a la lectura de los valores de los distintos elementos del array. Se puede obtener el valor de uno de los elementos del array llamando al nombre del array seguido de los corchetes los cuales encierran el número índice del elemento que se desea acceder: int[] datos = {19, 40, 75, 76, 90}; line(datos[0], 0, datos[0], height); line(datos[1], 0, datos[1], height); line(datos[2], 0, datos[2], height); line(datos[3], 0, datos[3], height); line(datos[4], 0, datos[4], height); Processing responderá con el mensaje ArrayIndexOutOfBoundsException con cualquier número índice ingresado que exceda el rango actual del array.

313 Uso del campo length Para obtener la cantidad total de elementos almacenados en un array usamos el campo length. Más adelante veremos qué es un campo en relación a la programación orientada a objetos, pero por el momento diremos que es posible acceder a un campo mediante el operador punto (. ). El siguiente ejemplo muestra cómo utilizarlo: int[] datos1 = {19, 40, 75, 76, 90}; int[] datos2 = {19, 40}; int[] datos3 = new int[127]; println(datos1.length); // Imprime "5" en la consola println(datos2.length); // Imprime "2" en la consola println(datos3.length); // Imprime "127" en la consola

314 Uso de la estructura for 1/4 Usualmente se utiliza una estructura for para acceder a los elementos de un array, especialmente cuando éste contiene numerosos elementos. El presente ejemplo… int[] datos = {19, 40, 75, 76, 90}; line(datos[0], 0, datos[0], height); line(datos[1], 0, datos[1], height); line(datos[2], 0, datos[2], height); line(datos[3], 0, datos[3], height); line(datos[4], 0, datos[4], height); … puede ser reemplazado por este otro: int[] datos = {19, 40, 75, 76, 90}; for (int i = 0; i < datos.length; i++) { line(datos[i], 0, datos[i], 100); }

315 Uso de la estructura for 2/4 La estructura for también puede ser usada para ingresar datos en un array. El siguiente ejemplo se almacena en un array los valores provenientes de una función sin() dentro del bloque setup(), y luego muestra dichos valores a través del color de contorno de líneas dentro del bloque draw() : float[] ondaSeno; void setup() { size(100, 100); ondaSeno = new float[width]; for (int i = 0; i < width; i++) { // Llena el array con los valores de sin() float r = map(i, 0, width, 0, TWO_PI); ondaSeno[i] = abs(sin(r)); } void draw() { for (int i = 0; i < ondaSeno.length; i++) { // Aplica los valores del array en la función stroke() stroke(ondaSeno[i] * 255); line(i, 0, i, height); }

316 Uso de la estructura for 3/4 Otra forma de hacer más sencilla la lectura y gestión de un programa puede darse gracias al uso de un array para almacenar las coordenadas de gran cantidad de elementos. En el siguiente ejemplo el array x[] almacena la coordenada-x para cada uno de los 12 elementos, y el array velocidad[] almacena la relación correspondiente a cada uno de ellos. La escritura de este programa sin arrays hubiera requerido la inclusión de 24 variables independientes. En cambio, de esta manera, resulta sencillo cambiar el valor asignado a numLineas para modificar el número de elementos dibujados en la pantalla.

317 Uso de la estructura for 4/4 int numLineas = 12; float[] x = new float[numLineas]; float[] velocidad = new float[numLineas]; float offset = 8; // Espacio entre líneas void setup() { size(100, 100); smooth(); strokeWeight(10); for (int i = 0; i < numLineas; i++) { x[i] = i; // Posición inicial velocidad[i] = 0.1 + (i / offset); // Velocidad inicial } void draw() { background(204); for (int i = 0; i < x.length; i++) { x[i] += velocidad[i]; // Actualiza posición de la línea if (x[i] > (width + offset)) { // Si sale por derecha, x[i] = -offset * 2; // regresa por la izquierda. } float y = i * offset; // Determina la posición-y de la línea line(x[i], y, x[i]+offset, y+offset); // Dibuja la línea }

318 Almacenamiento de datos del ratón 1/4 Los arrays también son muy usados para almacenar los datos obtenidos del ratón. Las variables de sistema pmouseX y pmouseY almacenan las coordenadas del ratón del cuadro previo, pero no existe una manera ya incorporada en el sistema para acceder a los valores del cursor de cuadros anteriores. En cada cuadro, los valores de las variables mouseX, mouseY, pmouseX y pmouseY son reemplazadas con nuevos valores y los previos son descartados. La forma más sencilla de almacenar la historia de dichos valores se da gracias al uso de un array. En el siguiente ejemplo, los 100 últimos valores de mouseY son almacenados en un array y son mostrados en pantalla mediante una línea que la recorre de izquierda a derecha. En cada cuadro, los valores del array son transladados a la derecha y el valor más reciente es agregado al inicio.

319 Almacenamiento de datos del ratón 2/4 int[] y; void setup() { size(100, 100); y = new int[width]; } void draw() { background(204); // Desplaza los valores a la derecha for (int i = y.length-1; i > 0; i--) { y[i] = y[i-1]; } // Agrega nuevos valores al inicio y[0] = constrain(mouseY, 0, height-1); // Muestra cada par de valores como una línea for (int i = 1; i < y.length; i++) { line(i, y[i], i-1, y[i-1]); }

320 Almacenamiento de datos del ratón 3/4 Aplique el mismo código a los valores de mouseX y mouseY para almacenar la posición del cursor. La visualización de estos valores en cada cuadro crea una estela detrás del cursor: void draw() { background(0); // Desplaza los valores a la derecha for (int i = num-1; i > 0; i--) { x[i] = x[i-1]; y[i] = y[i-1]; } // Agrega los nuevos valores al inicio del array x[0] = mouseX; y[0] = mouseY; // Dibuja los círculos for (int i = 0; i < num; i++) { ellipse(x[i], y[i], i/2.0, i/2.0); } int num = 50; int[] x = new int[num]; int[] y = new int[num]; void setup() { size(100, 100); noStroke(); smooth(); fill(255, 102); } // *** continúa ***

321 Almacenamiento de datos del ratón 4/4 El siguiente ejemplo produce el mismo resultado que el anterior pero usa una técnica más eficiente. En lugar de ordenar los elementos del array en cada cuadro, el programa escribe los nuevos datos en la próxima posición del array disponible. Los elementos del array permanecen en la misma posición una vez escritos, pero son leídos en un orden diferente en cada cuadro. La lectura comienza en la posición del elemento más antiguo y continúa hasta el final del array. Al final del array, se usa el operador % para volver nuevamente al principio. Esta técnica es especialmente útil para aplicar a arrays de gran tamaño para evitar la copia de datos innecesarios que puede llevar a una considerable reducción de velocidad del programa. void draw() { background(0); x[indicePosicion] = mouseX; y[indicePosicion] = mouseY; // Ciclo entre 0 y el número de elementos indicePosicion = (indicePosicion + 1) % num; for (int i = 0; i < num; i++) { // Determina la posición del array a leer int pos = (indicePosicion + i) % num; float radio = (num-i) / 2.0; ellipse(x[pos], y[pos], radio, radio); } int num = 50; int[] x = new int[num]; int[] y = new int[num]; int indicePosicion = 0; void setup() { size(100, 100); noStroke(); smooth(); fill(255, 102); } // *** continúa ***

322 Funciones de array Processing provee un grupo de funciones que permiten asistir en la gestión de los arrays: append() shorten() expand() arrayCopy() concat() subset() sort() reverse() splice()

323 Función de array: append() Expande un elemento del array, añade los datos en la nueva posición y regresa el array incrementado. El tipo de dato del segundo parámetro (elemento) debe ser el mismo que el tipo de dato del array. int[] numeros = {1, 3}; append(numeros, 5); // INCORRECTO! No cambia el array println(numeros); // Imprime "1" y "3" println(); numeros = append(numeros, 5); // Agrega "5" al final println(numeros); // Imprime "1", "3" y "5" println(); // Agrega "7" al final del array "numeros", y crea un nuevo // array donde guarda el cambio int[] masNumeros = append(numeros, 7); println(masNumeros); // Imprime "1", "3", "5" y "7"

324 Función de array: shorten() Disminuye un array en un elemento y regresa el array acortado. int[] numeros = {1, 3, 5, 7}; numeros = shorten(numeros); // Recorta el último elemento println(numeros); // Imprime "1", "3" y "5"

325 Función de array: expand() Incrementa el tamaño de un array. Puede expandirlo a un tamaño determinado (según el segundo parámetro) o, si no se especifica el tamaño, se lo dobla. int[] numeros = {1, 3, 5, 7}; println(numeros.length); // Imprime "4“ numeros = expand(numeros); println(numeros.length); // Imprime "8" numeros = expand(numeros, 512); println(numeros.length); // Imprime "512"

326 Función de array: arrayCopy() Copia un array (o parte de él) a otro array. Existen tres versiones de dicha función: int[] numeros1 = {1, 3, 5, 7}; int[] numeros2 = {2, 4, 6, 8}; arrayCopy(numeros1, numeros2); println(numeros2); // Imprime "1", "3", "5" y "7" int[] numeros1 = {1, 3, 5, 7}; int[] numeros2 = {2, 4, 6, 8}; arrayCopy(numeros1, 1, numeros2, 0, 2); println(numeros2); // Imprime "3", "5", "6" y "8" arrayCopy(arrayOrigen, arrayDestino) arrayCopy(arrayOrigen, arrayDestino, cantElementosACopiar) arrayCopy(arrayOrigen, arrayOrigenPos, arrayDestino, arrayDestinoPos, cantElementosACopiar)

327 Función de array: concat() Concatena dos arrays. int[] numeros1 = {1, 3, 5, 7}; int[] numeros2 = {2, 4, 6, 8}; numeros1 = concat(numeros1, numeros2); println(numeros1); // Imprime "1", "3", "5", "7", "2", "4", "6" y "8"

328 Función de array: reverse() Invierte el orden de un array. int[] numeros = {1, 3, 5, 7}; numeros = reverse(numeros); println(numeros); // Imprime "7", "5", "3" y "1"

329 Función de array: sort() Ordena un array de números de menor a mayor, o pone en orden alfabético un array de palabras. El array original no resulta modificado, se regresa un array re-ordenado. float[] decimales = {3.4, 2, 0, 7.1}; decimales = sort(decimales); println(decimales); // Imprime "0.0", "2.0", "3.4" y "7.1"

330 Función de array: splice() Inserta un valor o un array de valores dentro de un array existente. int[] numeros = {1, 3, 5, 7}; numeros = splice(numeros, 2, 1); println(numeros); // Imprime "1", "2", "3", "5" y "7" int[] numeros1 = {1, 3, 5}; int[] numeros2 = {2, 4, 6}; numeros2 = splice(numeros1, numeros2, 2); println(numeros2); // Imprime "1", "3", "2", "4", "6" y "5" splice(array, valorASerInsertado, posIndiceArray) splice(array, arrayASerInsertado, posIndiceArray) int[] numeros1 = {1, 3, 5}; int[] numeros2 = {2, 4, 6}; numeros1 = splice(numeros2, numeros1, 2); println(numeros1); // Imprime "2", "4", "1", "3", "5" y "6"

331 Función de array: subset() Extrae una serie de elementos de un array. El parámetro array define el array desde el cual serán tomados los elementos. El parámetro offset define la posición desde la cual se comienza a extraer (primer valor). El parámetro numeroDeValoresAExtraer determina la cantidad de elementos que serán extraídos. Si este parámetro no es utilizado, los elementos serán extraídos desde el offset hasta el final del array. int[] numeros1 = {1, 3, 5, 7}; int[] numeros2 = subset(numeros1, 2); println(numeros2); // Imprime "5" y "7" subset(array, offset) subset(array, offset, numeroDeValoresAExtraer) int[] numeros1 = {1, 3, 5, 7, 9, 11}; int[] numeros2 = subset(numeros1, 2, 3); println(numeros2); // Imprime "5", "7" y “9"

332 Consideraciones particulares sobre los arrays 1/3 Se pueden escribir nuevas funciones que realizan operaciones sobre los arrays, pero los arrays se comportan de manera diferente a otros tipos de dato como int o char. Cuando un array es usado como parámetro de función, la dirección (ubicación en la memoria) del array es transferida dentro de la función en lugar de los datos reales. No resulta creado ningún array nuevo, y los cambios realizados dentro de la función afectan al array usado como parámetro.

333 Consideraciones particulares sobre los arrays 2/3 En el siguiente ejemplo, el array datos[] es usado como parámetro de la función mitad(). La dirección de datos[] es pasada al array d[] mediante la función mitad(). Ya que la dirección de d[] y datos[] es la misma, ellos afectan al mismo contenido. Cuando se realizan cambios en d[], estos cambios se producen sobre los valores del array datos[]. float[] datos = {19.0, 40.0, 75.0, 76.0, 90.0}; void setup() { mitad(datos); println(datos[0]); // Imprime "9.5" println(datos[1]); // Imprime "20.0" println(datos[2]); // Imprime "37.5" println(datos[3]); // Imprime "38.0" println(datos[4]); // Imprime "45.0" } void mitad(float[] d) { for (int i = 0; i < d.length; i++) { // Cada elemento del array, d[i] = d[i] / 2.0; // se divide por dos. }

334 Consideraciones particulares sobre los arrays 3/3 El cambio de los datos de un array dentro de una función, sin modificar el array original, requiere algunas líneas de código adicional. En el siguiente ejemplo, se pasa el array como parámetro dentro de una función, se crea un nuevo array, los valores del array original son copiados en el nuevo array, los cambios son realizados en el nuevo array, y finalmente se regresa el array modificado. float[] datos = {19.0, 40.0, 75.0, 76.0, 90.0}; float[] mitadDatos; void setup() { mitadDatos = mitad(datos); // Ejecuta la función mitad() println(datos[0] + ", " + mitadDatos[0]); // Imprime "19.0, 9.5" println(datos[1] + ", " + mitadDatos[1]); // Imprime "40.0, 20.0" println(datos[2] + ", " + mitadDatos[2]); // Imprime "75.0, 37.5" println(datos[3] + ", " + mitadDatos[3]); // Imprime "76.0, 38.0" println(datos[4] + ", " + mitadDatos[4]); // Imprime "90.0, 45.0" } float[] mitad(float[] d) { float[] numeros = new float[d.length]; // Crea un nuevo array arraycopy(d, numeros); for (int i = 0; i < numeros.length; i++) { // Cada elemento del array, numeros[i] = numeros[i] / 2; // se divide por dos. } return numeros; // Regresa el nuevo array }

335 Arrays bidimensionales 1/2 Los datos también pueden ser almacenados y recuperados mediante arrays con más de una dimensión. A partir del ejemplo de la introducción de esta sección, mostraremos cómo utilizar un array 2D para almacenar los puntos vértice de la estrella: Un array 2D es esencialmente una lista de arrays 1D. Debe ser primero declarado, luego creado, y por último asignado tal como un array 1D. A continuación el código: int[][] puntos = { {50,18}, {61,37}, {83,43}, {69,60}, {71,82}, {50,73}, {29,82}, {31,60}, {17,43}, {39,37} }; println(puntos[4][0]); // Imprime "71" println(puntos[4][1]); // Imprime "82" println(puntos[4][2]); // ERROR! Este elemento se encuentra fuera de rango println(puntos[0][0]); // Imprime "50" println(puntos[9][1]); // Imprime "37" println(puntos[1]); // Imprime "61" y "37"

336 Arrays bidimensionales 2/2 El próximo ejemplo muestra cómo se aplica el array 2D en el ejemplo de la estrella: Si bien es posible crear arrays multidimensionales (3D, 4D o más) extrapolando estas técnicas, su implementación suele ser muy compleja y frecuentemente se prefiere aplicar múltiples arrays 1D o 2D. int[][] puntos = { {50,18}, {61,37}, {83,43}, {69,60}, {71,82}, {50,73}, {29,82}, {31,60}, {17,43}, {39,37} }; void setup() { size(100, 100); fill(0); smooth(); } void draw() { background(204); translate(mouseX - 50, mouseY - 50); beginShape(); for (int i = 0; i < puntos.length; i++) { vertex(puntos[i][0], puntos[i][1]); } endShape(); }

337 Lectura recomendada Capítulo “Data 4: Arrays” (pag. 301).

338 Parte 17 Objetos

339 Introducción 1/2 El paradigma planteado por la programación estructurada tradicional define las variables (datos) y las funciones (procedimientos) como los bloques básicos de construcción. Distintas funciones serán frecuentemente usadas en conjunto para trabajar sobre una serie determinada de variables. La programación orientada a objetos (POO) fue desarrollada para hacer más explícito este proceso. La POO utiliza clases y objetos como bloques básicos de construcción. Una clase define un grupo de métodos (funciones) y campos (variables). Un objeto es una única instancia de una clase. Los campos dentro de un objeto se acceden, típicamente, sólo a través de sus propios métodos, permitiendo a un objeto ocultar su complejidad de otras partes del programa.

340 Introducción 2/2 La POO difiere de la programación estructurada tradicional, en la que los datos y los procedimientos están separados y sin relación, ya que lo único que se busca es el procesamiento de unos datos de entrada para obtener otros de salida. La programación estructurada anima al programador a pensar sobre todo en términos de procedimientos o funciones, y en segundo lugar en las estructuras de datos que esos procedimientos manejan. En la programación estructurada sólo se escriben funciones que procesan datos. Los programadores que emplean POO, en cambio, primero definen objetos para luego enviarles mensajes solicitándoles que realicen sus métodos por sí mismos. Programación estructurada Progr. orientada a objetos Variable Campo Función Método

341 POO 1/3 Un programa modular está compuesto de módulos de código los cuales realizan, cada uno, una tarea específica. El uso de variables es un medio fundamental para estudiar la reutilización de elementos dentro de un programa. Permite que un determinado valor aparezca las veces que se necesite dentro de un programa y que sea fácilmente cambiado. Las funciones resumen una tarea específica y permiten que bloques de código sean usados en todo el programa. Típicamente, uno se concentra sólo en qué es lo que la función hace, no en cómo esta trabaja. Todo esto permite que la mente se concentre en los objetivos del programa antes que en las complejidades de la infraestructura.

342 POO 2/3 La programación orientada a objetos amplía aún más la modularidad -el uso de variables y escritura de funciones- al permitir la agrupación de funciones relacionadas. Podemos asociar los objetos de la POO con artefactos concretos: Si extendemos el ejemplo de la Manzana podremos apreciar un poco más las consideraciones acerca de las relaciones entre los objetos concretos y los objetos de software: el método crecer() podría tener entradas para temperatura y humedad ; el método caer() podría continuamente controlar peso y hacerla caer cuando supere un determinado umbral; el método descomponer() podría entonces hacerse cargo comenzando a disminuir el valor de peso y cambiar color. ClaseManzana Mariposa Campocolor, peso especie, genero Métodocrecer(), caer(), descomponer () vatirAlas(), tomarTierra() ClaseRadio Auto Campofrecuencia, volumen marca, modelo, color Métodoencender(), sintonizar(), ajustarVol() acelerar (), frenar(), girar()

343 POO 3/3 Los objetos son creados a partir de una clase, y una clase describe un conjunto de campos y métodos. Una instancia de una clase debe tener un nombre único. Si más de un objeto es creado a partir de una clase, cada uno debe tener un nombre único. Por ejemplo de la clase Manzana podemos crear dos objetos con sus correspondientes valores de campos: Se accede a los campos y métodos de un objeto mediante el operador punto (. ). Para obtener el valor del campo color del objeto deliciosa, se utiliza la sintaxis deliciosa.color. Para activar (o llamar) al método crecer() del objeto grannySmith, también se utiliza la siguiente sintaxis con el operador punto: grannySmith.crecer(). ObjetodeliciosagrannySmith Camposcolor: rojocolor: amarillo peso: 200peso: 230

344 Uso de clases y objetos 1/12 Definir una clase es, en definitiva, crear nuestro propio tipo de dato. A diferencia de otros tipos primitivos como int, float y boolean, se trata de un tipo compuesto -como String, PImage y PFont. Esto significa que puede almacenar muchas variables y métodos bajo un mismo nombre. Al momento de crear una clase, se debe pensar cuidadosamente sobre qué quiere que el código haga. Es común realizar primero una lista de variables (estas serán los campos) y resolver los tipos de dato correspondientes. Ejemplifiquemos: queremos dibujar un círculo blanco en la pantalla, por lo tanto pensamos en tres campos: dos para la posición y uno para el diámetro, prefiriendo el tipo float para aportar mayor flexibilidad para controlar el movimiento: floatxcoordenada-x del círculo floatycoordenada-y del círculo floatdiametrodiámetro del círculo

345 Uso de clases y objetos 2/12 El nombre de la clase debe ser cuidadosamente considerado. Por convención se recomienda utilizar una letra mayúscula en la primera letra para diferenciarlo de las variables. Una vez determinados el nombre de la clase y los campos, considere cómo hubiera escrito el programa sin el uso de objetos: float x = 33; float y = 50; float diametro = 30; void setup() { size(100, 100); smooth(); noStroke(); } void draw() { background(0); ellipse(x, y, diametro, diametro); }

346 Uso de clases y objetos 3/12 En el próximo ejemplo veremos cómo aplicar la POO en el código: moveremos los campos que pertenecen al círculo a su propia clase. La primera línea declara el objeto circ de tipo Circulo. La clase Circulo se declara a continuación del bloque setup() y draw(). El objeto circ es construido dentro de setup(), permitiendo así el acceso a sus campos. En las tres líneas siguientes se asignan valores a los campos dentro de Circulo. Se accede a estos valores dentro de draw() para determinar la posición y el tamaño del círculo. El operador punto (. ) se usa para asignar – setup() - y acceder – draw() - a las variables de la clase. Circulo circ; // Declaración del objeto void setup() { size(100, 100); smooth(); noStroke(); circ = new Circulo(); // Construcción del objeto circ.x = 33; // Asigna 33 al campo x circ.y = 50; // Asigna 50 al campo y circ.diametro = 30; // Asigna 30 al campo diametro } void draw() { background(0); ellipse(circ.x, circ.y, circ.diametro, circ.diametro); //Accede a los campos } class Circulo { float x, y; // Coordenadas xy float diametro; // Diámetro del círculo }

347 Uso de clases y objetos 4/12 La clase Circulo que hemos declarado no es muy útil por el momento, sin embargo es un inicio. El próximo ejemplo se construye sobre el anterior y agrega un método a dicha clase. El método mostrar() ha sido agregado a la definición de la clase para dibujar la figura en la pantalla. La última línea en draw() ejecuta el método mostrar() delegado al objeto circ al escribir los nombres del objeto y del método conectados por un operador punto (. ). Note también que no se usa el nombre del objeto para acceder a los campos. Esto sucede así porque que la función ellipse() se llama desde dentro de la clase Circulo. Ya que esta línea es parte del método mostrar(), este puede acceder a sus propias variables sin especificar su propio nombre. Circulo circ ; // Declaración del objeto void setup() { size(100, 100); smooth(); noStroke(); circ = new Circulo(); // Construcción del objeto circ.x = 33; circ.y = 50; circ.diametro = 30; } void draw() { background(0); circ.mostrar(); } class Circulo { float x, y, diametro; // Campos void mostrar() { // Método ellipse(x, y, diametro, diametro); }

348 Uso de clases y objetos 5/12 Resulta prudente a esta altura reforzar la diferencia que existe entre la clase Circulo y el objeto circ. Aunque el código pueda parecer indicarnos que los campos x, y y diametro, y el método mostrar() pertenecen a la clase, Circulo es sólo la definición para cualquier objeto creado a partir de dicha clase. Cada uno de estos elementos pertenecen a (están encapsulados por) la variable circ, la cual es una instancia del tipo de dato Circulo. El próximo ejemplo presenta un nuevo elemento de programación llamado constructor.

349 Uso de clases y objetos 6/12 Un constructor es un bloque de código activado al momento de la creación de un objeto. El constructor siempre tiene el mismo nombre que la clase y es típicamente usado para asignar valores a los campos de un objeto cuando este se construye. El constructor funciona como cualquier otro método, excepto en que no es precedido con un tipo de dato o la palabra clave void ya que no contempla ningún tipo de retorno. Cuando se crea el objeto circ, los parámetros 33, 50 y 30 son asignados en correspondencia a las variables xpos, ypos y diam dentro del constructor. Dentro del bloque constructor, estos valores resultan asignados a los campos x, y y diam del objeto. Para que los campos sean accesibles entre cada método del objeto, estos son declarados fuera del constructor. Recuerde las reglas del ámbito de las variables: si los campos son declarados dentro del constructor, estos no pueden ser accedidos por fuera del constructor.

350 Uso de clases y objetos 7/12 Ahora sí veamos el ejemplo: Circulo circ; // Declaración del objeto void setup() { size(100, 100); smooth(); noStroke(); circ = new Circulo(33, 50, 30); // Construcción del objeto } void draw() { background(0); circ.mostrar(); } class Circulo { float x, y, diametro; // Campos Circulo(float xpos, float ypos, float diam) { // Constructor x = xpos; // Asigna 33 a x y = ypos; // Asigna 50 a y diametro = diam; // Asigna 30 a diametro } void mostrar() { // Método ellipse(x, y, diametro, diametro); }

351 Uso de clases y objetos 8/12 El comportamiento de la clase Circulo puede ser extendido aún más gracias a la incorporación en su definición de una mayor cantidad de campos y métodos. El siguiente ejemplo extiende la clase para que el círculo se mueva hacia arriba y hacia abajo, y que cambie de dirección cuando este alcance el borde superior o inferior de la ventana de visualización. Ya que el círculo estará moviéndose, este necesita un campo que defina la velocidad y otro campo que almacene la dirección. Llamaremos a estos campos velocidad y direccion. El campo velocidad será un float para maximizar el rango de valores obtenibles, mientras que direccion será suficiente un int para aplicar operaciones aritméticas básicas (1 dirección hacia arriba, -1 dirección hacia abajo).

352 Uso de clases y objetos 9/12 Para crear el movimiento deseado, necesitamos actualizar la posición del círculo en cada cuadro. La dirección también debe actualizarse cuando alcance los bordes de la ventana de visualización. La evaluación de borde se da cuando la coordenada-y es menor que el radio del círculo, o cuando esta es mayor que la altura de la ventana menos el radio del círculo. Luego la dirección cambiará cuando el borde exterior del círculo (en vez de su centro) llegue al borde de la ventana. Además de decidir qué necesitan hacer los métodos y qué nombre tendrán, debemos considerar también el tipo de retorno. Ya que no se devolverá nada, se usará la palabra clave void. Los códigos dentro de los métodos mover() y mostrar() podrían haber sido combinados en un solo método; fueron separados para hacer más claro el ejemplo. El cambio de la posición del objeto y la visualización en la pantalla del mismo son tareas distintas, y el uso de métodos separados así lo refleja.

353 Uso de clases y objetos 10/12 Circulo circ; // Declaración del objeto void setup() { size(100, 100); smooth(); noStroke(); circ = new Circulo(33, 50, 30, 1.5); // Construcción del objeto } void draw() { fill(0, 15); rect(0, 0, width, height); fill(255); circ.mover(); circ.mostrar(); } class Circulo { // Campos float x, y, diametro; float velocidad; // Distancia recorrida en cada cuadro int direccion = 1; // Dirección del movimiento (1 hacia abajo, -1 hacia arriba) // Constructor Circulo(float xpos, float ypos, float diam, float vel) { x = xpos; y = ypos; diametro = diam; velocidad = vel; } // Métodos void mover() { y += (velocidad * direccion); if ((y > (height - diametro/2)) || (y < diametro/2)) { direccion *= -1; } void mostrar() { ellipse(x, y, diametro, diametro); }

354 Uso de clases y objetos 11/12 Al igual que una función, una clase bien escrita permite al programador concentrarse en comportamiento resultante y no en los detalles de ejecución. Los objetos deben ser escritos con la finalidad de reutilización. Como ocurre con otros tipos de variables, objetos adicionales son agregados al declarar más nombres. El siguiente ejemplo tiene tres objetos hechos a partir de la clase Circulo. Cada uno de estos objetos ( circ1, circ2 y circ3 ) tienen su propio conjunto de campos y métodos. Se ejecuta un método declarado en la clase por cada objeto que lo llama. Cuando se llaman a estos métodos, ellos acceden a los valores de los campos pertenecientes a cada objeto. Es decir, cuando circ2 llama por primera vez al método mover(), el valor del campo y es actualizado por el valor 2.0 del campo velocidad ya que dicho valor fue pasado al objeto circ2 a través del contructor.

355 Uso de clases y objetos 12/12 Ahora sí veamos el ejemplo: Circulo circ1, circ2, circ3; // Declaración de los objetos void setup() { size(100, 100); smooth(); noStroke(); circ1 = new Circulo(20, 50, 40, 0.5); // Construcción del objeto circ1 circ2 = new Circulo(50, 50, 10, 2.0); // Construcción del objeto circ2 circ3 = new Circulo(80, 50, 30, 1.5); // Construcción del objeto circ3 } void draw() { fill(0, 15); rect(0, 0, width, height); fill(255); circ1.mover(); circ2.mover(); circ3.mover(); circ1.mostrar(); circ2.mostrar(); circ3.mostrar(); } // Insertar aquí la clase Circulo

356 Otro ejemplo 1/2 Huevo humpty; // Declaración del objeto void setup() { size(100, 100); smooth(); // Entradas: coord-x, coord-y, factor de balanceo y altura humpty = new Huevo(50, 100, 8, 80); // Construcción del objeto } void draw() { background(0); humpty.balancear(); humpty.mostrar(); } class Huevo { // Campos float x, y; // coords-xy float inclinacion; // Offset ángulo izquierda y derecha float balanceo; // Factor de balanceo float angulo; // Ángulo de balanceo float altura; // Altura del huevo // Constructor Huevo(int xpos, int ypos, float balFactor, float h) { x = xpos; y = ypos; balanceo = balFactor; altura = h / 100.0; } // *** continúa ***

357 Otro ejemplo 2/2 // Métodos void balancear() { inclinacion = cos(angulo) / balanceo; angulo += 0.1; } void mostrar() { noStroke(); fill(255); pushMatrix(); translate(x, y); rotate(inclinacion); scale(altura); beginShape(); vertex(0, -100); bezierVertex(25, -100, 40, -65, 40, -40); bezierVertex(40, -15, 25, 0, 0, 0); bezierVertex(-25, 0, -40, -15, -40, -40); bezierVertex(-40, -65, -25, -100, 0, -100); endShape(); popMatrix(); }

358 Ejercicio 16 EJ16: Crear dos "entes autónomos" en el escenario. Uno debe exhibir movimientos de cualidades más bien mecánicas y el otro orgánicas. Utilizar clases. Comentar todas las instrucciones.

359 Lectura recomendada Capítulo “Structure 4: Objects I” (pag. 395).

360 Parte 18 Arrays de objetos

361 Introducción El trabajo con arrays de objetos es similar al trabajo con arrays de otros tipos de dato. Como todo array, un array de objetos se distingue de cualquier objeto gracias a los corchetes. Ya que cada elemento de array es un objeto, cada elemento del array debe ser creado antes de que pueda ser accedido. Los pasos para el trabajo con un array de objetos son los siguientes: 1. Declaración del array 2. Creación del array 3. Creación de cada objeto del array Veamos cómo se implementa en los próximos dos ejemplos:

362 Ejemplo 1 1/2 int numCirc = 6; // Declaración y creación del array Circulo[] circulos = new Circulo[numCirc]; void setup() { size(100, 100); smooth(); noStroke(); for (int i = 0; i < circulos.length; i++) { float x = 10 + i*16; float rate = 0.5 + i*0.05; // Creación de cada objeto del array circulos[i] = new Circulo(x, 50, 16, rate); } void draw() { fill(0, 12); rect(0, 0, width, height); fill(255); for (int i = 0; i < circulos.length; i++) { circulos[i].mover(); // Move each object circulos[i].mostrar(); // Display each object } // *** continúa ***

363 Ejemplo 1 2/2 class Circulo { // Campos float x, y, diametro; float velocidad; // Distancia movida en cada cuadro int direccion = 1; // Dirección del movimiento (1 hacia abajo, -1 hacia arriba) // Constructor Circulo(float xpos, float ypos, float diam, float vel) { x = xpos; y = ypos; diametro = diam; velocidad = vel; } // Métodos void mover() { y += (velocidad * direccion); if ((y > (height - diametro/2)) || (y < diametro/2)) { direccion *= -1; } void mostrar() { ellipse(x, y, diametro, diametro); } } // *** continúa ***

364 Ejemplo 2 1/2 Anillo[] anillos; // Declaración del array int numAnillos = 50; int actualAnillo = 0; void setup() { size(100, 100); smooth(); anillos = new Anillo[numAnillos]; // Creación del array for (int i = 0; i < numAnillos; i++) { anillos[i] = new Anillo(); // Creación de cada objeto } void draw() { background(0); for (int i = 0; i < numAnillos; i++) { anillos[i].crecer(); anillos[i].mostrar(); } // Click para crear un nuevo anillo void mousePressed() { anillos[actualAnillo].iniciar(mouseX, mouseY); actualAnillo++; if (actualAnillo >= numAnillos) { actualAnillo = 0; } // *** continúa ***

365 Ejemplo 2 2/2 class Anillo { float x, y; // Coordenadas-xy float diametro; // Diámetro del anillo boolean on = false; // Enciende o apaga la visualización void iniciar(float xpos, float ypos) { x = xpos; y = ypos; on = true; diametro = 1; } void crecer() { if (on == true) { diametro += 0.5; if (diametro > 400) { on = false; } void mostrar() { if (on == true) { noFill(); strokeWeight(4); stroke(155, 153); ellipse(x, y, diametro, diametro); }

366 Multiples archivos Cuando un programa crece de sobremanera resulta más conveniente dividirlo y separarlo en múltiples archivos. Esta práctica también es aconsejada cuando se desea reusar clases en otros programas. Para realizar esto se recomienda primero guardar el cuerpo principal del programa en un archivo, por ejemplo, anillos-main, y luego hacer clic en el botón del extremo derecho, en el sector de lengüetas. Allí se despliega un menú contextual y se seleccionará la primera opción New Tab. Entonces se nos pide el nombre del nuevo archivo e ingresaremos Anillo-clase. Finalizamos con clic en OK. En la carpeta correspondiente a anillos-main encontraremos dos archivos: anillos-main.pde y Anillo-clase.pde.

367 Lectura recomendada Capítulo “Structure 4: Objects I” (pag. 395).

368 Parte 19 Objetos II

369 Múltiples constructores 1/3 A medida que el programa comienza a crecer y las ideas se hacen más ambiciosas, los conceptos y técnicas avanzados sobre POO se vuelven ineludibles para la gestión del código. Una clase puede tener múltiples constructores que asignan los campos de formas diferentes. A veces resulta beneficioso especificar cada aspecto de los datos de un objeto al asignar parámetros a los campos, pero otras veces puede resultar apropiado definir sólo uno o algunos de ellos. En el siguiente ejemplo, un constructor aplica los valores de los campos x, y, y radio, mientras que el otro aplica valores por defecto. Cuando el objeto es creado, Processing elige el constructor apropiado de acuerdo a la identificación del número y tipo de variables especificado:

370 Múltiples constructores 2/3 Circulo circ1, circ2; void setup() { size(100, 100); smooth(); noLoop(); // Ejecuta el constructor sin parámetros circ1 = new Circulo(); // Ejecuta el constructor con tres parámetros circ2 = new Circulo(66, 50, 20); } void draw() { circ1.mostrar(); circ2.mostrar(); } // *** continúa ***

371 Múltiples constructores 3/3 class Circulo { float x, y, radio; // Primera versión del constructor Circulo; // se asigna a los campos valores por defecto Circulo() { x = 33; y = 50; radio = 8; } // Segunda versión del constructor Circulo; // se asigna a los campos los valores de los parámetros Circulo(float xpos, float ypos, float r) { x = xpos; y = ypos; radio = r; } void mostrar() { ellipse(x, y, radio*2, radio*2); }

372 Objetos compuestos 1/5 Un objeto puede incluir otros objetos. La creación de semejantes objetos compuestos resultan una forma apropiada para aplicar el principio de modularidad y construir niveles más altos de abstracción. En el mundo concreto, los objetos frecuentemente poseen componentes que operan autónomamente pero en relación a otros componentes. Si se permite usar una analogía biológica, puede crear una clase celula, grupos de ellas que combinadas conforman un tejido muscular o un tejido nervioso. Estos tejidos pueden ser combinados en órganos, y los órganos en un organismo. Con múltiples capas de abstracción, cada paso es construido a partir de compuestos de una capa anterior. El siguiente ejemplo combina la clase Huevo y la clase Anillo para crear una nueva clase llamada HuevoAnillo. Cuando se usa la clase HuevoAnillo en un programa, cada instancia dibuja un huevo en la pantalla con un anillo creciendo desde su centro. El ejemplo cuenta con un objeto de tipo Huevo llamado ovoide, creado en el constructor, y un objeto de tipo Anillo llamado circulo, creado en la base de la clase. El método transmitir() llama a los métodos de ambas clases y reinicializa circulo cuando el objeto alcanza su máximo tamaño. Ahora sí, veamos el ejemplo:

373 Objetos compuestos 2/5 HuevoAnillo ha1, ha2; void setup() { size(100, 100); smooth(); ha1 = new HuevoAnillo(33, 66, 16, 33); ha2 = new HuevoAnillo(66, 90, 8, 66); } void draw() { background(0); ha1.transmitir(); ha2.transmitir(); } // Se debe incluir las sig. Clases: Huevo, Anillo y HuevoAnillo

374 Objetos compuestos 3/5 class Huevo { float x, y; // coords-xy float inclinacion; // Offset ángulo izquierda y derecha float balanceo; // Factor de balanceo float angulo; // Ángulo de balanceo float altura; // Altura del huevo Huevo(int xpos, int ypos, float balFactor, float h) { x = xpos; y = ypos; balanceo = balFactor; altura = h / 100.0; } void balancear() { inclinacion = cos(angulo) / balanceo; angulo += 0.1; } void mostrar() { noStroke(); fill(255); pushMatrix(); translate(x, y); rotate(inclinacion); scale(altura); beginShape(); vertex(0, -100); bezierVertex(25, -100, 40, -65, 40, -40); bezierVertex(40, -15, 25, 0, 0, 0); bezierVertex(-25, 0, -40, -15, -40, -40); bezierVertex(-40, -65, -25, -100, 0, -100); endShape(); popMatrix(); }

375 Objetos compuestos 4/5 class Anillo { float x, y; // Coordenadas-xy float diametro; // Diámetro del anillo boolean on = false; // Enciende o apaga la visualización void iniciar(float xpos, float ypos) { x = xpos; y = ypos; on = true; diametro = 1; } void crecer() { if (on == true) { diametro += 0.5; if (diametro > 400) { on = false; } void mostrar() { if (on == true) { noFill(); strokeWeight(4); stroke(155, 153); ellipse(x, y, diametro, diametro); }

376 Objetos compuestos 5/5 class HuevoAnillo { Huevo ovoide; Anillo circulo = new Anillo(); HuevoAnillo(int x, int y, float t, float sp) { ovoide = new Huevo(x, y, t, sp); circulo.iniciar(x, y - sp/2); } void transmitir() { ovoide.balancear(); ovoide.mostrar(); circulo.crecer(); circulo.mostrar(); if (circulo.on == false) { circulo.on = true; }

377 Herencia Una clase puede ser definida usando otra clase como fundamento. En términos de POO, una clase puede heredar campos o métodos de otra. Un objeto que hereda de otro (heredero) es llamado subclase, mientras que el objeto del cual se hereda (antecesor) es llamado superclase. Una subclase extiende las capacidades de una superclase. Cuando una clase extiende a otra, todos los campos y métodos de la superclase resultan automáticamente incluidos en la subclase. Cuando se define la subclase se utiliza la palabra clave extends antecediendo el nombre de la superclase. Se puede agregar nuevos campos y métodos a la subclase para poder trabajar sobre los datos y los comportamientos de la superclase. Si un nombre de método es repetido dentro de la subclase, y además tiene el mismo prototipo (mismo número de parámetros y mismo tipo de datos) que el presente en la superclase, el método de la subclase anula el de la superclase, por lo tanto lo reemplaza. Cuando un campo o método de la superclase es llamado desde la subclase, el nombre es antecedido por la palabra clave super para hacer saber a Processing que dicho campo o método es parte de la superclase. El siguiente ejemplo refleja estos nuevos términos y conceptos:

378 Herencia Ejemplo (Giro: superclase) class Giro { float x, y, velocidad; float angulo = 0.0; Giro(float xpos, float ypos, float vel) { x = xpos; y = ypos; velocidad = vel; } void actualizar() { angulo += velocidad; }

379 Herencia Ejemplo (GiroLinea: subclase) class GiroLinea extends Giro { GiroLinea(float x, float y, float v) { super(x, y, v); } void mostrar() { strokeWeight(1); stroke(0); pushMatrix(); translate(x, y); angulo += velocidad; rotate(angulo); line(0, 0, 100, 0); popMatrix(); }

380 Herencia Ejemplo (GiroCirculos: subclase) class GiroCirculos extends Giro { float dimen; GiroCirculos(float x, float y, float v, float d) { super(x, y, v); dimen = d; } void mostrar() { noStroke(); pushMatrix(); translate(x, y); angulo += velocidad; rotate(angulo); ellipse(-dimen/2, 0, dimen, dimen); ellipse(dimen/2, 0, dimen, dimen); popMatrix(); }

381 Herencia Ejemplo (animacion: main) GiroCirculos circulos; GiroLinea linea; void setup() { size(100, 100); smooth(); linea = new GiroLinea(width/2, height/2, 0.01); circulos = new GiroCirculos(width/2, height/2, -0.02, 33.0); } void draw() { background(204); linea.actualizar(); linea.mostrar(); circulos.actualizar(); circulos.mostrar(); }

382 Lectura recomendada Capítulo “Structure 5: Objects II” (pag. 453).

383 Parte 20 ArrayList

384 ArrayList La clase Java ArrayList permite implementar arrays de tamaño flexible, donde es posible agregar o remover elementos del principio, mitad o final del array. El uso de un ArrayList conceptualmente es similar al de un array estandar, pero lo que difiere es la sintáxis. El siguiente ejemplo muestra el uso de ArrayList para crear una aplicación que genera un emisor de partículas. Cada vez que presionamos el botón del mouse, la aplicación genera una partícula en cada fotograma mientras dure la presión del mouse.

385 ArrayList Ejemplo (ArrayList: main) // Learning Processing // Daniel Shiffman // http://www.learningprocessing.com // Ejemplo 23-2: Sistema de partículas simple con ArrayList ArrayList particulas; void setup() { size(200,200); particulas = new ArrayList(); smooth(); } void draw() { // Cuando el botón del mouse es presionado, se agrega un nuevo // objeto Particula al ArrayList en cada ciclo del draw(). if (mousePressed) { particulas.add(new Particula()); } background(255); // Iteración a través del ArrayList y obtiene cada partícula. // ArrayList mantiene un seguimiento del nñúmero total de partículas. for (int i = 0; i < particulas.size(); i++ ) { Particula p = (Particula) particulas.get(i); p.ejecutar(); p.gravedad(); p.mostrar(); } // Si ArrayList contiene más de 100 elementos, eliminamos // el primer elemento usando el método remove(). if (particulas.size() > 100) { particulas.remove(0); }

386 ArrayList Ejemplo (Particula: clase) // Learning Processing // Daniel Shiffman // http://www.learningprocessing.com // Ejemplo 23-2: Sistema de partículas simple con ArrayList // Una clase de Partícula simple class Particula { float x; float y; float xVelocidad; float yVelocidad; Particula() { x = mouseX; y = mouseY; xVelocidad = random(-1,1); yVelocidad = random(-2,0); } void ejecutar() { x = x + xVelocidad; y = y + yVelocidad; } void gravedad() { yVelocidad += 0.1; } void mostrar() { stroke(0); fill(0,75); ellipse(x,y,10,10); }

387 Lectura recomendada Capítulo 23 “Java” (pag. 423). Shiffman, D. “Learning Processing - A Beginner’s Guide to Programming Images, Animation, and Interaction”, Morgan Kaufman, 2008.

388 Ej 17 EJ17: Crear una superficie sensible a datos de entrada (mouse y/o teclado) cuya programación haga uso de las técnicas, procedimientos y estructuras aprendidas durante el curso. Comentar todas las instrucciones.

389 Trabajo Práctico Final TPFinal: Crear un sistema generativo de imagen (processing) y sonido (pure data), en el cual se establezcan relaciones de influencia audiovisual en una o en dos vías a través de datos enviados entre sí mediante el formato de contenido Open Sound Control. Comentar todas las instrucciones.

390 FIN


Descargar ppt "Introducción a Processing v1.2.1 Raúl Lacabanne - 2011 Versión 41 Resumen de los tópicos básicos del libro: “Processing: A Programming Handbook for Visual."

Presentaciones similares


Anuncios Google