// w and s move in and out // mouse drag rotates float angleX, angleY; static final int kSize = 400; static final int kNumPainters = 25; static final int kMaxLength = 250; int resetMode = 2; // 0 for re-use, 1 for new download, 2 for no more drawing static final int kMaxStringLength = 1024; static final float kInvMaxLength = 1. / (float)kMaxLength; static final float kMaxMass = 50.; static final float kMaxStrength = 10e5; boolean transparency; boolean online; float zPos = 0; Painter[] painters; void setup() { size(kSize, kSize); fill(0); stroke(0); online = !online(); angleX = angleY = 0; BFont courier = loadFont("Courier_New.vlw"); textFont(courier); textSize(24); textMode(ALIGN_CENTER); transparency = false; strokeWeight(20); painters = new Painter[kNumPainters]; } void mouseDragged() { angleX -= mouseY - pmouseY; angleY += mouseX - pmouseX; } void keyPressed() { switch (key) { case 'r': background(255); painters = new Painter[kNumPainters]; for(int i = 0; i < kNumPainters; ++i) painters[i] = new Painter(random(kSize), random(kSize), random(kSize), random(1, kMaxMass), random(0, kMaxStrength), (random(1.) > .5)); break; case 'w': ++zPos; break; case 's': --zPos; break; case ' ': for(int i = 0; i < kNumPainters; ++i) painters[i].positions = new LinkedList(); break; case 't': transparency = !transparency; break; case '0': resetMode = 0; break; case '1': resetMode = 1; break; case '2': resetMode = 2; break; } } // we load our data up in loop because if we do it in setup, the applet FREAKS OUT! static float numLoaded = 0; static boolean loadingComplete = false; void loop() { fill(0); background(255); if (!loadingComplete) { text(("Loading " + (100.*(numLoaded+1.) / (float)kNumPainters) + "% complete"), width/2, 60); line(0, height/2, width * ((numLoaded+1.) / (float)kNumPainters), height/2); painters[(int)numLoaded] = new Painter(random(kSize), random(kSize), random(kSize), random(1, kMaxMass), random(0, kMaxStrength), (random(1.) > .5)); ++numLoaded; if (numLoaded == kNumPainters) { textSize(12); loadingComplete = true; } return; } push(); translate(kSize/2, kSize/2, kSize/2); translate(0, 0, zPos); rotateX(radians(angleX)); rotateY(radians(angleY)); for(int i = 0; i < kNumPainters; ++i) { painters[i].execute(); painters[i].render(); } pop(); } class Painter { Point accel; Point vel; Point pos; float mass; float invMass; float strength; boolean polarity; boolean animating; LinkedList positions; LinkedList letters; void reset(boolean inConstructor) { if (inConstructor) { letters = new LinkedList(); positions = new LinkedList(); if (online) makeUpData();//getTextData(findAtomURL()); else makeUpData(); return; } switch (resetMode) { case 0: letters.addAll(letters); break; case 1: letters = new LinkedList(); positions = new LinkedList(); if (online) getTextData(findAtomURL()); else makeUpData(); break; case 2: animating = false; break; } } Painter(float x, float y, float z, float m, float s, boolean p) { pos = new Point(x, y, z); vel = new Point(0, 0, 0); accel = new Point(0, 0, 0); mass = m; invMass = 1. / m; polarity = p; strength = s; animating = true; reset(true); if (letters.size() == 0) letters.add(new Character('*')); } Point forceFrom(Point pt, boolean p) { float dx = pt.get(0) - pos.get(0); float dy = pt.get(1) - pos.get(1); float dz = pt.get(2) - pos.get(2); float distanceSq = dx*dx + dy*dy + dz*dz; float distance = sqrt(distanceSq); float invDistance = 1. / distance; float force = strength / (dx*dx + dy*dy + dz*dz); if (p == polarity) force *= -1.; float ftid = force * invDistance; Point forceVector = new Point(dx*ftid, dy*ftid, dz*ftid); return forceVector; } void execute() { if (animating) { for(int i = 0; i < kNumPainters; ++i) { if (painters[i] == this) continue; Point force = painters[i].forceFrom(pos, polarity); for(int j = 0; j < 3; ++j) accel.add(j, force.get(j) * invMass); } // keep our stuff in the picture maybe? for(int i = 0; i < 3; ++i) { if ((pos.get(i) < 0) || (pos.get(i) > kSize)) { vel.set(i, -1.*vel.get(i)); accel.set(i, -.5*accel.get(i)); //accel.add(i, (0 - pos.get(i))*50.); } // this is where I should be doing Runge-Kutta vel.add(i, accel.get(i) * .01); vel.set(i, constrain(vel.get(i), -500., 500.)); pos.add(i, vel.get(i) * .01); //pos.add(i, random(-15, 15)); } positions.add(new Point(pos)); } if (positions.size() > letters.size()) { reset(false); } if (positions.size() > kMaxLength) { positions.removeFirst(); letters.removeFirst(); } } void render() { ListIterator pointIter = positions.listIterator(0); ListIterator letterIter = letters.listIterator(0); Point p = null, pLast = null, pNext = null, normVel; float velocity; float i = 1; if (pointIter.hasNext()) p = pLast = pNext = (Point)pointIter.next(); while (pointIter.hasNext()) { pLast = p; p = pNext; pNext = (Point)pointIter.next(); Point tempLast = new Point(p); tempLast.subtract(pLast); Point tempNext = new Point(pNext); tempNext.subtract(p); push(); translate(p.get(0) - kSize/2, p.get(1)-kSize/2, p.get(2)-kSize/2); rotateY(TWO_PI - radians(angleY)); rotateX(TWO_PI - radians(angleX)); float dx = tempNext.scrX() - tempLast.scrX(); float dy = tempNext.scrY() - tempLast.scrY(); //rotate(-atan2(dy, dx)); char letter = ((Character)letterIter.next()).charValue(); if (transparency) fill(0, 0, 0, 255. * (i / (float)(positions.size()))); text(letter, 0, 0); pop(); ++i; } } void makeUpData() { for(int i = 0; i < kMaxStringLength; ++i) letters.add(new Character((char)(random((int)'a', (int)'z')))); } String findAtomURL() { String atomURL = ""; while (atomURL.equals("")) { try { URL myURL = new URL("http://www.livejournal.com/random.bml"); FilterInputStream myContent = (FilterInputStream)myURL.getContent(); int avail; avail = min(myContent.available(), 1024); int lettersIn = 0; char[] atom = {'"', 'A', 't', 'o', 'm', '"', ' ', 'h', 'r', 'e', 'f', '=', '"'}; while (avail != 0) { --avail; char letter = (char)myContent.read(); if ((lettersIn == atom.length) && (letter != '"')) atomURL += letter; else if ((lettersIn == atom.length) && (letter == '"')) break; else if ((lettersIn < atom.length) && (letter == atom[lettersIn])) ++lettersIn; else lettersIn = 0; } } catch (MalformedURLException e) { } catch (IOException e) { } } return atomURL; } void getTextData(String atomURL) { String textStr = ""; boolean inTag = false; try { URL myURL = new URL(atomURL); FilterInputStream myContent = (FilterInputStream)myURL.getContent(); int avail = myContent.available(); int assigned = 0; for(; (avail >= 0) && (assigned < kMaxStringLength); --avail) { char letter = (char)myContent.read(); if (inTag && (letter == '>')) { inTag = false; continue; } else if (!inTag && (letter == '<')) { inTag = true; continue; } else if (inTag) continue; else if (Character.isLetter(letter) || Character.isWhitespace(letter)) { letters.add(new Character(letter)); ++assigned; } } println(); } catch (MalformedURLException e) { } catch (IOException e) { } } } class Point { float[] me = {0, 0, 0}; Point(float a, float b, float c) { me[0] = a; me[1] = b; me[2] = c; } Point(Point p) { me[0] = p.get(0); me[1] = p.get(1); me[2] = p.get(2); } float get(int i) { return me[i]; } void set(int i, float val) { me[i] = val; } void add(int i, float val) { me[i] += val; } void add(float val) { for(int i = 0; i < 3; ++i) me[i] += val; } void subtract(Point p) { for(int i = 0; i < 3; ++i) me[i] -= p.get(i); } float scrX() { return screenX(me[0], me[1], me[2]); } float scrY() { return screenY(me[0], me[1], me[2]); } void normalize() { float value = sqrt(sq(me[0]) + sq(me[1]) + sq(me[2])); for(int i = 0; i < 3; ++i) me[i] /= value; } }