After another highly successful skating CHAT session (YES! you can/should/better join us) Laura and I had a meeting with particle physicist turned coder Dave W about the development of a poetry visualization tool. Laura presented her exciting vision for the tool, and Dave and I discussed how we could help Laura build it.

Dave will handle the back-end database component, and I'll try to tackle the front-end graphical stuff. The visualization will be a dynamically generated 3D plot of user selected data fields. For example, a user may select a list of poems based on a certain time period, meter structure, theme, etc. The tool will plot the results as a series of relational nodes in 3D space, with the different axes and node types representing the relevant metrics. In addition, users will be able to specify style characteristics for nodes as well as save images of the visualizations. We'll soon be shaking the grant trees for funding (ideas/cash welcome) and developing a prototype.

Inspired by Laura's skating and generative prowess, I created a little code piece in the spirit of her vis tool (ok, it will also be an example in my book.) As usual, paste the code below in Processing and run the dang thing. If the animation runs too slowly, try lowering the number of cubies (int cubies = 150;).

// Paste the code below into Processing

Cube stage; // external large cube

int cubies = 150;

Cube[]c = new Cube[cubies]; // internal little cubes

color[][]quadBG = new color[cubies][6];

// controls cubie's movement

float[]x = new float[cubies];

float[]y = new float[cubies];

float[]z = new float[cubies];

float[]xSpeed = new float[cubies];

float[]ySpeed = new float[cubies];

float[]zSpeed = new float[cubies];

// controls cubie's rotation

float[]xRot = new float[cubies];

float[]yRot = new float[cubies];

float[]zRot = new float[cubies];

// size of external cube

float bounds = 300;

void setup(){

size(400, 400, P3D);

framerate(30);

for (int i=0; i<cubies; i++){

// each cube face has a random color component

float colorShift = random(-75, 75);

quadBG[i][0] = color(175+colorShift, 30, 30);

quadBG[i][1] = color(30, 175+colorShift, 30);

quadBG[i][2] = color(30, 30, 175+colorShift);

quadBG[i][3] = color(175+colorShift, 175+colorShift, 30);

quadBG[i][4] = color(175+colorShift, 30, 175+colorShift);

quadBG[i][5] = color(175+colorShift, 87+colorShift, 30);

// cubies are randomly sized

float cubieSize = random(5, 10);

c[i] = new Cube(cubieSize, cubieSize, cubieSize);

//initialize cubie's position, speed and rotation

x[i] = 0;

y[i] = 0;

z[i] = 0;

xSpeed[i] = random(-2, 2);

ySpeed[i] = random(-2, 2);

zSpeed[i] = random(-2, 2);

xRot[i] = random(40, 100);

yRot[i] = random(40, 100);

zRot[i] = random(40, 100);

}

// instantiate external large cube

stage = new Cube(300, 300, 300);

}

void draw(){

background(50);

// center in display window

translate(width/2, height/2, -130);

// outer transparent cube

noFill();

// rotate everything, including external large cube

rotateX(frameCount*PI/225);

rotateY(frameCount*PI/250);

rotateZ(frameCount*PI/275);

stroke(255);

// draw external large cube

stage.create();

//move/rotate cubies

for (int i=0; i<cubies; i++){

pushMatrix();

translate(x[i], y[i], z[i]);

rotateX(frameCount*PI/xRot[i]);

rotateY(frameCount*PI/yRot[i]);

rotateX(frameCount*PI/zRot[i]);

noStroke();

c[i].create(quadBG[i]);

x[i]+=xSpeed[i];

y[i]+=ySpeed[i];

z[i]+=zSpeed[i];

popMatrix();

// draw lines connecting cubies

stroke(35);

if (i< cubies-1){

line(x[i], y[i], z[i], x[i+1], y[i+1], z[i+1]);

}

// check wall collisions

if (x[i]>bounds/2 || x[i]<-bounds/2){

xSpeed[i]*=-1;

}

if (y[i]>bounds/2 || y[i]<-bounds/2){

ySpeed[i]*=-1;

}

if (z[i]>bounds/2 || z[i]<-bounds/2){

zSpeed[i]*=-1;

}

}

}

/*

Extremely simple class to

hold each 3D vertex

*/

class Point3D{

float x, y, z;

// constructors

Point3D(){

}

Point3D(float x, float y, float z){

this.x = x;

this.y = y;

this.z = z;

}

}

/* custom Cube class

slightly cooler than Processing's

box() function */

class Cube{

Point3D[] vertices = new Point3D[24];

float w, h, d;

//constructor

Cube(float w, float h, float d){

this.w = w;

this.h = h;

this.d = d;

// cube composed of 6 quads

//front

vertices[0] = new Point3D(-w/2,-h/2,d/2);

vertices[1] = new Point3D(w/2,-h/2,d/2);

vertices[2] = new Point3D(w/2,h/2,d/2);

vertices[3] = new Point3D(-w/2,h/2,d/2);

//left

vertices[4] = new Point3D(-w/2,-h/2,d/2);

vertices[5] = new Point3D(-w/2,-h/2,-d/2);

vertices[6] = new Point3D(-w/2,h/2,-d/2);

vertices[7] = new Point3D(-w/2,h/2,d/2);

//right

vertices[8] = new Point3D(w/2,-h/2,d/2);

vertices[9] = new Point3D(w/2,-h/2,-d/2);

vertices[10] = new Point3D(w/2,h/2,-d/2);

vertices[11] = new Point3D(w/2,h/2,d/2);

//back

vertices[12] = new Point3D(-w/2,-h/2,-d/2);

vertices[13] = new Point3D(w/2,-h/2,-d/2);

vertices[14] = new Point3D(w/2,h/2,-d/2);

vertices[15] = new Point3D(-w/2,h/2,-d/2);

//top

vertices[16] = new Point3D(-w/2,-h/2,d/2);

vertices[17] = new Point3D(-w/2,-h/2,-d/2);

vertices[18] = new Point3D(w/2,-h/2,-d/2);

vertices[19] = new Point3D(w/2,-h/2,d/2);

//bottom

vertices[20] = new Point3D(-w/2,h/2,d/2);

vertices[21] = new Point3D(-w/2,h/2,-d/2);

vertices[22] = new Point3D(w/2,h/2,-d/2);

vertices[23] = new Point3D(w/2,h/2,d/2);

}

void create(){

// draw cube

for (int i=0; i<6; i++){

beginShape(QUADS);

for (int j=0; j<4; j++){

vertex(vertices[j+4*i].x, vertices[j+4*i].y, vertices[j+4*i].z);

}

endShape();

}

}

void create(color[]quadBG){

// draw cube

for (int i=0; i<6; i++){

fill(quadBG[i]);

beginShape(QUADS);

for (int j=0; j<4; j++){

vertex(vertices[j+4*i].x, vertices[j+4*i].y, vertices[j+4*i].z);

}

endShape();

}

}

}