La siguiente aplicación fue expuesta en el Museo Universitario de Arte Moderno (MUAC) con el propósito del cierre de actividades referentes a la exposición Pseudomatismos del artista e ingeniero químico Rafael Lozano-Hemmer. Dicha aplicación junto con otras desarrolladas por equipo multidisciplinarios formaron parte de una muestra llamada “Nuevas Herramientas para el Proceso Creativo”. La aplicación desarrollada en el lenguaje Processing produce superficies generativas, es decir a partir de un molde o modelo base dado por fórmulas de superficies (primeramente se desarrolló con fórmulas correspondientes a superficies conocidas, por ejemplo: toro, elipsoide y esfera, posteriormente se implementaron fórmulas que dieron como resultado figuras complejas), posteriormente los valores correspondientes a la trayectoria de dibujo de los trazos de la imagen son alterados por medio del proceso generativo. Finalmente se podrán apreciar imágenes que presentan una textura en común característica, a pesar de las diferentes superficies que representan. Dicha textura pretende alejarse de la perfección del dibujo trazado de manera artificial, mas bien imita la forma en la que una persona lo haría con papel y un lápiz, con errores y bordes irregulares, pero sin llegar al punto de desplegar figuras amorfas y carentes de sentido producto de un errado proceso aleatorio en cuyo único fin descasaría lo “impredecible”.
//Variables Globales//
/*_num hace referencia al número de objetos independientes con los que será
dibujada la superficie (300). La forma en como se definieron las variables
ha sido retomada de diferentes autores, pero principalmente de Matt Pearson
y su libro Generative Art del cual fue retomado el algorimo para dibujar un
circulo y la declaración y usode objetos mediante arrglos.*/
int _num = 300;
int e,r,t;
Circle[] pArr = new Circle[_num];
void setup() {
//Se indica el tamaño de la ventana con la propiedad de render en 3D
size(1200, 700, P3D);
smooth();
//Ajuste de velocidad de refresco de pantalla a 60 cuadros por segundo
frameRate(60);
background(230);
//Posicionamiento del inicio de trazo así como configuración de la perspectiva
//de la cámara (no esta situda completamente al centro de la pantalla).
float startx = (width/4)*3;
float starty = (height/4)*3;
float startz =0;
camera(60, 180, 80, (width/2), (height/2), 0, 1.0, 1.5, 1.0);
//Son inicializados los objetos mediante un arreglo y colocados en una misma posición
for (int i=0;i<_num;i++) {
pArr[i] = new Circle(i);
pArr[i].init(startx, starty, startz);
}
}
void draw() {
// Cada ciclo son modificados los valores de posición de cada objeto
for (int i=0;i<_num;i++) {
pArr[i].update();
}
}
//*********************************************INTERACCIÓN
void keyReleased() {
//Si la tecla «S» o «s» es oprimida, se guardará una captura de la imagen generada
if (key == ‘s’ || key == ‘S’) saveFrame(«_##.png»);
}
void mousePressed() {
// Si el mouse es presionado, el proceso se reinicia
setup();
}
//*********************************************OBJETOS
class Circle {
int id; //ID de objeto
float angnoise, radiusnoise; //Tanto el radio como el ángulo son afectados por el efecto
//generativo, deformando su comportamiento
float angle = 0; //ángulo de inicio de figura 1
float angle2 = 0; //ángulo de inicio de figura 2
float radius; //rádio de la figura (para ambas)
float centreX; //Centro en el espacio X,Y,Z
float centreY;
float centreZ;
float strokeCol; //Color de trazo
float angleincr; //Incremento del ángulo
float lastX = 9999; //Para evitar trazos «ruido» en el primer ciclo
float lastX2 = 9999; //se declaran valores altos fuera de alcance
float lastY,lastY2,lastZ, lastZ2; //Son declaradas varables de respaldo de la
float lastOuterX, lastOuterY, lastOuterZ; //última posición y generar trazos (líneas)
Circle (int num) {
//A cada objeto le es asignado un ID
id = num;
}
void init(float ex, float why, float ze) {
//El inicio del trazo será denominado el centro del trazo
centreX = ex;
centreY = why;
centreZ = ze;
//Se tendrá una valor de radio inicial independiente asignado de manera aleatoria
radiusnoise = random(1);
//El color de cada trazo es determindado en la escala de grises de forma aleatoria
strokeCol = random(255);
//Ángulo de inicio es asignado de forma aleatoria
angle = random(180);
//El incremento es mínimo de un grado por ciclo mas una afracción aleatoria
angleincr = random(1) + 1;
}
void update() {
//Cada Ciclo el ruido del radio es incrementado
radiusnoise += 0.02;
/*El radio es afecta por el siguiente proceso generativo:
El valor que contantemente esincrementado, se evalua con la funcion noise,
la cual arroja valores entre 0 y 1, multiplicado por un valor proporcional
a un cuarto del ancho de la ventana, multiplicado por un factor de proporción
(5), el cual se obtuvo para obtener un tamaño deseable en la figura. Finalmente
es añadido nuevamente el valor del ruido de radio evaluado en la función ruido
para obtener pequeños bordes irregulares a la forma.
*/
radius = (5*(noise(radiusnoise) * (width/4)))+noise(radiusnoise);
//El ángulo se incrementa
angle += angleincr;
//Son evaluados en valores entre -PI y PI, ciertos casos los resultados son
//significativamente diferentes al modificar dicho intervalo.
float rad2 = (radians(angle)-(PI));
float rad = rad2;
/*
//Estas fórmulas corresponden a figuras en 2D, que se implementaron en la primera
//etapa del desarrollo.
float x2 = centreX + ((radius * cos(rad))/(1+pow(sin(rad),2)));
float y2 = centreY + ((radius * sin(rad)*cos(rad))/(1+pow(sin(rad),2)));// infinito
float y1 = centreX -280 – ((radius/1.5 * cos(rad))*(1-cos(rad)))*1.2;
float x1 = centreY + ((radius/1.7 * sin(rad)*(1-cos(rad))));// cardioide
*/
/*
//El código comentado corresponde a figuras que han sido exploradas y se han mantenido
//como registro debido a su importancia (3D).
float x1 = centreX + cos(2*rad)*cos(rad2) + (radius/2)*cos(2*rad)*(1.5+sin(3*rad)/2);
float y1 = centreY +sin(2*rad)*cos(rad2) + (radius/2) * sin(2*rad)*(1.5+sin(3*rad)/2);
float z1 = sin(rad2)+200*cos(3*rad);
*/
/*
float x1 = centreX + radius * sin(3*rad) / (2+cos(rad2));
float y1 = centreY + radius * (sin(rad) + 2*sin(2*rad)) /(2 + cos(rad2+3.1416*2/3));
float z1 = radius/2 * (cos(rad) – 2*cos(2*rad))*(2+cos(rad2)) * (2 + cos(rad2+3.1416*2/3)) / 4;
float x2 = centreX + (radius * sin(rad) * cos(rad) * cos(rad2));
float y2 = centreY + (radius * sin(rad) * sin(rad2) *cos(rad2));
float z2 = radius * (cos(rad)* cos(2*rad));
*/
float x1 = centreX + radius * sin(3*rad) / (2+cos(rad2));
float y1 = centreY + radius * (sin(rad) + 2*sin(2*rad)) /(2 + cos(rad2+3.1416*2/3));
float z1 = radius/2 * (cos(rad) – 2*cos(2*rad))*(2+cos(rad2)) * (2 + cos(rad2+3.1416*2/3)) / 4;
float x2 = centreX + (radius * sin(rad) * cos(rad2)*cos(radius*0.01)*0.6);
float y2 = centreY + (radius * sin(rad) * sin(rad2)*cos(radius*0.01)*0.6);
float z2 = radius * cos(rad)*sin(radius*0.001)*0.6;
if (lastX != 9999) {
//Se asigna grosor y color al trazo (los valores de color se multiplicaron por «basura»
//intencionalmete).
strokeWeight(1);
stroke(strokeCol*r, strokeCol*e,strokeCol*t,10);
//Es posible eliminar el comentario en la siguiente línea para desplegar 2 imágenes
//simultáneamente
/*line(x1, y1,z1, lastX, lastY,lastZ);*/
//trazo recto entre la posición actual y anterior
line(x2, y2,z2, lastX2, lastY2,lastZ2);
}
//Las siguiente líneas ayudan a almacenar la posición anterior y poder realizar trazos
lastX2 = x2;
lastY2 = y2;
lastZ2= z2;
lastX = x1;
lastY = y1;
lastZ = z1;
}
}