/** * four dimensional goodness - ben fry * originally completed sometime in december of 2000, * updated for processing in february 2005 * * based largely on what i learned/ported from HYPRCUBE.BAS * found in the following article by Ken Holmes (kholmes@melbpc.org.au) * http://www.melbpc.org.au/pcupdate/9907/9907article10.htm * * this was my initial code to figure out how these things work, * which i later ported up to an arbtitrary number of dimensions * for the "atmosphere" project. see: * http://acg.media.mit.edu/projects/atmosphere * http://acg.media.mit.edu/people/fry/atmosphere * * space to enable/disable auto-movement * t to show/hide the text * * x y z w to wiggle in each of the four dimensions * 1 2 3 to manually rotate with respect to each plane * * f s to move faster or slower * * vector font code ported from code by jared schiffman and i from * the acg's internal graphics library "acu". vector font by josh nimoy. * * the integrator is my simple spring-based integrator class. */ boolean showText = true; int cornCount = 0; float seedCorn[][] = { {-1,-1,-1,-1}, {-1,-1,-1, 1}, {-1,-1, 1,-1}, {-1,-1, 1, 1}, {-1, 1,-1,-1}, {-1, 1,-1, 1}, {-1, 1, 1,-1}, {-1, 1, 1, 1}, { 1,-1,-1,-1}, { 1,-1,-1, 1}, { 1,-1, 1,-1}, { 1,-1, 1, 1}, { 1, 1,-1,-1}, { 1, 1,-1, 1}, { 1, 1, 1,-1}, { 1, 1, 1, 1} }; float corn[][] = new float[66][4]; float tcorn[][] = new float[66][4]; float fcornx[] = new float[66]; float fcorny[] = new float[66]; int trail[] = { 0, 1, 3, 2, 6, 14, 10, 8, 9, 11, 3, 7, 15, 14, 12, 13, 9, 1, 5, 7, 6, 4, 12, 8, 0, 4, 5, 13, 15, 11, 10, 2, 0 }; Integrator wiggle[]; float sin, cos; float angle; int a = 0, b = 0; boolean inMotion = true; acVectorFont font; void setup() { size(640, 480, P3D); framerate(30); wiggle = new Integrator[4]; for (int i = 0; i < 4; i++) { wiggle[i] = new Integrator(1); wiggle[i].setDamping(0.8f); } setAngle(0.08); for (int i = 1; i < trail.length; i++) { addPoint(seedCorn[trail[i-1]], seedCorn[trail[i]]); } //try { font = new acVectorFont(openStream("vector.jvf")); //} catch (IOException e) { //e.printStackTrace(); //} addString("potato", 0, 3, 0.0f, 1.0f); addString("tomato", 2, 1, -0.6f, -1.8f); addString("avocado", 0, 2, 0.0f, 1.4f); addString("tamale", 3, 1, 1.0f, -1.4f); addString("orangutang", 1, 2, -1.7f, 0.8f); addString("eggplant", 2, 3, 1.2f, 0.0f); } protected void addPoint(float one[], float two[]) { corn[cornCount] = new float[4]; System.arraycopy(one, 0, corn[cornCount], 0, 4); cornCount++; corn[cornCount] = new float[4]; System.arraycopy(two, 0, corn[cornCount], 0, 4); cornCount++; } protected void addString(String what, int planeX, int planeY, float offsetX, float offsetY) { font.buildString(what, offsetX, offsetY); int newCornCount = cornCount + font.bcount*2; float temp[][]; temp = new float[newCornCount][4]; System.arraycopy(corn, 0, temp, 0, cornCount); corn = temp; //corntext = new boolean[newCornCount]; tcorn = new float[newCornCount][4]; fcornx = new float[newCornCount]; fcorny = new float[newCornCount]; for (int i = 0; i < font.bcount; i++) { // add the points for one axis/plane //corntext[cornCount] = true; corn[cornCount] = new float[4]; corn[cornCount][planeX] = font.bx1[i]; corn[cornCount][planeY] = font.by1[i]; cornCount++; // add the points for the other axis/plane //corntext[cornCount] = true; corn[cornCount] = new float[4]; corn[cornCount][planeX] = font.bx2[i]; corn[cornCount][planeY] = font.by2[i]; cornCount++; } } // rotates in 1 and 3 // cos(theta) 0 -sin(theta) // 0 1 0 // sin(theta) 0 cos(theta) protected void multiply(float mat1[][], float mat2[][], float out[][]) { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { out[i][j] = 0; } } for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { for (int k = 0; k < 4; k++) { out[i][j] += mat1[i][k] * mat2[k][j]; } } } } protected void transform4(float m[][], float in[], float out[]) { out[0] = m[0][0]*in[0] + m[0][1]*in[1] + m[0][2]*in[2] + m[0][3]*in[3]; out[1] = m[1][0]*in[0] + m[1][1]*in[1] + m[1][2]*in[2] + m[1][3]*in[3]; out[2] = m[2][0]*in[0] + m[2][1]*in[1] + m[2][2]*in[2] + m[2][3]*in[3]; out[3] = m[3][0]*in[0] + m[3][1]*in[1] + m[3][2]*in[2] + m[3][3]*in[3]; } float rotation[][] = new float[4][4]; protected void rotate(int a, int b) { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { rotation[i][j] = 0; } } for (int i = 0; i < 4; i++) { rotation[i][i] = 1; } //setAngle(noise(frameCount) * 0.08); rotation[a][a] = cos; rotation[b][a] = -sin; rotation[a][b] = sin; rotation[b][b] = cos; multiply(ctm, rotation, newctm); oldctm = ctm; ctm = newctm; newctm = oldctm; } static float ctm[][] = new float[4][4]; static float oldctm[][] = new float[4][4]; static float newctm[][] = new float[4][4]; static { for (int i = 0; i < 4; i++) { ctm[i][i] = 1; } } float wiggled[] = new float[4]; protected void calcTransform() { for (int i = 0; i < cornCount; i++) { for (int j = 0; j < 4; j++) { wiggled[j] = corn[i][j] * wiggle[j].value; transform4(ctm, wiggled, tcorn[i]); float f = 2f / (3+tcorn[i][0]); float x = f * tcorn[i][1]; float y = f * tcorn[i][2]; float z = f * tcorn[i][3]; float fz = z / (z + 10); fcornx[i] = x - x*fz; fcorny[i] = y - y*fz; } } } static final float WIG = 1f; void setAngle(float newAngle) { angle = newAngle; sin = (float) Math.sin(angle); cos = (float) Math.cos(angle); } void keyPressed() { switch (key) { case ' ': inMotion = !inMotion; break; case 't': showText = !showText; break; case 'x': wiggle[1].value += WIG; break; case 'y': wiggle[2].value += WIG; break; case 'z': wiggle[3].value += WIG; break; case 'w': wiggle[0].value += WIG; break; case '1': rotate(1, 0); break; // '1' x and u axes case '2': rotate(2, 0); break; // '2' y and u axes case '3': rotate(3, 0); break; // '3' z and u axes case 'f': setAngle(angle + .003); break; // "f" makes faster case 'F': setAngle(angle - .003); break; // "F" makes slower } } public int TX(float x) { return (int) (width * ((x + 3.2f) / 6.4f)); } public int TY(float y) { return (int) (height * ((y + 2.4f) / 4.8f)); } void LINE(int x1, int y1, int x2, int y2) { if ((x1 > width) || (x2 > width) || (x1 < 0) || (x2 < 0) || (y1 > height) || (y2 > height) || (y1 < 0) || (y2 < 0)) return; line(x1, y1, x2, y2); } void draw() { //println(framerate()); if (inMotion) { if ((a == b) || (Math.random() < 0.1)) { a = (int) (Math.random() * 4.0); b = 0; do { b = (int) (Math.random() * 4.0); } while (a == b); } rotate(a, b); } for (int i = 0; i < 4; i++) { wiggle[i].attraction(1, 0.1f); wiggle[i].update(); } background(0); calcTransform(); for (int i = 0; i < (showText ? cornCount : 64); i += 2) { if (showText) { stroke(i < 64 ? 128 : 255); } else { stroke(255); } LINE(TX(fcornx[i]), TY(fcorny[i]), TX(fcornx[i+1]), TY(fcorny[i+1])); } }